From 79ca0256618b91faf38db28af05dbb0b99278772 Mon Sep 17 00:00:00 2001 From: v-sanjmaurya Date: Tue, 23 Jun 2026 16:45:07 +0530 Subject: [PATCH] Fix and Automartion --- .../Codeunits/BillingProposal.Codeunit.al | 9 +++- .../Billing/RecurringBillingTest.Codeunit.al | 44 +++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/BillingProposal.Codeunit.al b/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/BillingProposal.Codeunit.al index 81bfa4d6c0..9db14aea94 100644 --- a/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/BillingProposal.Codeunit.al +++ b/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/BillingProposal.Codeunit.al @@ -483,6 +483,7 @@ codeunit 8062 "Billing Proposal" local procedure CalculateBillingPeriod(ServiceCommitment: Record "Subscription Line"; BillingDate: Date; BillToDate: Date; var BillingPeriodStart: Date; var BillingPeriodEnd: Date) var UsageDataBilling: Record "Usage Data Billing"; + PreviousBillingPeriodEnd: Date; begin BillingPeriodEnd := 0D; BillingPeriodStart := ServiceCommitment."Next Billing Date"; @@ -505,10 +506,14 @@ codeunit 8062 "Billing Proposal" end; BillingPeriodEnd := CalculateNextBillingToDateForServiceCommitment(ServiceCommitment, BillingPeriodStart); + PreviousBillingPeriodEnd := 0D; while (BillingPeriodEnd < BillingDate) and - ((BillingPeriodEnd < ServiceCommitment."Subscription Line End Date") or (ServiceCommitment."Subscription Line End Date" = 0D)) - do + ((BillingPeriodEnd < ServiceCommitment."Subscription Line End Date") or (ServiceCommitment."Subscription Line End Date" = 0D)) and + (BillingPeriodEnd > PreviousBillingPeriodEnd) + do begin + PreviousBillingPeriodEnd := BillingPeriodEnd; BillingPeriodEnd := CalculateNextBillingToDateForServiceCommitment(ServiceCommitment, BillingPeriodEnd + 1); + end; end; procedure CalculateNextBillingToDateForServiceCommitment(ServiceCommitment: Record "Subscription Line"; BillingFromDate: Date) NextBillingToDate: Date diff --git a/src/Apps/W1/Subscription Billing/Test/Billing/RecurringBillingTest.Codeunit.al b/src/Apps/W1/Subscription Billing/Test/Billing/RecurringBillingTest.Codeunit.al index 4ace5e4a06..a6be06ce12 100644 --- a/src/Apps/W1/Subscription Billing/Test/Billing/RecurringBillingTest.Codeunit.al +++ b/src/Apps/W1/Subscription Billing/Test/Billing/RecurringBillingTest.Codeunit.al @@ -68,6 +68,7 @@ codeunit 139688 "Recurring Billing Test" PostedDocumentNo: Code[20]; StrMenuHandlerStep: Integer; BillingProposalNotCreatedErr: Label 'Billing proposal not created.', Locked = true; + BillingToCappedErr: Label 'Billing to should be capped at the harmonized Next Billing To date.', Locked = true; ExtendedTextValueErr: Label 'Sales line with extended text description should be created.', Locked = true; ExtendedTextPurchValueErr: Label 'Purchase line with extended text description should be created.', Locked = true; RecurringBillingPage: TestPage "Recurring Billing"; @@ -1571,6 +1572,49 @@ codeunit 139688 "Recurring Billing Test" Assert.AreEqual(ExpectedNextBillingDate, ServiceCommitment."Next Billing Date", 'Next Billing Date should be the day after the last Billing To date.'); end; + [Test] + procedure CreateBillingProposalForHarmonizedContractWithCappedNextBillingToDoesNotHang() + var + ContractType: Record "Subscription Contract Type"; + CappedNextBillingTo: Date; + BillingDate: Date; + begin + // [SCENARIO 637042] Creating a Billing Proposal for a harmonized billing contract must not hang when the contract "Next Billing To" caps the calculated billing period below the Billing Date + Initialize(); + + // [GIVEN] An LCY Customer Subscription Contract with a monthly Subscription Line + CreateCustomerContract('<1M>', '<12M>'); + FindFirstServiceCommitment(); + + // [GIVEN] The contract uses a harmonized billing contract type + ContractTestLibrary.CreateContractType(ContractType); + ContractType.HarmonizedBillingCustContracts := true; + ContractType.Modify(false); + + // [GIVEN] The harmonized "Next Billing To" is frozen at the first billing period end, which is earlier than the Billing Date + CappedNextBillingTo := ServiceCommitment.CalculateNextToDate(ServiceCommitment."Billing Rhythm", ServiceCommitment."Next Billing Date"); + BillingDate := CalcDate('<+6M>', ServiceCommitment."Next Billing Date"); + CustomerContract.Get(CustomerContract."No."); + CustomerContract."Contract Type" := ContractType.Code; + CustomerContract."Next Billing To" := CappedNextBillingTo; + CustomerContract.Modify(false); + + // [GIVEN] A Billing Template filtered on the harmonized contract + CustomerContract.SetRecFilter(); + ContractTestLibrary.CreateRecurringBillingTemplate(BillingTemplate, '', '', CustomerContract.GetView(), Enum::"Service Partner"::Customer); + + // [WHEN] Create Billing Proposal up to a Billing Date that lies beyond the frozen "Next Billing To" + // [THEN] The process completes without entering an infinite loop in CalculateBillingPeriod (before the fix it would spin until a SQL timeout) + ContractTestLibrary.CreateBillingProposal(BillingTemplate, Enum::"Service Partner"::Customer, BillingDate, 0D); + + // [THEN] A Billing Line is created and its "Billing to" is capped at the harmonized "Next Billing To" date + BillingLine.Reset(); + BillingLine.SetRange("Subscription Contract No.", CustomerContract."No."); + Assert.RecordIsNotEmpty(BillingLine); + BillingLine.FindLast(); + Assert.AreEqual(CappedNextBillingTo, BillingLine."Billing to", BillingToCappedErr); + end; + #endregion Tests #region Procedures