[Bug][SubscriptionBilling] Enforce Subscription Line Start Date change rules on all edit paths
Nobody has claimed this yet.
- Dominant language
- AL
- Stars
- 683
- Forks
- 459
- Avg merge
- 3d 26m
- Merged PRs (30d)
- 633
Description
Describe the issue
The start date of a contract line can still be changed after the line has already been billed. When it is changed, the next billing date silently moves along with it, so a period that was already invoiced can be invoiced again, or a period can be skipped and never invoiced at all. The user gets no warning and no error — the change simply goes through.
There are two separate gaps:
-
The protection is missing on one of the pages. Changing the start date from the customer or vendor contract line list is blocked once the line has been billed. Changing the same field on the Subscription Lines page is not blocked at all, so the same edit that is rejected in one place is accepted in another.
-
Where the protection does run, it asks the wrong question. It looks at the total invoiced value of the line instead of at whether the line was billed. A line that was billed at zero value (free or 100% discounted subscription), or a line whose invoices and credit memos happen to add up to zero, is treated as if it had never been billed, and the start date can be moved.
The intended business rule is that the start date may only be changed when the line has never been billed, or when the next billing date is back at the start date — which is the state after a cancellation or credit memo, and the case where a contract manager legitimately needs to correct the billing period. In every other case the change must be rejected with an error.
The impact is unintentional billing gaps and double billing on live contracts, which are usually only noticed after the invoices have been sent.
Affected objects
src/Apps/W1/Subscription Billing/App/Service Commitments/Tables/SubscriptionLine.Table.alsrc/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommitments.Page.alsrc/Apps/W1/Subscription Billing/App/Customer Contracts/Pages/CustomerContractLineSubp.Page.alsrc/Apps/W1/Subscription Billing/App/Vendor Contracts/Pages/VendorContractLineSubpage.Page.al
Expected behavior
Changing "Subscription Line Start Date" on table Subscription Line should be allowed only if at least one of the following holds:
- No billing has been performed for the Subscription Line — neither
Billing LinenorBilling Line Archiverecords exist for"Entry No.". "Next Billing Date"equals"Subscription Line Start Date"— the state left behind by a cancellation or credit memo, which is exactly the correction case that must stay open.
Otherwise the change must be blocked with an error. "Next Billing Date" must remain non-editable directly; this is already the case (field 8 is Editable = false).
Current behavior
The guard is not on the field. "Subscription Line Start Date" (field 6) validates unconditionally and always recomputes the next billing date:
trigger OnValidate()
begin
DateFormulaManagement.ErrorIfDateEmpty("Subscription Line Start Date", FieldCaption("Subscription Line Start Date"));
UpdateNextBillingDate("Subscription Line Start Date" - 1);
CheckServiceDates();
...
end;
UpdateNextBillingDate then sets "Next Billing Date" := CalcDate('<+1D>', LastBillingToDate), so the next billing date follows the new start date.
The billed-state check lives only in UpdateServiceCommitment(CalledByFieldNo: Integer), in the FieldNo("Subscription Line Start Date") branch:
FieldNo("Subscription Line Start Date"):
begin
Rec.ErrorIfBillingLineArchiveForServiceCommitmentExist();
Rec.ErrorIfBillingLineForServiceCommitmentExist();
Validate("Subscription Line Start Date", "Subscription Line Start Date");
end;
UpdateServiceCommitment is called only from CustomerContractLineSubp.Page.al and VendorContractLineSubpage.Page.al. Page Subscription Lines exposes Rec."Subscription Line Start Date" as a plain editable field whose OnValidate only calls CurrPage.Update(), so that path bypasses the guard completely.
ErrorIfBillingLineArchiveForServiceCommitmentExist is amount-based rather than billing-state based:
BillingLineArchive.FilterBillingLineArchiveOnServiceCommitment(Rec."Entry No.");
BillingLineArchive.CalcSums(Amount);
if BillingLineArchive.Amount <> 0 then
Error(BillingLineArchiveForServiceCommitmentExistErr);
Any line whose archived billing amounts net to zero passes this test even though billing occurred and "Next Billing Date" no longer equals "Subscription Line Start Date".
Proposed change
- Move the check into the
OnValidatetrigger of"Subscription Line Start Date", beforeUpdateNextBillingDateis called, so every edit path (pages, code, data import) is covered. - Replace the amount test in
ErrorIfBillingLineArchiveForServiceCommitmentExistwith an existence check (IsEmpty), and combine it with the"Next Billing Date" = "Subscription Line Start Date"allowance, so the two documented conditions above are evaluated instead of a summed amount. - Keep the existing error message, e.g.
The contract line has already been billed. The Subscription Line start date can no longer be changed. - Remove the now-redundant calls from
UpdateServiceCommitmentonce the field trigger owns the rule. - Secondary: consider raising an integration event (for example
OnBeforeCheckSubscriptionLineStartDateChangeAllowedwith anIsHandled/IsAllowedparameter) so localizations and extensions can extend the allowance without duplicating the trigger.
Steps to reproduce
Scenario A — the check is skipped on the Subscription Lines page
- Create a customer contract with one contract line and a monthly billing rhythm, starting 01-01-2026.
- Run Create Billing Proposal and post the resulting contract invoice, so the line is billed for January.
- Open the contract line in the contract's line list, change Subscription Line Start Date to 01-03-2026, and confirm that the change is rejected with an error.
- Search for and open the Subscription Lines page and locate the same line.
- Change Subscription Line Start Date to 01-03-2026 there.
- Actual: the change is accepted and Next Billing Date silently moves to 01-03-2026, leaving February unbilled. Expected: the change is rejected with the same error as in step 3.
Scenario B — the check passes because the billed amounts net to zero
- Create a customer contract with one contract line, starting 01-01-2026, monthly rhythm, and set the price so the line bills at 0.00 (free item or 100% discount).
- Run Create Billing Proposal and post the contract invoice for January, so the line has been billed and Next Billing Date is 01-02-2026.
- Open the contract line in the contract's line list and change Subscription Line Start Date to 01-03-2026.
- Actual: the change is accepted, because the archived billing amounts sum to zero, and Next Billing Date moves to 01-03-2026. Expected: the change is rejected — the line has been billed and Next Billing Date does not equal Subscription Line Start Date.
Additional context
No response
I will provide a fix for a bug
- I will provide a fix for a bug
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the Subscription Line table trigger in src/Apps/W1/Subscription Billing/App/Service Commitments/Tables/SubscriptionLine.Table.al, then inspect UpdateServiceCommitment in the customer and vendor contract pages and the Subscription Lines page path. Reproduce scenarios A and B to verify every edit path rejects changes after billing unless no billing records exist or Next Billing Date equals Subscription Line Start Date.
Written by the indexing model from the issue text.
Assessment
- Domain
- backend, payments
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100