0156

Mid-Period Plan Change

Algorithm
Easy
date
rounding
proration
subscription

Mid-Period Plan Change

A customer on your Streaming Plus plan just upgraded to Premium — eleven days into the billing period, of course, never on day one. The full period was already invoiced at the old price, so billing now owes the customer two adjustment lines: a credit for the days they will not use on the old plan, and a charge for those same days at the new price. This is exactly what Business Central's subscription-billing module does on every mid-cycle contract change, and finance will reject the batch if the numbers drift by a single cent.

Beware the folklore: subscription proration here has nothing to do with "a month is 30 days". Days are counted on the real calendar, and February — 28 days, or 29 in a leap year — is where the 30-day shortcut goes to die.

Requirements

Create a codeunit named "Plan Change Prorator" with one public procedure:

procedure ProrateChange(PeriodStart: Date; PeriodEnd: Date; ChangeDate: Date; OldPrice: Decimal; NewPrice: Decimal; var CreditAmount: Decimal; var ChargeAmount: Decimal)

What you may rely on — the tests never violate this:

  • The billing period runs from PeriodStart through PeriodEnd, both days inclusive, and is at least one day long. The customer has already been invoiced OldPrice for the whole period.
  • PeriodStart <= ChangeDate <= PeriodEnd. The change takes effect on ChangeDate: that day and every later day of the period belong to the new plan.
  • OldPrice and NewPrice are >= 0 and exact multiples of 0.01. NewPrice = 0 means the customer cancels — the remaining days are simply not billed.

What the procedure must produce — all of it is graded:

  1. Day counts use actual calendar days, inclusive on both ends: the period day count runs from PeriodStart through PeriodEnd, and the remaining day count runs from ChangeDate through PeriodEnd. A February period has 28 or 29 days — never 30.
  2. CreditAmount is the value of the remaining days at the old price: OldPrice × remaining days ÷ period days, rounded to the nearest cent, with an exact half-cent rounding up (away from zero). It is returned as a positive amount.
  3. ChargeAmount is the value of the remaining days at the new price: NewPrice × remaining days ÷ period days, same rounding. A cancellation therefore charges exactly 0.00.
  4. Both lines cover the same remaining days with the same rounding, so a plan "change" that keeps the price unchanged must yield CreditAmount equal to ChargeAmount to the cent — the customer nets exactly zero. This symmetry is graded with randomized inputs.
  5. Assign both var parameters unconditionally — the tests pass in variables that already hold garbage, and whatever your procedure leaves there is what gets graded.

What the tests check

The tests call ProrateChange and compare both amounts penny-exact against hand-computed fixtures: a change on the first day of the period (full credit, full charge), a mid-January upgrade, a downgrade inside a 28-day February, an upgrade inside a leap-year February, a cancellation, a change on the last day of the period (exactly one remaining day), and a fixture whose exact shares end in half a cent. Two randomized tests finish the job: a same-price switch must net to exactly zero, and a fully random plan change is checked against independently computed proration — so hardcoding the examples fails.

Pick your object ID in the 50100–50199 range and reference other objects by name, never by ID.

Learn More

Hint 1
January has 31 days and February has 28 or 29 — any '÷ 30' in your formula misprices most months of the year. Count real calendar days, and remember both the period and the remaining window include their boundary days: a change on the last period day still leaves one billable day.
Hint 2
In AL, subtracting one Date from another yields the number of days between them as an Integer — so the remaining day count from the change date through the period end, both inclusive, is PeriodEnd - ChangeDate + 1, and the period length is PeriodEnd - PeriodStart + 1.
Hint 3
Round(Amount, 0.01) rounds to the nearest cent with ties away from zero — exactly the rounding the statement asks for. Compute both lines from the same remaining-day count with that same rounding; the symmetry is what makes a same-price switch credit and charge the identical amount.
ALBusiness Central 28.4
Press Compile to check your code compiles — Submit runs the tests.
The code editor is desktop-only
Open this problem on a computer to write and run code. Reading the description, tests and discussion works fine here.