/// <summary>
        /// Returns how many plan periods are due.
        /// </summary>
        /// <param name="plan">The payment plan to calculate.</param>
        /// <param name="currentDate">The current date, ensuring all calculations are done using the exact same date.</param>
        /// <returns>The amount of periods to pay.</returns>
        private decimal CalcPeriodQty(ApiPaymentPlan plan, DateTimeOffset currentDate)
        {
            var loopDate = new[] { plan.StartDate, plan.PaidUntilDate, plan.DateAdded }.Max() ?? MovePeriodDate(plan, plan.NextChargeDate !.Value, -1);
            var qtyTotal = 0m;
            var qtyTrial = 0m;
            var count    = 0;

            while (GetPlanPeriod(plan, loopDate, currentDate, out var periodStart, out var periodEnd) &&
                   (plan.TransactionsLeft == null || count < plan.TransactionsLeft))
            {
                count++;
                var fullPeriod = periodEnd - periodStart;
                var billPeriod = periodEnd - loopDate;
                if (plan.EndDate.HasValue)
                {
                    billPeriod -= (periodEnd - plan.EndDate.Value);
                }
                // Calculate period by period.
                var qty = (decimal)billPeriod.Ticks / fullPeriod.Ticks;

                // Trial period will only be billed once we've completed a billing cycle afterwards.
                if (plan.PeriodUnit == PaymentPeriodUnit.MonthPostpaidTrial && billPeriod.TotalDays <= (plan.PeriodQty ?? _config.Value.DefaultTrialDays))
                {
                    qtyTrial += qty;
                }
                else
                {
                    qtyTotal += qty + qtyTrial;
                    qtyTrial  = 0;
                }

                loopDate = periodEnd;
            }
            return(qtyTotal);
        }
 /// <summary>
 /// Moves the date by specified number of full billing periods.
 /// </summary>
 /// <param name="plan">The plan containing billing period info.</param>
 /// <param name="datePlan">The date to move.</param>
 /// <param name="qty">The amount of full periods to add or remove to the date.</param>
 /// <returns>The date for a different billing period.</returns>
 private static DateTimeOffset MovePeriodDate(ApiPaymentPlan plan, DateTimeOffset datePlan, int qty)
 {
     return(plan.PeriodUnit switch
     {
         PaymentPeriodUnit.MonthPostpaid => datePlan.AddMonths(1 * qty),
         PaymentPeriodUnit.MonthPostpaidTrial => datePlan.AddMonths(1 * qty),
         PaymentPeriodUnit.Day => datePlan.AddDays((plan.PeriodQty ?? 1) * qty),
         PaymentPeriodUnit.Week => datePlan.AddDays(7 * (plan.PeriodQty ?? 1) * qty),
         PaymentPeriodUnit.Month => datePlan.AddMonths((plan.PeriodQty ?? 1) * qty),
         PaymentPeriodUnit.Year => datePlan.AddYears((plan.PeriodQty ?? 1) * qty),
         _ => throw new InvalidOperationException(Res.GetPlanPeriodUnrecognizedUnit)
     });
 /// <summary>
 /// Calculates the start and end dates of a period, and returns whether the period end is past and due to be paid.
 /// </summary>
 /// <param name="plan">The payment plan to calculate.</param>
 /// <param name="planDate">The date to calculate period start and end dates for.</param>
 /// <param name="currentDate">The current date, ensuring all calculations are done using the exact same date.</param>
 /// <param name="periodStart">Returns the period start date.</param>
 /// <param name="periodEnd">Returns the period end date.</param>
 /// <returns>Whether endDate is past and due to be paid.</returns>
 private bool GetPlanPeriod(ApiPaymentPlan plan, DateTimeOffset planDate, DateTimeOffset currentDate, out DateTimeOffset periodStart, out DateTimeOffset periodEnd)
 {
     if (plan.PeriodUnit == PaymentPeriodUnit.MonthPostpaid || plan.PeriodUnit == PaymentPeriodUnit.MonthPostpaidTrial)
     {
         periodStart = new DateTimeOffset(planDate.Year, planDate.Month, 1, 0, 0, 0, TimeSpan.Zero);
     }
     else
     {
         periodStart = planDate;
     }
     periodEnd = MovePeriodDate(plan, periodStart, 1);
     return(periodEnd <= currentDate.AddHours(_config.Value.PayHoursAhead));
 }