From 71df770de18c09f108ad6ca124340f31e46ce261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miljan=20Milosavljevi=C4=87?= Date: Wed, 28 Jan 2026 16:19:56 +0100 Subject: [PATCH 1/2] Calculating inital dates correctly --- .../Codeunits/SalesDocuments.Codeunit.al | 3 +- .../PostSubContractRenewal.Codeunit.al | 3 +- .../Pages/PlannedServiceCommitments.Page.al | 2 +- .../ContractAnalysisEntries.Page.al | 2 +- .../SubContrAnalysisEntry.Table.al | 1 + .../Pages/ClosedCustContLineSubp.Page.al | 2 +- .../Pages/CustomerContractLineSubp.Page.al | 2 +- .../Pages/CustomerContractLines.Page.al | 2 +- .../CreateSubscriptionLine.Codeunit.al | 3 +- .../Pages/ImportedServiceCommitments.Page.al | 2 +- .../Pages/SalesServCommArchiveList.Page.al | 2 +- .../Pages/SalesServiceCommitments.Page.al | 2 +- .../Pages/SalesServiceCommitmentsList.Page.al | 2 +- .../Pages/ServiceCommPackageLines.Page.al | 2 +- .../Pages/ServiceCommitmentArchive.Page.al | 2 +- .../Pages/ServiceCommitments.Page.al | 2 +- .../Pages/ServiceCommitmentsList.Page.al | 2 +- .../Tables/SubscriptionLine.Table.al | 46 +---- .../Tables/SubscriptionHeader.Table.al | 1 - .../Pages/ClosedVendContLineSubp.Page.al | 2 +- .../Pages/VendorContractLineSubpage.Page.al | 2 +- .../ServiceObjectTest.Codeunit.al | 190 ++++++++++-------- .../UBB/UsageBasedRebillingTest.Codeunit.al | 1 + 23 files changed, 133 insertions(+), 145 deletions(-) diff --git a/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/SalesDocuments.Codeunit.al b/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/SalesDocuments.Codeunit.al index 9ed9b0a127..14be5477d6 100644 --- a/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/SalesDocuments.Codeunit.al +++ b/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/SalesDocuments.Codeunit.al @@ -542,9 +542,8 @@ codeunit 8063 "Sales Documents" SubscriptionLine.Validate("Calculation Base Amount", SubscriptionLine."Calculation Base Amount" * -1); SubscriptionLine.UpdateNextPriceUpdate(); - SubscriptionLine.CalculateInitialTermUntilDate(); SubscriptionLine.CalculateInitialServiceEndDate(); - SubscriptionLine.CalculateInitialCancellationPossibleUntilDate(); + SubscriptionLine.CalculateInitialTermUntilDate(); SubscriptionLine.SetCurrencyData(SalesHeader."Currency Factor", SalesHeader."Posting Date", SalesHeader."Currency Code"); SubscriptionLine.SetLCYFields(true); if SalesLine."No." = SubscriptionLine."Invoicing Item No." then begin diff --git a/src/Apps/W1/Subscription Billing/App/Contract Renewal/Codeunits/PostSubContractRenewal.Codeunit.al b/src/Apps/W1/Subscription Billing/App/Contract Renewal/Codeunits/PostSubContractRenewal.Codeunit.al index 6d8213dde8..7ad1dc2967 100644 --- a/src/Apps/W1/Subscription Billing/App/Contract Renewal/Codeunits/PostSubContractRenewal.Codeunit.al +++ b/src/Apps/W1/Subscription Billing/App/Contract Renewal/Codeunits/PostSubContractRenewal.Codeunit.al @@ -107,9 +107,8 @@ codeunit 8004 "Post Sub. Contract Renewal" TempServiceCommitment."Subscription Line End Date" := CalcDate('<-1D>', TempServiceCommitment."Subscription Line End Date"); end; TempServiceCommitment.CopyFromSalesServiceCommitment(SalesServiceCommitment); - TempServiceCommitment.CalculateInitialTermUntilDate(); TempServiceCommitment.CalculateInitialServiceEndDate(); - TempServiceCommitment.CalculateInitialCancellationPossibleUntilDate(); + TempServiceCommitment.CalculateInitialTermUntilDate(); TempServiceCommitment.SetCurrencyData(SalesHeader."Currency Factor", SalesHeader."Posting Date", SalesHeader."Currency Code"); TempServiceCommitment.SetLCYFields(true); TempServiceCommitment.SetDefaultDimensions(true); diff --git a/src/Apps/W1/Subscription Billing/App/Contract Renewal/Pages/PlannedServiceCommitments.Page.al b/src/Apps/W1/Subscription Billing/App/Contract Renewal/Pages/PlannedServiceCommitments.Page.al index 53383f3c7c..831afb9cc5 100644 --- a/src/Apps/W1/Subscription Billing/App/Contract Renewal/Pages/PlannedServiceCommitments.Page.al +++ b/src/Apps/W1/Subscription Billing/App/Contract Renewal/Pages/PlannedServiceCommitments.Page.al @@ -182,7 +182,7 @@ page 8004 "Planned Service Commitments" } field("Extension Term"; Rec."Extension Term") { - ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until". If the field is empty and the initial term or notice period is filled, the end of Subscription Line is automatically set to the end of the initial term or notice period.'; + ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until".'; } field("Cancellation Possible Until"; Rec."Cancellation Possible Until") { diff --git a/src/Apps/W1/Subscription Billing/App/ContractAnalysis/ContractAnalysisEntries.Page.al b/src/Apps/W1/Subscription Billing/App/ContractAnalysis/ContractAnalysisEntries.Page.al index 1ddfc07c09..11ad347b02 100644 --- a/src/Apps/W1/Subscription Billing/App/ContractAnalysis/ContractAnalysisEntries.Page.al +++ b/src/Apps/W1/Subscription Billing/App/ContractAnalysis/ContractAnalysisEntries.Page.al @@ -142,7 +142,7 @@ page 8090 "Contract Analysis Entries" } field("Extension Term"; Rec."Extension Term") { - ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until". If the field is empty and the initial term or notice period is filled, the end of Subscription Line is automatically set to the end of the initial term or notice period.'; + ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until".'; } field("Billing Rhythm"; Rec."Billing Rhythm") { diff --git a/src/Apps/W1/Subscription Billing/App/ContractAnalysis/SubContrAnalysisEntry.Table.al b/src/Apps/W1/Subscription Billing/App/ContractAnalysis/SubContrAnalysisEntry.Table.al index 3a43e7bbfa..1d5d39995d 100644 --- a/src/Apps/W1/Subscription Billing/App/ContractAnalysis/SubContrAnalysisEntry.Table.al +++ b/src/Apps/W1/Subscription Billing/App/ContractAnalysis/SubContrAnalysisEntry.Table.al @@ -341,6 +341,7 @@ table 8019 "Sub. Contr. Analysis Entry" Rec."Currency Factor Date" := ServiceCommitment."Currency Factor Date"; Rec."Extension Term" := ServiceCommitment."Extension Term"; Rec."Initial Term" := ServiceCommitment."Initial Term"; + Rec."Renewal Term" := ServiceCommitment."Renewal Term"; Rec."Invoicing Item No." := ServiceCommitment."Invoicing Item No."; Rec."Next Billing Date" := ServiceCommitment."Next Billing Date"; Rec."Notice Period" := ServiceCommitment."Notice Period"; diff --git a/src/Apps/W1/Subscription Billing/App/Customer Contracts/Pages/ClosedCustContLineSubp.Page.al b/src/Apps/W1/Subscription Billing/App/Customer Contracts/Pages/ClosedCustContLineSubp.Page.al index 2fe840189c..bf69325b64 100644 --- a/src/Apps/W1/Subscription Billing/App/Customer Contracts/Pages/ClosedCustContLineSubp.Page.al +++ b/src/Apps/W1/Subscription Billing/App/Customer Contracts/Pages/ClosedCustContLineSubp.Page.al @@ -239,7 +239,7 @@ page 8080 "Closed Cust. Cont. Line Subp." field("Extension Term"; ServiceCommitment."Extension Term") { Caption = 'Subsequent Term'; - ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until". If the field is empty and the initial term or notice period is filled, the end of Subscription Line is automatically set to the end of the initial term or notice period.'; + ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until".'; Editable = false; Visible = false; } diff --git a/src/Apps/W1/Subscription Billing/App/Customer Contracts/Pages/CustomerContractLineSubp.Page.al b/src/Apps/W1/Subscription Billing/App/Customer Contracts/Pages/CustomerContractLineSubp.Page.al index 59377e1232..bb4b9b904e 100644 --- a/src/Apps/W1/Subscription Billing/App/Customer Contracts/Pages/CustomerContractLineSubp.Page.al +++ b/src/Apps/W1/Subscription Billing/App/Customer Contracts/Pages/CustomerContractLineSubp.Page.al @@ -386,7 +386,7 @@ page 8068 "Customer Contract Line Subp." field("Extension Term"; ServiceCommitment."Extension Term") { Caption = 'Subsequent Term'; - ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until". If the field is empty and the initial term or notice period is filled, the end of Subscription Line is automatically set to the end of the initial term or notice period.'; + ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until".'; Editable = false; Visible = false; } diff --git a/src/Apps/W1/Subscription Billing/App/Customer Contracts/Pages/CustomerContractLines.Page.al b/src/Apps/W1/Subscription Billing/App/Customer Contracts/Pages/CustomerContractLines.Page.al index bc6131edbe..f4f5476778 100644 --- a/src/Apps/W1/Subscription Billing/App/Customer Contracts/Pages/CustomerContractLines.Page.al +++ b/src/Apps/W1/Subscription Billing/App/Customer Contracts/Pages/CustomerContractLines.Page.al @@ -182,7 +182,7 @@ page 8075 "Customer Contract Lines" field("Extension Term"; ServiceCommitment."Extension Term") { Caption = 'Subsequent Term'; - ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until". If the field is empty and the initial term or notice period is filled, the end of Subscription Line is automatically set to the end of the initial term or notice period.'; + ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until".'; } field("Billing Rhythm"; ServiceCommitment."Billing Rhythm") { diff --git a/src/Apps/W1/Subscription Billing/App/Import/Codeunits/CreateSubscriptionLine.Codeunit.al b/src/Apps/W1/Subscription Billing/App/Import/Codeunits/CreateSubscriptionLine.Codeunit.al index ccdb17e50e..69fcb27457 100644 --- a/src/Apps/W1/Subscription Billing/App/Import/Codeunits/CreateSubscriptionLine.Codeunit.al +++ b/src/Apps/W1/Subscription Billing/App/Import/Codeunits/CreateSubscriptionLine.Codeunit.al @@ -118,10 +118,9 @@ codeunit 8006 "Create Subscription Line" if ImportedServiceCommitment."Calculation Base Amount (LCY)" <> 0 then ServiceCommitment."Calculation Base Amount (LCY)" := ImportedServiceCommitment."Calculation Base Amount (LCY)"; - ServiceCommitment.CalculateInitialTermUntilDate(); if ServiceCommitment."Subscription Line End Date" = 0D then ServiceCommitment.CalculateInitialServiceEndDate(); - ServiceCommitment.CalculateInitialCancellationPossibleUntilDate(); + ServiceCommitment.CalculateInitialTermUntilDate(); ServiceCommitment.SetDefaultDimensions(true); ServiceCommitment."Renewal Term" := ServiceCommitment."Initial Term"; diff --git a/src/Apps/W1/Subscription Billing/App/Import/Pages/ImportedServiceCommitments.Page.al b/src/Apps/W1/Subscription Billing/App/Import/Pages/ImportedServiceCommitments.Page.al index 0fb6499c8e..cf72258006 100644 --- a/src/Apps/W1/Subscription Billing/App/Import/Pages/ImportedServiceCommitments.Page.al +++ b/src/Apps/W1/Subscription Billing/App/Import/Pages/ImportedServiceCommitments.Page.al @@ -115,7 +115,7 @@ page 8009 "Imported Service Commitments" } field("Extension Term"; Rec."Extension Term") { - ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until". If the field is empty and the initial term or notice period is filled, the end of Subscription Line is automatically set to the end of the initial term or notice period.'; + ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until".'; } field("Billing Rhythm"; Rec."Billing Rhythm") { diff --git a/src/Apps/W1/Subscription Billing/App/Sales Service Commitments/Pages/SalesServCommArchiveList.Page.al b/src/Apps/W1/Subscription Billing/App/Sales Service Commitments/Pages/SalesServCommArchiveList.Page.al index ff2b28719f..acce26911b 100644 --- a/src/Apps/W1/Subscription Billing/App/Sales Service Commitments/Pages/SalesServCommArchiveList.Page.al +++ b/src/Apps/W1/Subscription Billing/App/Sales Service Commitments/Pages/SalesServCommArchiveList.Page.al @@ -78,7 +78,7 @@ page 8083 "Sales Serv. Comm. Archive List" } field("Extension Term"; Rec."Extension Term") { - ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until". If the field is empty and the initial term or notice period is filled, the end of Subscription Line is automatically set to the end of the initial term or notice period.'; + ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until".'; } field("Billing Base Period"; Rec."Billing Base Period") { diff --git a/src/Apps/W1/Subscription Billing/App/Sales Service Commitments/Pages/SalesServiceCommitments.Page.al b/src/Apps/W1/Subscription Billing/App/Sales Service Commitments/Pages/SalesServiceCommitments.Page.al index 063aed8c96..82c5ea8000 100644 --- a/src/Apps/W1/Subscription Billing/App/Sales Service Commitments/Pages/SalesServiceCommitments.Page.al +++ b/src/Apps/W1/Subscription Billing/App/Sales Service Commitments/Pages/SalesServiceCommitments.Page.al @@ -101,7 +101,7 @@ page 8082 "Sales Service Commitments" } field("Extension Term"; Rec."Extension Term") { - ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until". If the field is empty and the initial term or notice period is filled, the end of Subscription Line is automatically set to the end of the initial term or notice period.'; + ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until".'; } field("Billing Base Period"; Rec."Billing Base Period") { diff --git a/src/Apps/W1/Subscription Billing/App/Sales Service Commitments/Pages/SalesServiceCommitmentsList.Page.al b/src/Apps/W1/Subscription Billing/App/Sales Service Commitments/Pages/SalesServiceCommitmentsList.Page.al index 57a43c03b6..e97aaa4ac5 100644 --- a/src/Apps/W1/Subscription Billing/App/Sales Service Commitments/Pages/SalesServiceCommitmentsList.Page.al +++ b/src/Apps/W1/Subscription Billing/App/Sales Service Commitments/Pages/SalesServiceCommitmentsList.Page.al @@ -39,7 +39,7 @@ page 8015 "Sales Service Commitments List" } field("Extension Term"; Rec."Extension Term") { - ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until". If the field is empty and the initial term or notice period is filled, the end of Subscription Line is automatically set to the end of the initial term or notice period.'; + ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until".'; } field(Price; Rec.Price) { diff --git a/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommPackageLines.Page.al b/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommPackageLines.Page.al index d0bc7ad458..7a76f6f4fc 100644 --- a/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommPackageLines.Page.al +++ b/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommPackageLines.Page.al @@ -86,7 +86,7 @@ page 8058 "Service Comm. Package Lines" { Style = Strong; StyleExpr = Bold; - ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until". If the field is empty and the initial term or notice period is filled, the end of Subscription Line is automatically set to the end of the initial term or notice period.'; + ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until".'; } field("Notice Period"; Rec."Notice Period") { diff --git a/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommitmentArchive.Page.al b/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommitmentArchive.Page.al index 700cec6455..07822375d7 100644 --- a/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommitmentArchive.Page.al +++ b/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommitmentArchive.Page.al @@ -106,7 +106,7 @@ page 8094 "Service Commitment Archive" } field("Extension Term"; Rec."Extension Term") { - ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until". If the field is empty and the initial term or notice period is filled, the end of Subscription Line is automatically set to the end of the initial term or notice period.'; + ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until".'; } field("Cancellation Possible Until"; Rec."Cancellation Possible Until") { diff --git a/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommitments.Page.al b/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommitments.Page.al index a0d1f25be4..a810316723 100644 --- a/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommitments.Page.al +++ b/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommitments.Page.al @@ -191,7 +191,7 @@ page 8064 "Service Commitments" } field("Extension Term"; Rec."Extension Term") { - ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until". If the field is empty and the initial term or notice period is filled, the end of Subscription Line is automatically set to the end of the initial term or notice period.'; + ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until".'; } field("Renewal Term"; Rec."Renewal Term") { diff --git a/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommitmentsList.Page.al b/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommitmentsList.Page.al index 0c3e315da1..f633daa3e5 100644 --- a/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommitmentsList.Page.al +++ b/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommitmentsList.Page.al @@ -118,7 +118,7 @@ page 8014 "Service Commitments List" } field("Extension Term"; Rec."Extension Term") { - ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until". If the field is empty and the initial term or notice period is filled, the end of Subscription Line is automatically set to the end of the initial term or notice period.'; + ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until".'; Visible = false; } field("Billing Rhythm"; Rec."Billing Rhythm") diff --git a/src/Apps/W1/Subscription Billing/App/Service Commitments/Tables/SubscriptionLine.Table.al b/src/Apps/W1/Subscription Billing/App/Service Commitments/Tables/SubscriptionLine.Table.al index 451fda6dd2..519d51c1ba 100644 --- a/src/Apps/W1/Subscription Billing/App/Service Commitments/Tables/SubscriptionLine.Table.al +++ b/src/Apps/W1/Subscription Billing/App/Service Commitments/Tables/SubscriptionLine.Table.al @@ -743,8 +743,6 @@ table 8059 "Subscription Line" begin if IsInitialTermEmpty() then exit; - if not IsExtensionTermEmpty() then - exit; TestField("Subscription Line Start Date"); "Subscription Line End Date" := CalcDate("Initial Term", "Subscription Line Start Date"); @@ -752,43 +750,17 @@ table 8059 "Subscription Line" RefreshRenewalTerm(); end; - internal procedure CalculateInitialCancellationPossibleUntilDate() - begin - if IsExtensionTermEmpty() then - exit; - if IsNoticePeriodEmpty() then - exit; - if IsInitialTermEmpty() then - exit; - - TestField("Subscription Line Start Date"); - "Cancellation Possible Until" := CalcDate("Initial Term", "Subscription Line Start Date"); - CalendarManagement.ReverseDateFormula(NegativeDateFormula, "Notice Period"); - "Cancellation Possible Until" := CalcDate(NegativeDateFormula, "Cancellation Possible Until"); - "Cancellation Possible Until" := CalcDate('<-1D>', "Cancellation Possible Until"); - end; - internal procedure CalculateInitialTermUntilDate() begin - if "Subscription Line End Date" <> 0D then begin - "Term Until" := "Subscription Line End Date"; - exit; - end; - - if IsExtensionTermEmpty() then - exit; - if IsNoticePeriodEmpty() and IsInitialTermEmpty() then - exit; - - TestField("Subscription Line Start Date"); - if IsInitialTermEmpty() then begin - "Term Until" := CalcDate("Notice Period", "Subscription Line Start Date"); - "Term Until" := CalcDate('<-1D>', "Term Until"); - UpdateCancellationPossibleUntil(); - end else begin - "Term Until" := CalcDate("Initial Term", "Subscription Line Start Date"); - "Term Until" := CalcDate('<-1D>', "Term Until"); - end; + if "Subscription Line End Date" <> 0D then + "Term Until" := "Subscription Line End Date" + else + if not IsNoticePeriodEmpty() then begin + TestField("Subscription Line Start Date"); + "Term Until" := CalcDate("Notice Period", "Subscription Line Start Date"); + "Term Until" := CalcDate('<-1D>', "Term Until"); + end; + UpdateCancellationPossibleUntil(); end; internal procedure GetReferenceDate(): Date diff --git a/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al b/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al index 0b9bb1938a..9ba3215ce6 100644 --- a/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al +++ b/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al @@ -1806,7 +1806,6 @@ table 8057 "Subscription Header" ServiceCommitment."Subscription Line End Date" := ServiceEndDate else ServiceCommitment.CalculateInitialServiceEndDate(); - ServiceCommitment.CalculateInitialCancellationPossibleUntilDate(); ServiceCommitment.CalculateInitialTermUntilDate(); ServiceCommitment.ClearTerminationPeriodsWhenServiceEnded(); ServiceCommitment.UpdateNextBillingDate(ServiceCommitment."Subscription Line Start Date" - 1); diff --git a/src/Apps/W1/Subscription Billing/App/Vendor Contracts/Pages/ClosedVendContLineSubp.Page.al b/src/Apps/W1/Subscription Billing/App/Vendor Contracts/Pages/ClosedVendContLineSubp.Page.al index d29afce8b2..ecf555e19a 100644 --- a/src/Apps/W1/Subscription Billing/App/Vendor Contracts/Pages/ClosedVendContLineSubp.Page.al +++ b/src/Apps/W1/Subscription Billing/App/Vendor Contracts/Pages/ClosedVendContLineSubp.Page.al @@ -229,7 +229,7 @@ page 8089 "Closed Vend. Cont. Line Subp." field("Extension Term"; ServiceCommitment."Extension Term") { Caption = 'Subsequent Term'; - ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until". If the field is empty and the initial term or notice period is filled, the end of Subscription Line is automatically set to the end of the initial term or notice period.'; + ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until".'; Editable = false; Visible = false; } diff --git a/src/Apps/W1/Subscription Billing/App/Vendor Contracts/Pages/VendorContractLineSubpage.Page.al b/src/Apps/W1/Subscription Billing/App/Vendor Contracts/Pages/VendorContractLineSubpage.Page.al index 7e4b20928a..956aad2918 100644 --- a/src/Apps/W1/Subscription Billing/App/Vendor Contracts/Pages/VendorContractLineSubpage.Page.al +++ b/src/Apps/W1/Subscription Billing/App/Vendor Contracts/Pages/VendorContractLineSubpage.Page.al @@ -354,7 +354,7 @@ page 8078 "Vendor Contract Line Subpage" field("Extension Term"; ServiceCommitment."Extension Term") { Caption = 'Subsequent Term'; - ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until". If the field is empty and the initial term or notice period is filled, the end of Subscription Line is automatically set to the end of the initial term or notice period.'; + ToolTip = 'Specifies a date formula for automatic renewal after initial term and the rhythm of the update of "Notice possible to" and "Term Until".'; Editable = false; Visible = false; } diff --git a/src/Apps/W1/Subscription Billing/Test/Service Objects/ServiceObjectTest.Codeunit.al b/src/Apps/W1/Subscription Billing/Test/Service Objects/ServiceObjectTest.Codeunit.al index 1a481d53be..c1d90ce6a1 100644 --- a/src/Apps/W1/Subscription Billing/Test/Service Objects/ServiceObjectTest.Codeunit.al +++ b/src/Apps/W1/Subscription Billing/Test/Service Objects/ServiceObjectTest.Codeunit.al @@ -283,35 +283,43 @@ codeunit 148157 "Service Object Test" [Test] procedure CheckClearTerminationPeriods() var - ItemServCommitmentPackage: Record "Item Subscription Package"; - ServiceCommPackageLine: Record "Subscription Package Line"; - ServiceCommitment: Record "Subscription Line"; - ServiceCommitmentPackage: Record "Subscription Package"; - ServiceObject: Record "Subscription Header"; - ServiceCommitmentTemplateCode: Code[20]; + ItemSubscriptionPackage: Record "Item Subscription Package"; + SubscriptionPackageLine: Record "Subscription Package Line"; + SubscriptionLine: Record "Subscription Line"; + SubscriptionPackage: Record "Subscription Package"; + SubscriptionHeader: Record "Subscription Header"; + SubscriptionTemplateCode: Code[20]; ServiceAndCalculationStartDate: Date; ServiceEndDate: Date; begin + // [SCENARIO] Termination periods are cleared when Service End Date is set to a date in the past + // [SCENARIO] This ensures that ended services don't show active termination periods Initialize(); - ServiceAndCalculationStartDate := CalcDate('<-1Y>', WorkDate()); - SetupServiceObjectTemplatePackageAndAssignItemToPackage(ServiceCommitmentTemplateCode, ServiceObject, ServiceCommitmentPackage, ServiceCommPackageLine); - ModifyCurrentServiceCommPackageLine('<12M>', '<12M>', '<1M>', ServiceCommPackageLine); + // [GIVEN] A Subscription Line with Initial Term of 12M, Extension Term of 12M, and Notice Period of 1M + // [GIVEN] Service Start Date is set to 6 months ago + ServiceAndCalculationStartDate := CalcDate('<-6M>', WorkDate()); + SetupServiceObjectTemplatePackageAndAssignItemToPackage(SubscriptionTemplateCode, SubscriptionHeader, SubscriptionPackage, SubscriptionPackageLine); + ModifyCurrentServiceCommPackageLine('<12M>', '<12M>', '<1M>', SubscriptionPackageLine); - ServiceCommitmentPackage.SetFilter(Code, ItemServCommitmentPackage.GetPackageFilterForItem(ServiceObject."Source No.")); - ServiceObject.InsertServiceCommitmentsFromServCommPackage(ServiceAndCalculationStartDate, ServiceCommitmentPackage); + SubscriptionPackage.SetFilter(Code, ItemSubscriptionPackage.GetPackageFilterForItem(SubscriptionHeader."Source No.")); + SubscriptionHeader.InsertServiceCommitmentsFromServCommPackage(ServiceAndCalculationStartDate, SubscriptionPackage); - ServiceCommitment.SetRange("Subscription Header No.", ServiceObject."No."); - ServiceCommitment.FindFirst(); - Assert.AreEqual(0D, ServiceCommitment."Subscription Line End Date", '"Service End Date" is set.'); - Assert.AreNotEqual(0D, ServiceCommitment."Term Until", '"Term Until" not set.'); - Assert.AreNotEqual(0D, ServiceCommitment."Cancellation Possible Until", '"Cancellation Possible Until" is not set.'); + SubscriptionLine.SetRange("Subscription Header No.", SubscriptionHeader."No."); + SubscriptionLine.FindFirst(); - ServiceEndDate := CalcDate('<-6M>', WorkDate()); + // [GIVEN] The Subscription Line End Date, Term Until, and Cancellation Possible Until are populated + Assert.AreNotEqual(0D, SubscriptionLine."Subscription Line End Date", '"Service End Date" not set.'); + Assert.AreNotEqual(0D, SubscriptionLine."Term Until", '"Term Until" not set.'); + Assert.AreNotEqual(0D, SubscriptionLine."Cancellation Possible Until", '"Cancellation Possible Until" is not set.'); - ServiceCommitment.Validate("Subscription Line End Date", ServiceEndDate); - Assert.AreEqual(0D, ServiceCommitment."Term Until", '"Term Until" not cleared.'); - Assert.AreEqual(0D, ServiceCommitment."Cancellation Possible Until", '"Cancellation Possible Until" is not cleared.'); + // [WHEN] The Subscription Line End Date is changed to a date in the past (before WorkDate) + ServiceEndDate := CalcDate('<-3M>', WorkDate()); + SubscriptionLine.Validate("Subscription Line End Date", ServiceEndDate); + + // [THEN] Term Until and Cancellation Possible Until are cleared (set to 0D) + Assert.AreEqual(0D, SubscriptionLine."Term Until", '"Term Until" not cleared.'); + Assert.AreEqual(0D, SubscriptionLine."Cancellation Possible Until", '"Cancellation Possible Until" is not cleared.'); end; [Test] @@ -601,39 +609,43 @@ codeunit 148157 "Service Object Test" end; [Test] - procedure CheckServiceCommitmentServiceInitialEndDateCalculation() + procedure CheckSubscriptionLineInitialEndDateCalculation() var Item: Record Item; - ServiceCommitment: Record "Subscription Line"; - ServiceObject: Record "Subscription Header"; + SubscriptionLine: Record "Subscription Line"; + SubscriptionHeader: Record "Subscription Header"; DateFormulaVariable: DateFormula; ExpectedServiceEndDate: Date; begin + // [SCENARIO] CalculateInitialServiceEndDate sets Subscription Line End Date based on Initial Term + // [SCENARIO] When Initial Term is empty, the End Date is not calculated Initialize(); - SetupServiceObjectWithServiceCommitment(Item, ServiceObject, false, false); - FindServiceCommitment(ServiceCommitment, ServiceObject."No."); - ServiceCommitment.Validate("Subscription Line Start Date", WorkDate()); + SetupServiceObjectWithServiceCommitment(Item, SubscriptionHeader, false, false); + FindServiceCommitment(SubscriptionLine, SubscriptionHeader."No."); + // [GIVEN] A Subscription Line with Start Date set to WorkDate and Initial Term of 1 month + SubscriptionLine.Validate("Subscription Line Start Date", WorkDate()); Evaluate(DateFormulaVariable, '<1M>'); - - Clear(ServiceCommitment."Extension Term"); - ServiceCommitment.Validate("Initial Term", DateFormulaVariable); - ExpectedServiceEndDate := CalcDate(ServiceCommitment."Initial Term", ServiceCommitment."Subscription Line Start Date"); + SubscriptionLine.Validate("Initial Term", DateFormulaVariable); + ExpectedServiceEndDate := CalcDate(SubscriptionLine."Initial Term", SubscriptionLine."Subscription Line Start Date"); ExpectedServiceEndDate := CalcDate('<-1D>', ExpectedServiceEndDate); - ServiceCommitment.CalculateInitialServiceEndDate(); - ServiceCommitment.TestField("Subscription Line End Date", ExpectedServiceEndDate); - Clear(ServiceCommitment."Subscription Line End Date"); - ServiceCommitment.Validate("Extension Term", DateFormulaVariable); - ServiceCommitment.CalculateInitialServiceEndDate(); - ServiceCommitment.TestField("Subscription Line End Date", 0D); + // [WHEN] CalculateInitialServiceEndDate is called + SubscriptionLine.CalculateInitialServiceEndDate(); - Clear(ServiceCommitment."Subscription Line End Date"); - Clear(ServiceCommitment."Extension Term"); - Clear(ServiceCommitment."Initial Term"); - ServiceCommitment.CalculateInitialServiceEndDate(); - ServiceCommitment.TestField("Subscription Line End Date", 0D); + // [THEN] Subscription Line End Date is set to Start Date + Initial Term - 1 day + SubscriptionLine.TestField("Subscription Line End Date", ExpectedServiceEndDate); + + // [GIVEN] The Subscription Line End Date and Initial Term are cleared + Clear(SubscriptionLine."Subscription Line End Date"); + Clear(SubscriptionLine."Initial Term"); + + // [WHEN] CalculateInitialServiceEndDate is called with empty Initial Term + SubscriptionLine.CalculateInitialServiceEndDate(); + + // [THEN] Subscription Line End Date remains empty (0D) + SubscriptionLine.TestField("Subscription Line End Date", 0D); end; [Test] @@ -681,45 +693,53 @@ codeunit 148157 "Service Object Test" end; [Test] - procedure CheckServiceCommitmentUpdateTerminationDatesCalculation() + procedure CheckSubscriptionLineUpdateTerminationDatesCalculation() var Item: Record Item; - ItemServCommitmentPackage: Record "Item Subscription Package"; - ServiceCommPackageLine: Record "Subscription Package Line"; - ServiceCommitment: Record "Subscription Line"; - ServiceCommitment2: Record "Subscription Line"; - ServiceCommitmentPackage: Record "Subscription Package"; - ServiceCommitmentTemplate: Record "Sub. Package Line Template"; - ServiceObject: Record "Subscription Header"; + ItemSubscriptionPackage: Record "Item Subscription Package"; + SubscriptionPackageLine: Record "Subscription Package Line"; + SubscriptionLine: Record "Subscription Line"; + OldSubscriptionLine: Record "Subscription Line"; + SubscriptionPackage: Record "Subscription Package"; + SubPackageLineTemplate: Record "Sub. Package Line Template"; + SubscriptionHeader: Record "Subscription Header"; ServiceAndCalculationStartDate: Date; begin + // [SCENARIO] UpdateTermUntilUsingExtensionTerm and UpdateCancellationPossibleUntil correctly calculate termination dates + // [SCENARIO] The loop continues updating Term Until and Cancellation Possible Until until current date is reached Initialize(); + // [GIVEN] A Subscription Line created 5 years ago with Initial Term of 12M, Extension Term of 12M, and Notice Period of 1M ServiceAndCalculationStartDate := CalcDate('<-5Y>', WorkDate()); - ContractTestLibrary.CreateServiceObjectForItem(ServiceObject, Item, false); - ContractTestLibrary.CreateServiceCommitmentTemplate(ServiceCommitmentTemplate); - Evaluate(ServiceCommitmentTemplate."Billing Base Period", '<12M>'); - ServiceCommitmentTemplate.Modify(false); - ContractTestLibrary.CreateServiceCommitmentPackageWithLine(ServiceCommitmentTemplate.Code, ServiceCommitmentPackage, ServiceCommPackageLine); - ContractTestLibrary.AssignItemToServiceCommitmentPackage(Item, ServiceCommitmentPackage.Code); - - Evaluate(ServiceCommPackageLine."Initial Term", '<12M>'); - Evaluate(ServiceCommPackageLine."Extension Term", '<12M>'); - Evaluate(ServiceCommPackageLine."Notice Period", '<1M>'); - Evaluate(ServiceCommPackageLine."Billing Rhythm", '<1M>'); - ServiceCommPackageLine.Modify(false); - - ServiceCommitmentPackage.SetFilter(Code, ItemServCommitmentPackage.GetPackageFilterForItem(ServiceObject."Source No.")); - ServiceObject.InsertServiceCommitmentsFromServCommPackage(ServiceAndCalculationStartDate, ServiceCommitmentPackage); - - FindServiceCommitment(ServiceCommitment, ServiceObject."No."); + ContractTestLibrary.CreateServiceObjectForItem(SubscriptionHeader, Item, false); + ContractTestLibrary.CreateServiceCommitmentTemplate(SubPackageLineTemplate); + Evaluate(SubPackageLineTemplate."Billing Base Period", '<12M>'); + SubPackageLineTemplate.Modify(false); + ContractTestLibrary.CreateServiceCommitmentPackageWithLine(SubPackageLineTemplate.Code, SubscriptionPackage, SubscriptionPackageLine); + ContractTestLibrary.AssignItemToServiceCommitmentPackage(Item, SubscriptionPackage.Code); + + Evaluate(SubscriptionPackageLine."Initial Term", '<12M>'); + Evaluate(SubscriptionPackageLine."Extension Term", '<12M>'); + Evaluate(SubscriptionPackageLine."Notice Period", '<1M>'); + Evaluate(SubscriptionPackageLine."Billing Rhythm", '<1M>'); + SubscriptionPackageLine.Modify(false); + + SubscriptionPackage.SetFilter(Code, ItemSubscriptionPackage.GetPackageFilterForItem(SubscriptionHeader."Source No.")); + SubscriptionHeader.InsertServiceCommitmentsFromServCommPackage(ServiceAndCalculationStartDate, SubscriptionPackage); + + FindServiceCommitment(SubscriptionLine, SubscriptionHeader."No."); + + // [WHEN] Repeatedly applying UpdateTermUntilUsingExtensionTerm and UpdateCancellationPossibleUntil + // [WHEN] Until the Cancellation Possible Until date reaches or exceeds WorkDate repeat - ServiceCommitment2 := ServiceCommitment; - ServiceCommitment.UpdateTermUntilUsingExtensionTerm(); - ServiceCommitment.UpdateCancellationPossibleUntil(); - ServiceCommitment.Modify(false); - TestServiceCommitmentUpdatedTerminationDates(ServiceCommitment2, ServiceCommitment, ServiceCommitment); - until WorkDate() <= ServiceCommitment."Cancellation Possible Until"; + OldSubscriptionLine := SubscriptionLine; + SubscriptionLine.UpdateTermUntilUsingExtensionTerm(); + SubscriptionLine.UpdateCancellationPossibleUntil(); + SubscriptionLine.Modify(false); + + // [THEN] Termination dates are calculated correctly based on Extension Term and Notice Period + TestSubscriptionLineUpdatedTerminationDates(OldSubscriptionLine, SubscriptionLine); + until WorkDate() <= SubscriptionLine."Cancellation Possible Until"; end; [Test] @@ -1694,13 +1714,6 @@ codeunit 148157 "Service Object Test" DateTimeManagement.MoveDateToLastDayOfMonth(CancellationPossibleUntil); end; - local procedure GetUpdatedTermUntilDate(CalculationStartDate: Date; SourceServiceCommitment: Record "Subscription Line") TermUntil: Date - begin - if (Format(SourceServiceCommitment."Extension Term") = '') or (CalculationStartDate = 0D) then - exit(0D); - TermUntil := CalcDate(SourceServiceCommitment."Extension Term", CalculationStartDate); - end; - local procedure MockServiceObjectWithEndUserCustomerNo(var ServiceObject: Record "Subscription Header"; CustomerNo: Code[20]) begin ServiceObject.Init(); @@ -1755,14 +1768,19 @@ codeunit 148157 "Service Object Test" Assert.AreEqual(ExpectedDate, SourceServiceCommitment."Term Until", '"Term Until" Date is not calculated correctly.'); end; - local procedure TestServiceCommitmentUpdatedTerminationDates(ServiceCommitment2: Record "Subscription Line"; SourceServiceCommitment: Record "Subscription Line"; ServiceCommitment: Record "Subscription Line") + local procedure TestSubscriptionLineUpdatedTerminationDates(OldSubscriptionLine: Record "Subscription Line"; UpdatedSubscriptionLine: Record "Subscription Line") var - ExpectedDate: Date; - begin - ExpectedDate := GetUpdatedTermUntilDate(ServiceCommitment2."Term Until", SourceServiceCommitment); - Assert.AreEqual(ExpectedDate, SourceServiceCommitment."Term Until", '"Term Until" Date is not calculated correctly.'); - ExpectedDate := GetUpdatedCancellationPossibleUntilDate(SourceServiceCommitment."Term Until", ServiceCommitment); - Assert.AreEqual(ExpectedDate, SourceServiceCommitment."Cancellation Possible Until", '"Cancellation Possible Until" Date is not calculated correctly.'); + ExpectedTermUntilDate: Date; + ExpectedCancellationPossibleUntilDate: Date; + begin + if OldSubscriptionLine."Term Until" = 0D then begin // Term Until was not set because the initial term end date has already passed the workdate + OldSubscriptionLine.CalculateInitialTermUntilDate(); + ExpectedTermUntilDate := OldSubscriptionLine."Term Until"; + end else + ExpectedTermUntilDate := CalcDate(OldSubscriptionLine."Extension Term", OldSubscriptionLine."Term Until"); + Assert.AreEqual(ExpectedTermUntilDate, UpdatedSubscriptionLine."Term Until", '"Term Until" Date is not calculated correctly.'); + ExpectedCancellationPossibleUntilDate := GetUpdatedCancellationPossibleUntilDate(UpdatedSubscriptionLine."Term Until", UpdatedSubscriptionLine); + Assert.AreEqual(ExpectedCancellationPossibleUntilDate, UpdatedSubscriptionLine."Cancellation Possible Until", '"Cancellation Possible Until" Date is not calculated correctly.'); end; local procedure ValidateServiceDateCombination(StartDate: Date; EndDate: Date; NextCalcDate: Date; ServiceObjectNo: Code[20]) diff --git a/src/Apps/W1/Subscription Billing/Test/UBB/UsageBasedRebillingTest.Codeunit.al b/src/Apps/W1/Subscription Billing/Test/UBB/UsageBasedRebillingTest.Codeunit.al index 91cdac53d3..0aecfac540 100644 --- a/src/Apps/W1/Subscription Billing/Test/UBB/UsageBasedRebillingTest.Codeunit.al +++ b/src/Apps/W1/Subscription Billing/Test/UBB/UsageBasedRebillingTest.Codeunit.al @@ -472,6 +472,7 @@ codeunit 139694 "Usage Based Rebilling Test" ServiceCommitmentGlobal."Usage Based Billing" := true; ServiceCommitmentGlobal."Usage Based Pricing" := "Usage Based Pricing"::"Usage Quantity"; ServiceCommitmentGlobal.Validate("Subscription Line Start Date", ServiceAndCalculationStartDate); + ServiceCommitmentGlobal.Validate("Subscription Line End Date", 0D); UsageDataSupplierReference.FilterUsageDataSupplierReference(UsageDataSupplier."No.", GetSubscriptionId(), Enum::"Usage Data Reference Type"::Subscription); if UsageDataSupplierReference.FindFirst() then From 187f0a302cfc3274a20d3a909af187ed9d77f445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miljan=20Milosavljevi=C4=87?= Date: Fri, 30 Jan 2026 16:24:34 +0100 Subject: [PATCH 2/2] Calculating Subscription Dates when Start Date is entered --- .../Codeunits/SalesDocuments.Codeunit.al | 3 +- .../PostSubContractRenewal.Codeunit.al | 5 +- .../CreateSubscriptionLine.Codeunit.al | 5 +- .../Tables/SubscriptionLine.Table.al | 7 ++ .../Tables/SubscriptionHeader.Table.al | 10 +- .../ServiceObjectTest.Codeunit.al | 92 ++++++++++++++----- 6 files changed, 88 insertions(+), 34 deletions(-) diff --git a/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/SalesDocuments.Codeunit.al b/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/SalesDocuments.Codeunit.al index 14be5477d6..083da9941b 100644 --- a/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/SalesDocuments.Codeunit.al +++ b/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/SalesDocuments.Codeunit.al @@ -542,8 +542,7 @@ codeunit 8063 "Sales Documents" SubscriptionLine.Validate("Calculation Base Amount", SubscriptionLine."Calculation Base Amount" * -1); SubscriptionLine.UpdateNextPriceUpdate(); - SubscriptionLine.CalculateInitialServiceEndDate(); - SubscriptionLine.CalculateInitialTermUntilDate(); + SubscriptionLine.CalculateInitialSubscriptionDates(); SubscriptionLine.SetCurrencyData(SalesHeader."Currency Factor", SalesHeader."Posting Date", SalesHeader."Currency Code"); SubscriptionLine.SetLCYFields(true); if SalesLine."No." = SubscriptionLine."Invoicing Item No." then begin diff --git a/src/Apps/W1/Subscription Billing/App/Contract Renewal/Codeunits/PostSubContractRenewal.Codeunit.al b/src/Apps/W1/Subscription Billing/App/Contract Renewal/Codeunits/PostSubContractRenewal.Codeunit.al index 7ad1dc2967..d56151a74a 100644 --- a/src/Apps/W1/Subscription Billing/App/Contract Renewal/Codeunits/PostSubContractRenewal.Codeunit.al +++ b/src/Apps/W1/Subscription Billing/App/Contract Renewal/Codeunits/PostSubContractRenewal.Codeunit.al @@ -107,8 +107,7 @@ codeunit 8004 "Post Sub. Contract Renewal" TempServiceCommitment."Subscription Line End Date" := CalcDate('<-1D>', TempServiceCommitment."Subscription Line End Date"); end; TempServiceCommitment.CopyFromSalesServiceCommitment(SalesServiceCommitment); - TempServiceCommitment.CalculateInitialServiceEndDate(); - TempServiceCommitment.CalculateInitialTermUntilDate(); + TempServiceCommitment.CalculateInitialSubscriptionDates(); TempServiceCommitment.SetCurrencyData(SalesHeader."Currency Factor", SalesHeader."Posting Date", SalesHeader."Currency Code"); TempServiceCommitment.SetLCYFields(true); TempServiceCommitment.SetDefaultDimensions(true); @@ -273,7 +272,7 @@ codeunit 8004 "Post Sub. Contract Renewal" end; /// - /// Updates the existing subscription line from planned subscription line. It is used for subscription renewal. + /// Updates the existing subscription line from planned subscription line. It is used for subscription renewal. /// /// Record "Planned Subscription Line". procedure ProcessPlannedServiceCommitment(PlannedServiceCommitment: Record "Planned Subscription Line") diff --git a/src/Apps/W1/Subscription Billing/App/Import/Codeunits/CreateSubscriptionLine.Codeunit.al b/src/Apps/W1/Subscription Billing/App/Import/Codeunits/CreateSubscriptionLine.Codeunit.al index 69fcb27457..25f5a13ea1 100644 --- a/src/Apps/W1/Subscription Billing/App/Import/Codeunits/CreateSubscriptionLine.Codeunit.al +++ b/src/Apps/W1/Subscription Billing/App/Import/Codeunits/CreateSubscriptionLine.Codeunit.al @@ -119,8 +119,9 @@ codeunit 8006 "Create Subscription Line" ServiceCommitment."Calculation Base Amount (LCY)" := ImportedServiceCommitment."Calculation Base Amount (LCY)"; if ServiceCommitment."Subscription Line End Date" = 0D then - ServiceCommitment.CalculateInitialServiceEndDate(); - ServiceCommitment.CalculateInitialTermUntilDate(); + ServiceCommitment.CalculateInitialSubscriptionDates() + else + ServiceCommitment.CalculateInitialTermUntilDate(); ServiceCommitment.SetDefaultDimensions(true); ServiceCommitment."Renewal Term" := ServiceCommitment."Initial Term"; diff --git a/src/Apps/W1/Subscription Billing/App/Service Commitments/Tables/SubscriptionLine.Table.al b/src/Apps/W1/Subscription Billing/App/Service Commitments/Tables/SubscriptionLine.Table.al index 519d51c1ba..8f4d5987ba 100644 --- a/src/Apps/W1/Subscription Billing/App/Service Commitments/Tables/SubscriptionLine.Table.al +++ b/src/Apps/W1/Subscription Billing/App/Service Commitments/Tables/SubscriptionLine.Table.al @@ -58,6 +58,7 @@ table 8059 "Subscription Line" CheckServiceDates(); RecalculateHarmonizedBillingFieldsOnCustomerContract(); UpdateNextPriceUpdate(); + CalculateInitialSubscriptionDates(); end; } field(7; "Subscription Line End Date"; Date) @@ -739,6 +740,12 @@ table 8059 "Subscription Line" end; end; + internal procedure CalculateInitialSubscriptionDates() + begin + CalculateInitialServiceEndDate(); + CalculateInitialTermUntilDate(); + end; + internal procedure CalculateInitialServiceEndDate() begin if IsInitialTermEmpty() then diff --git a/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al b/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al index 9ba3215ce6..9581897e6c 100644 --- a/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al +++ b/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al @@ -1802,11 +1802,11 @@ table 8057 "Subscription Header" ServiceCommitment.Validate("Notice Period", ServiceCommPackageLine."Notice Period"); ServiceCommitment.Validate("Initial Term", ServiceCommPackageLine."Initial Term"); - if ServiceEndDate <> 0D then - ServiceCommitment."Subscription Line End Date" := ServiceEndDate - else - ServiceCommitment.CalculateInitialServiceEndDate(); - ServiceCommitment.CalculateInitialTermUntilDate(); + if ServiceEndDate <> 0D then begin + ServiceCommitment."Subscription Line End Date" := ServiceEndDate; + ServiceCommitment.CalculateInitialTermUntilDate(); + end else + ServiceCommitment.CalculateInitialSubscriptionDates(); ServiceCommitment.ClearTerminationPeriodsWhenServiceEnded(); ServiceCommitment.UpdateNextBillingDate(ServiceCommitment."Subscription Line Start Date" - 1); OnAfterDatesCalculatedOnInsertSubscriptionLinesFromSubscriptionPackage(ServiceCommitment, ServiceCommPackageLine); diff --git a/src/Apps/W1/Subscription Billing/Test/Service Objects/ServiceObjectTest.Codeunit.al b/src/Apps/W1/Subscription Billing/Test/Service Objects/ServiceObjectTest.Codeunit.al index c1d90ce6a1..c610259453 100644 --- a/src/Apps/W1/Subscription Billing/Test/Service Objects/ServiceObjectTest.Codeunit.al +++ b/src/Apps/W1/Subscription Billing/Test/Service Objects/ServiceObjectTest.Codeunit.al @@ -695,13 +695,8 @@ codeunit 148157 "Service Object Test" [Test] procedure CheckSubscriptionLineUpdateTerminationDatesCalculation() var - Item: Record Item; - ItemSubscriptionPackage: Record "Item Subscription Package"; - SubscriptionPackageLine: Record "Subscription Package Line"; SubscriptionLine: Record "Subscription Line"; OldSubscriptionLine: Record "Subscription Line"; - SubscriptionPackage: Record "Subscription Package"; - SubPackageLineTemplate: Record "Sub. Package Line Template"; SubscriptionHeader: Record "Subscription Header"; ServiceAndCalculationStartDate: Date; begin @@ -711,23 +706,7 @@ codeunit 148157 "Service Object Test" // [GIVEN] A Subscription Line created 5 years ago with Initial Term of 12M, Extension Term of 12M, and Notice Period of 1M ServiceAndCalculationStartDate := CalcDate('<-5Y>', WorkDate()); - ContractTestLibrary.CreateServiceObjectForItem(SubscriptionHeader, Item, false); - ContractTestLibrary.CreateServiceCommitmentTemplate(SubPackageLineTemplate); - Evaluate(SubPackageLineTemplate."Billing Base Period", '<12M>'); - SubPackageLineTemplate.Modify(false); - ContractTestLibrary.CreateServiceCommitmentPackageWithLine(SubPackageLineTemplate.Code, SubscriptionPackage, SubscriptionPackageLine); - ContractTestLibrary.AssignItemToServiceCommitmentPackage(Item, SubscriptionPackage.Code); - - Evaluate(SubscriptionPackageLine."Initial Term", '<12M>'); - Evaluate(SubscriptionPackageLine."Extension Term", '<12M>'); - Evaluate(SubscriptionPackageLine."Notice Period", '<1M>'); - Evaluate(SubscriptionPackageLine."Billing Rhythm", '<1M>'); - SubscriptionPackageLine.Modify(false); - - SubscriptionPackage.SetFilter(Code, ItemSubscriptionPackage.GetPackageFilterForItem(SubscriptionHeader."Source No.")); - SubscriptionHeader.InsertServiceCommitmentsFromServCommPackage(ServiceAndCalculationStartDate, SubscriptionPackage); - - FindServiceCommitment(SubscriptionLine, SubscriptionHeader."No."); + CreateSubscriptionLineWithTerms(SubscriptionHeader, SubscriptionLine, ServiceAndCalculationStartDate, '<12M>', '<12M>', '<1M>'); // [WHEN] Repeatedly applying UpdateTermUntilUsingExtensionTerm and UpdateCancellationPossibleUntil // [WHEN] Until the Cancellation Possible Until date reaches or exceeds WorkDate @@ -742,6 +721,32 @@ codeunit 148157 "Service Object Test" until WorkDate() <= SubscriptionLine."Cancellation Possible Until"; end; + [Test] + procedure CheckSubscriptionLineRecalculatesTerminationDatesOnStartDateChange() + var + SubscriptionLine: Record "Subscription Line"; + SubscriptionHeader: Record "Subscription Header"; + InitialStartDate: Date; + NewStartDate: Date; + begin + // [SCENARIO] When Subscription Line Start Date is changed, End Date and termination dates are automatically recalculated + Initialize(); + + // [GIVEN] A Subscription Line with Initial Term of 12M, Extension Term of 6M, and Notice Period of 1M + InitialStartDate := WorkDate(); + CreateSubscriptionLineWithTerms(SubscriptionHeader, SubscriptionLine, InitialStartDate, '<12M>', '<6M>', '<1M>'); + + // [THEN] Initial dates are calculated correctly + VerifySubscriptionLineDates(SubscriptionLine, InitialStartDate, '<12M>', '<1M>'); + + // [WHEN] The Subscription Line Start Date is changed to a new date + NewStartDate := CalcDate('<+1M>', InitialStartDate); + SubscriptionLine.Validate("Subscription Line Start Date", NewStartDate); + + // [THEN] All termination dates are recalculated based on new Start Date + VerifySubscriptionLineDates(SubscriptionLine, NewStartDate, '<12M>', '<1M>'); + end; + [Test] procedure CheckServiceObjectQtyRecalculation() var @@ -1806,6 +1811,49 @@ codeunit 148157 "Service Object Test" Assert.AreEqual(ExpectedPriceGroupServiceCommitment, SubscriptionLine."Customer Price Group", 'Unexpected Customer Price Group in Service Commitment'); end; + local procedure CreateSubscriptionLineWithTerms(var SubscriptionHeader: Record "Subscription Header"; var SubscriptionLine: Record "Subscription Line"; StartDate: Date; InitialTerm: Text; ExtensionTerm: Text; NoticePeriod: Text) + var + Item: Record Item; + ItemSubscriptionPackage: Record "Item Subscription Package"; + SubscriptionPackage: Record "Subscription Package"; + SubscriptionPackageLine: Record "Subscription Package Line"; + SubPackageLineTemplate: Record "Sub. Package Line Template"; + begin + ContractTestLibrary.CreateServiceObjectForItem(SubscriptionHeader, Item, false); + ContractTestLibrary.CreateServiceCommitmentTemplate(SubPackageLineTemplate); + Evaluate(SubPackageLineTemplate."Billing Base Period", '<12M>'); + SubPackageLineTemplate.Modify(false); + ContractTestLibrary.CreateServiceCommitmentPackageWithLine(SubPackageLineTemplate.Code, SubscriptionPackage, SubscriptionPackageLine); + ContractTestLibrary.AssignItemToServiceCommitmentPackage(Item, SubscriptionPackage.Code); + + Evaluate(SubscriptionPackageLine."Initial Term", InitialTerm); + Evaluate(SubscriptionPackageLine."Extension Term", ExtensionTerm); + Evaluate(SubscriptionPackageLine."Notice Period", NoticePeriod); + Evaluate(SubscriptionPackageLine."Billing Rhythm", '<1M>'); + SubscriptionPackageLine.Modify(false); + + SubscriptionPackage.SetFilter(Code, ItemSubscriptionPackage.GetPackageFilterForItem(SubscriptionHeader."Source No.")); + SubscriptionHeader.InsertServiceCommitmentsFromServCommPackage(StartDate, SubscriptionPackage); + + FindServiceCommitment(SubscriptionLine, SubscriptionHeader."No."); + end; + + local procedure VerifySubscriptionLineDates(var SubscriptionLine: Record "Subscription Line"; StartDate: Date; InitialTerm: Text; NoticePeriod: Text) + var + ExpectedEndDate: Date; + ExpectedTermUntil: Date; + ExpectedCancellationPossibleUntil: Date; + begin + ExpectedEndDate := CalcDate(InitialTerm + '-1D', StartDate); + ExpectedTermUntil := ExpectedEndDate; + ExpectedCancellationPossibleUntil := CalcDate('-' + NoticePeriod, ExpectedTermUntil); + + SubscriptionLine.TestField("Subscription Line Start Date", StartDate); + SubscriptionLine.TestField("Subscription Line End Date", ExpectedEndDate); + SubscriptionLine.TestField("Term Until", ExpectedTermUntil); + SubscriptionLine.TestField("Cancellation Possible Until", ExpectedCancellationPossibleUntil); + end; + #endregion Procedures #region Handlers