diff --git a/src/Apps/W1/Subscription Billing/App/Deferrals/Codeunits/CustomerDeferralsMngmt.Codeunit.al b/src/Apps/W1/Subscription Billing/App/Deferrals/Codeunits/CustomerDeferralsMngmt.Codeunit.al index 06535d0d88f..13fe823a98c 100644 --- a/src/Apps/W1/Subscription Billing/App/Deferrals/Codeunits/CustomerDeferralsMngmt.Codeunit.al +++ b/src/Apps/W1/Subscription Billing/App/Deferrals/Codeunits/CustomerDeferralsMngmt.Codeunit.al @@ -34,6 +34,8 @@ codeunit 8067 "Customer Deferrals Mngmt." GeneralPostingSetup.TestField("Cust. Sub. Contr. Def Account"); SalesAccount := GeneralPostingSetup."Cust. Sub. Contr. Def Account"; end else begin + if SalesLine.Type = SalesLine.Type::"G/L Account" then + exit; // post directly to the G/L account selected on the contract line GeneralPostingSetup.TestField("Cust. Sub. Contract Account"); SalesAccount := GeneralPostingSetup."Cust. Sub. Contract Account"; end; diff --git a/src/Apps/W1/Subscription Billing/App/Deferrals/Codeunits/VendorDeferralsMngmt.Codeunit.al b/src/Apps/W1/Subscription Billing/App/Deferrals/Codeunits/VendorDeferralsMngmt.Codeunit.al index b006de302ee..c31c1674d1e 100644 --- a/src/Apps/W1/Subscription Billing/App/Deferrals/Codeunits/VendorDeferralsMngmt.Codeunit.al +++ b/src/Apps/W1/Subscription Billing/App/Deferrals/Codeunits/VendorDeferralsMngmt.Codeunit.al @@ -36,6 +36,8 @@ codeunit 8068 "Vendor Deferrals Mngmt." GeneralPostingSetup.TestField("Vend. Sub. Contr. Def. Account"); SalesAccount := GeneralPostingSetup."Vend. Sub. Contr. Def. Account"; end else begin + if PurchLine.Type = PurchLine.Type::"G/L Account" then + exit; // post directly to the G/L account selected on the contract line GeneralPostingSetup.TestField("Vend. Sub. Contract Account"); SalesAccount := GeneralPostingSetup."Vend. Sub. Contract Account"; end; diff --git a/src/Apps/W1/Subscription Billing/App/Deferrals/Pages/CustomerContractDeferrals.Page.al b/src/Apps/W1/Subscription Billing/App/Deferrals/Pages/CustomerContractDeferrals.Page.al index 73c640f242c..402f52f0991 100644 --- a/src/Apps/W1/Subscription Billing/App/Deferrals/Pages/CustomerContractDeferrals.Page.al +++ b/src/Apps/W1/Subscription Billing/App/Deferrals/Pages/CustomerContractDeferrals.Page.al @@ -101,6 +101,9 @@ page 8079 "Customer Contract Deferrals" field("Gen. Prod. Posting Group"; Rec."Gen. Prod. Posting Group") { } + field("G/L Account No."; Rec."G/L Account No.") + { + } field("G/L Entry No."; Rec."G/L Entry No.") { ToolTip = 'Specifies the number of the G/L item with which the deferral was released.'; diff --git a/src/Apps/W1/Subscription Billing/App/Deferrals/Pages/VendorContractDeferrals.Page.al b/src/Apps/W1/Subscription Billing/App/Deferrals/Pages/VendorContractDeferrals.Page.al index 1eeb308695e..a5f929d15f6 100644 --- a/src/Apps/W1/Subscription Billing/App/Deferrals/Pages/VendorContractDeferrals.Page.al +++ b/src/Apps/W1/Subscription Billing/App/Deferrals/Pages/VendorContractDeferrals.Page.al @@ -101,6 +101,9 @@ page 8081 "Vendor Contract Deferrals" field("Gen. Prod. Posting Group"; Rec."Gen. Prod. Posting Group") { } + field("G/L Account No."; Rec."G/L Account No.") + { + } field("G/L Entry No."; Rec."G/L Entry No.") { ToolTip = 'Specifies the number of the G/L item with which the deferral was released.'; diff --git a/src/Apps/W1/Subscription Billing/App/Deferrals/Reports/ContractDeferralsRelease.Report.al b/src/Apps/W1/Subscription Billing/App/Deferrals/Reports/ContractDeferralsRelease.Report.al index 4e988842330..4a529eb75a8 100644 --- a/src/Apps/W1/Subscription Billing/App/Deferrals/Reports/ContractDeferralsRelease.Report.al +++ b/src/Apps/W1/Subscription Billing/App/Deferrals/Reports/ContractDeferralsRelease.Report.al @@ -151,7 +151,7 @@ report 8051 "Contract Deferrals Release" end; if (CustomerContractDeferral.Amount <> 0) or (CustomerContractDeferral."Discount Amount" <> 0) then - CheckGenPostingSetup(CustomerContractDeferral."Gen. Bus. Posting Group", CustomerContractDeferral."Gen. Prod. Posting Group", Enum::"Service Partner"::Customer); + CheckGenPostingSetup(CustomerContractDeferral."Gen. Bus. Posting Group", CustomerContractDeferral."Gen. Prod. Posting Group", Enum::"Service Partner"::Customer, CustomerContractDeferral."G/L Account No." <> ''); CustomerContractDeferral.Released := true; CustomerContractDeferral."Release Posting Date" := PostingDate; @@ -164,7 +164,7 @@ report 8051 "Contract Deferrals Release" CustomerContractDeferral."Subscription Contract No.", CustomerContractDeferral."Entry No.", CustomerContractDeferral."Dimension Set ID", - GenPostingSetup."Cust. Sub. Contract Account", + GetContractAccount(CustomerContractDeferral."G/L Account No.", GenPostingSetup."Cust. Sub. Contract Account"), GenPostingSetup."Cust. Sub. Contr. Def Account", CustomerContractDeferral."Gen. Bus. Posting Group", CustomerContractDeferral."Gen. Prod. Posting Group", @@ -206,7 +206,7 @@ report 8051 "Contract Deferrals Release" end; if (VendorContractDeferral.Amount <> 0) or (VendorContractDeferral."Discount Amount" <> 0) then - CheckGenPostingSetup(VendorContractDeferral."Gen. Bus. Posting Group", VendorContractDeferral."Gen. Prod. Posting Group", Enum::"Service Partner"::Vendor); + CheckGenPostingSetup(VendorContractDeferral."Gen. Bus. Posting Group", VendorContractDeferral."Gen. Prod. Posting Group", Enum::"Service Partner"::Vendor, VendorContractDeferral."G/L Account No." <> ''); VendorContractDeferral.Released := true; VendorContractDeferral."Release Posting Date" := PostingDate; @@ -219,7 +219,7 @@ report 8051 "Contract Deferrals Release" VendorContractDeferral."Subscription Contract No.", VendorContractDeferral."Entry No.", VendorContractDeferral."Dimension Set ID", - GenPostingSetup."Vend. Sub. Contract Account", + GetContractAccount(VendorContractDeferral."G/L Account No.", GenPostingSetup."Vend. Sub. Contract Account"), GenPostingSetup."Vend. Sub. Contr. Def. Account", VendorContractDeferral."Gen. Bus. Posting Group", VendorContractDeferral."Gen. Prod. Posting Group", @@ -256,25 +256,34 @@ report 8051 "Contract Deferrals Release" end; end; - local procedure CheckGenPostingSetup(GenBusPostingGroup: Code[20]; GenProdPostingGroup: Code[20]; Partner: Enum "Service Partner") + local procedure CheckGenPostingSetup(GenBusPostingGroup: Code[20]; GenProdPostingGroup: Code[20]; Partner: Enum "Service Partner"; ContractAccountTakenFromDeferral: Boolean) begin GenPostingSetup.Get(GenBusPostingGroup, GenProdPostingGroup); case Partner of Enum::"Service Partner"::Customer: begin - GenPostingSetup.TestField("Cust. Sub. Contract Account"); + if not ContractAccountTakenFromDeferral then + GenPostingSetup.TestField("Cust. Sub. Contract Account"); if LineDiscountPosting then GenPostingSetup.TestField("Sales Line Disc. Account"); end; Enum::"Service Partner"::Vendor: begin - GenPostingSetup.TestField("Vend. Sub. Contract Account"); + if not ContractAccountTakenFromDeferral then + GenPostingSetup.TestField("Vend. Sub. Contract Account"); if LineDiscountPosting then GenPostingSetup.TestField("Purch. Line Disc. Account"); end; end; end; + local procedure GetContractAccount(DeferralGLAccountNo: Code[20]; GenPostingSetupContractAccount: Code[20]): Code[20] + begin + if DeferralGLAccountNo <> '' then + exit(DeferralGLAccountNo); + exit(GenPostingSetupContractAccount); + end; + local procedure GetPostingAmount(Amount: Decimal; DiscountAmount: Decimal): Decimal begin if LineDiscountPosting then diff --git a/src/Apps/W1/Subscription Billing/App/Deferrals/Tables/CustSubContractDeferral.Table.al b/src/Apps/W1/Subscription Billing/App/Deferrals/Tables/CustSubContractDeferral.Table.al index 613788f4a74..628be5a2174 100644 --- a/src/Apps/W1/Subscription Billing/App/Deferrals/Tables/CustSubContractDeferral.Table.al +++ b/src/Apps/W1/Subscription Billing/App/Deferrals/Tables/CustSubContractDeferral.Table.al @@ -1,6 +1,7 @@ namespace Microsoft.SubscriptionBilling; using Microsoft.Finance.Dimension; +using Microsoft.Finance.GeneralLedger.Account; using Microsoft.Finance.GeneralLedger.Ledger; using Microsoft.Finance.GeneralLedger.Setup; using Microsoft.Sales.Customer; @@ -135,6 +136,14 @@ table 8066 "Cust. Sub. Contract Deferral" { Caption = 'Currency Code'; } + field(27; "G/L Account No."; Code[20]) + { + Caption = 'G/L Account No.'; + ToolTip = 'Specifies the G/L account selected on the Subscription Contract line. If it is filled, releasing the deferral posts to this account instead of the Customer Subscription Contract Account from the General Posting Setup.'; + DataClassification = CustomerContent; + Editable = false; + TableRelation = "G/L Account"; + } field(74; "Gen. Bus. Posting Group"; Code[20]) { Caption = 'Gen. Bus. Posting Group'; @@ -195,6 +204,8 @@ table 8066 "Cust. Sub. Contract Deferral" Rec."Currency Code" := SalesLine."Currency Code"; Rec."Gen. Bus. Posting Group" := SalesLine."Gen. Bus. Posting Group"; Rec."Gen. Prod. Posting Group" := SalesLine."Gen. Prod. Posting Group"; + if SalesLine.Type = SalesLine.Type::"G/L Account" then + Rec."G/L Account No." := SalesLine."No."; OnAfterInitFromSalesLine(Rec, SalesLine, Sign); end; diff --git a/src/Apps/W1/Subscription Billing/App/Deferrals/Tables/VendSubContractDeferral.Table.al b/src/Apps/W1/Subscription Billing/App/Deferrals/Tables/VendSubContractDeferral.Table.al index cc2f4454bab..3f7f46ba718 100644 --- a/src/Apps/W1/Subscription Billing/App/Deferrals/Tables/VendSubContractDeferral.Table.al +++ b/src/Apps/W1/Subscription Billing/App/Deferrals/Tables/VendSubContractDeferral.Table.al @@ -1,6 +1,7 @@ namespace Microsoft.SubscriptionBilling; using Microsoft.Finance.Dimension; +using Microsoft.Finance.GeneralLedger.Account; using Microsoft.Finance.GeneralLedger.Ledger; using Microsoft.Finance.GeneralLedger.Setup; using Microsoft.Purchases.Document; @@ -136,6 +137,14 @@ table 8072 "Vend. Sub. Contract Deferral" { Caption = 'Currency Code'; } + field(27; "G/L Account No."; Code[20]) + { + Caption = 'G/L Account No.'; + ToolTip = 'Specifies the G/L account selected on the Subscription Contract line. If it is filled, releasing the deferral posts to this account instead of the Vendor Subscription Contract Account from the General Posting Setup.'; + DataClassification = CustomerContent; + Editable = false; + TableRelation = "G/L Account"; + } field(74; "Gen. Bus. Posting Group"; Code[20]) { Caption = 'Gen. Bus. Posting Group'; @@ -198,6 +207,8 @@ table 8072 "Vend. Sub. Contract Deferral" Rec."Currency Code" := PurchaseLine."Currency Code"; Rec."Gen. Bus. Posting Group" := PurchaseLine."Gen. Bus. Posting Group"; Rec."Gen. Prod. Posting Group" := PurchaseLine."Gen. Prod. Posting Group"; + if PurchaseLine.Type = PurchaseLine.Type::"G/L Account" then + Rec."G/L Account No." := PurchaseLine."No."; OnAfterInitFromPurchaseLine(Rec, PurchaseLine, Sign); end; diff --git a/src/Apps/W1/Subscription Billing/Test/Base/ContractTestLibrary.Codeunit.al b/src/Apps/W1/Subscription Billing/Test/Base/ContractTestLibrary.Codeunit.al index c74c72dffec..db7c16b90a2 100644 --- a/src/Apps/W1/Subscription Billing/Test/Base/ContractTestLibrary.Codeunit.al +++ b/src/Apps/W1/Subscription Billing/Test/Base/ContractTestLibrary.Codeunit.al @@ -732,35 +732,41 @@ codeunit 139685 "Contract Test Library" procedure CreateServiceObjectForGLAccountWithServiceCommitments(var ServiceObject: Record "Subscription Header"; var GLAccount: Record "G/L Account"; NoOfCustomerServCommLinesToCreate: Integer; NoOfVendorServCommLinesToCreate: Integer; BillingBasePeriodText: Text; BillingRhythmText: Text) + begin + CreateServiceObjectForGLAccountWithServiceCommitments(ServiceObject, GLAccount, NoOfCustomerServCommLinesToCreate, NoOfVendorServCommLinesToCreate, BillingBasePeriodText, BillingRhythmText, WorkDate(), 0); + end; + + procedure CreateServiceObjectForGLAccountWithServiceCommitments(var ServiceObject: Record "Subscription Header"; var GLAccount: Record "G/L Account"; + NoOfCustomerServCommLinesToCreate: Integer; NoOfVendorServCommLinesToCreate: Integer; BillingBasePeriodText: Text; BillingRhythmText: Text; + SubscriptionLineStartDate: Date; CalculationBaseAmount: Decimal) var - ServiceCommitment: Record "Subscription Line"; i: Integer; begin CreateServiceObjectForGLAccount(ServiceObject, GLAccount); - for i := 1 to NoOfCustomerServCommLinesToCreate do begin - ServiceCommitment.Init(); - ServiceCommitment."Subscription Header No." := ServiceObject."No."; - ServiceCommitment."Entry No." := 0; - ServiceCommitment.Description := ServiceObject.Description; - ServiceCommitment."Invoicing via" := ServiceCommitment."Invoicing via"::Contract; - ServiceCommitment.Partner := ServiceCommitment.Partner::Customer; - ServiceCommitment.Validate("Subscription Line Start Date", WorkDate()); - Evaluate(ServiceCommitment."Billing Base Period", BillingBasePeriodText); - Evaluate(ServiceCommitment."Billing Rhythm", BillingRhythmText); - ServiceCommitment.Insert(false); - end; - for i := 1 to NoOfVendorServCommLinesToCreate do begin - ServiceCommitment.Init(); - ServiceCommitment."Subscription Header No." := ServiceObject."No."; - ServiceCommitment."Entry No." := 0; - ServiceCommitment.Description := ServiceObject.Description; - ServiceCommitment."Invoicing via" := ServiceCommitment."Invoicing via"::Contract; - ServiceCommitment.Partner := ServiceCommitment.Partner::Vendor; - ServiceCommitment.Validate("Subscription Line Start Date", WorkDate()); - Evaluate(ServiceCommitment."Billing Base Period", BillingBasePeriodText); - Evaluate(ServiceCommitment."Billing Rhythm", BillingRhythmText); - ServiceCommitment.Insert(false); + for i := 1 to NoOfCustomerServCommLinesToCreate do + InsertSubscriptionLineForServiceObject(ServiceObject, Enum::"Service Partner"::Customer, BillingBasePeriodText, BillingRhythmText, SubscriptionLineStartDate, CalculationBaseAmount); + for i := 1 to NoOfVendorServCommLinesToCreate do + InsertSubscriptionLineForServiceObject(ServiceObject, Enum::"Service Partner"::Vendor, BillingBasePeriodText, BillingRhythmText, SubscriptionLineStartDate, CalculationBaseAmount); + end; + + local procedure InsertSubscriptionLineForServiceObject(ServiceObject: Record "Subscription Header"; ServicePartner: Enum "Service Partner"; BillingBasePeriodText: Text; BillingRhythmText: Text; SubscriptionLineStartDate: Date; CalculationBaseAmount: Decimal) + var + ServiceCommitment: Record "Subscription Line"; + begin + ServiceCommitment.Init(); + ServiceCommitment."Subscription Header No." := ServiceObject."No."; + ServiceCommitment."Entry No." := 0; + ServiceCommitment.Description := ServiceObject.Description; + ServiceCommitment."Invoicing via" := ServiceCommitment."Invoicing via"::Contract; + ServiceCommitment.Partner := ServicePartner; + ServiceCommitment.Validate("Subscription Line Start Date", SubscriptionLineStartDate); + Evaluate(ServiceCommitment."Billing Base Period", BillingBasePeriodText); + Evaluate(ServiceCommitment."Billing Rhythm", BillingRhythmText); + if CalculationBaseAmount <> 0 then begin + ServiceCommitment.Validate("Calculation Base %", 100); + ServiceCommitment.Validate("Calculation Base Amount", CalculationBaseAmount); end; + ServiceCommitment.Insert(false); end; procedure CreateServiceObjectForItem(var ServiceObject: Record "Subscription Header"; ItemNo: Code[20]) diff --git a/src/Apps/W1/Subscription Billing/Test/Deferrals/CustomerDeferralsTest.Codeunit.al b/src/Apps/W1/Subscription Billing/Test/Deferrals/CustomerDeferralsTest.Codeunit.al index 1ec5b89884f..6ccb53f0e59 100644 --- a/src/Apps/W1/Subscription Billing/Test/Deferrals/CustomerDeferralsTest.Codeunit.al +++ b/src/Apps/W1/Subscription Billing/Test/Deferrals/CustomerDeferralsTest.Codeunit.al @@ -2,6 +2,7 @@ namespace Microsoft.SubscriptionBilling; using Microsoft.Finance.Currency; using Microsoft.Finance.Deferral; +using Microsoft.Finance.GeneralLedger.Account; using Microsoft.Finance.GeneralLedger.Ledger; using Microsoft.Finance.GeneralLedger.Setup; using Microsoft.Inventory.Item; @@ -28,6 +29,7 @@ codeunit 139912 "Customer Deferrals Test" CustomerContractDeferral: Record "Cust. Sub. Contract Deferral"; SalesCrMemoDeferral: Record "Cust. Sub. Contract Deferral"; SalesInvoiceDeferral: Record "Cust. Sub. Contract Deferral"; + GLAccount: Record "G/L Account"; GLSetup: Record "General Ledger Setup"; GeneralPostingSetup: Record "General Posting Setup"; Item: Record Item; @@ -77,6 +79,9 @@ codeunit 139912 "Customer Deferrals Test" NumberOfDaysRemainingMismatchErr: Label 'Number of Days should equal remaining days in month.'; NumberOfDaysScheduleMismatchErr: Label 'Number of Days should equal actual schedule days, not full month.'; NumberOfDaysDateRangeMismatchErr: Label 'Number of Days should equal actual date range days.'; + DeferralsMissingLineGLAccountErr: Label 'Every deferral entry must carry the G/L account from the contract line.'; + ReleasedAmountNotPostedToLineGLAccountErr: Label 'Releasing the contract deferrals must post to the G/L account selected on the contract line.'; + RevenueNotPostedToLineGLAccountErr: Label 'The revenue must be posted directly to the G/L account selected on the contract line when no contract deferrals are created.'; #region Tests [Test] @@ -1145,6 +1150,96 @@ codeunit 139912 "Customer Deferrals Test" Assert.AreEqual(ExpectedLastMonthDays, CustomerContractDeferral."Number of Days", LastPartialMonthDaysMismatchErr); end; + [Test] + [HandlerFunctions('CreateCustomerBillingDocsContractPageHandler,MessageHandler')] + procedure GLAccountContractLinePostsToLineGLAccountWithoutDeferrals() + var + GLEntry: Record "G/L Entry"; + ExpectedAmount: Decimal; + begin + // [SCENARIO] When a contract line of type G/L Account is invoiced without contract deferrals, + // the revenue is posted directly to the G/L account selected on the contract line instead of + // the Customer Subscription Contract Account from the General Posting Setup. + Initialize(); + + // [GIVEN] A customer contract with a G/L Account contract line and contract deferrals disabled + CreateCustomerContractWithGLAccountLine('<2M-CM>'); + ContractTestLibrary.DisableDeferralsForCustomerContract(CustomerContract, false); + CreateBillingProposalAndCreateBillingDocuments('<2M-CM>', '<8M+CM>'); + + // [GIVEN] The Customer Subscription Contract Account is not set in the General Posting Setup + GeneralPostingSetup.Get(Customer."Gen. Bus. Posting Group", GLAccount."Gen. Prod. Posting Group"); + GeneralPostingSetup."Cust. Sub. Contract Account" := ''; + GeneralPostingSetup.Modify(false); + + // [WHEN] The contract invoice is posted + SalesLine.SetRange("Document No.", SalesHeader."No."); + SalesLine.SetRange(Type, SalesLine.Type::"G/L Account"); + SalesLine.CalcSums(Amount); + ExpectedAmount := SalesLine.Amount; + PostedDocumentNo := LibrarySales.PostSalesDocument(SalesHeader, true, true); + + // [THEN] No deferrals are created and the revenue is posted to the G/L account from the contract line + CustomerContractDeferral.SetRange("Document No.", PostedDocumentNo); + Assert.RecordIsEmpty(CustomerContractDeferral); + GLEntry.SetRange("Document No.", PostedDocumentNo); + GLEntry.SetRange("G/L Account No.", GLAccount."No."); + GLEntry.CalcSums(Amount); + Assert.AreEqual(-ExpectedAmount, GLEntry.Amount, RevenueNotPostedToLineGLAccountErr); + end; + + [Test] + [HandlerFunctions('CreateCustomerBillingDocsContractPageHandler,ContractDeferralsReleaseRequestPageHandler,MessageHandler')] + procedure GLAccountContractLineReleasesDeferralsToLineGLAccount() + var + GLEntry: Record "G/L Entry"; + ContractDeferralsRelease: Report "Contract Deferrals Release"; + TotalDeferralCount: Integer; + begin + // [SCENARIO] When a contract line of type G/L Account is invoiced with contract deferrals, + // the deferral entries carry the G/L account from the contract line and releasing them posts + // to that account instead of the Customer Subscription Contract Account from the General Posting Setup. + Initialize(); + SetPostingAllowTo(0D); + + // [GIVEN] A customer contract with a G/L Account contract line and contract deferrals enabled + CreateCustomerContractWithGLAccountLine('<2M-CM>'); + CreateBillingProposalAndCreateBillingDocuments('<2M-CM>', '<8M+CM>'); + + // [GIVEN] The Customer Subscription Contract Account is not set in the General Posting Setup + GeneralPostingSetup.Get(Customer."Gen. Bus. Posting Group", GLAccount."Gen. Prod. Posting Group"); + GeneralPostingSetup."Cust. Sub. Contract Account" := ''; + GeneralPostingSetup.Modify(false); + + // [WHEN] The contract invoice is posted + PostSalesDocumentAndFetchDeferrals(); + + // [THEN] Every deferral entry carries the G/L account from the contract line + TotalDeferralCount := CustomerContractDeferral.Count(); + CustomerContractDeferral.SetRange("G/L Account No.", GLAccount."No."); + Assert.RecordCount(CustomerContractDeferral, TotalDeferralCount); + Assert.AreNotEqual(0, TotalDeferralCount, DeferralsMissingLineGLAccountErr); + CustomerContractDeferral.FindFirst(); + + // [THEN] The invoice posting does not post to the G/L account from the contract line + GLEntry.SetRange("Document No.", PostedDocumentNo); + GLEntry.SetRange("G/L Account No.", GLAccount."No."); + Assert.RecordIsEmpty(GLEntry); + + // [WHEN] The contract deferrals are released for the first deferral posting date + PostingDate := CustomerContractDeferral."Posting Date"; + Commit(); // close transaction before report is called + ContractDeferralsRelease.Run(); // ContractDeferralsReleaseRequestPageHandler + + // [THEN] The released amount is posted to the G/L account from the contract line + CustomerContractDeferral.SetRange("G/L Account No."); + CustomerContractDeferral.SetRange(Released, true); + CustomerContractDeferral.CalcSums(Amount); + GLEntry.CalcSums(Amount); + Assert.AreEqual(CustomerContractDeferral.Amount, GLEntry.Amount, ReleasedAmountNotPostedToLineGLAccountErr); + Assert.AreNotEqual(0, GLEntry.Amount, ReleasedAmountNotPostedToLineGLAccountErr); + end; + #endregion Tests #region Procedures @@ -1232,6 +1327,28 @@ codeunit 139912 "Customer Deferrals Test" ContractTestLibrary.CreateCustomerContractAndCreateContractLinesForItems(CustomerContract, ServiceObject, Customer."No."); end; + local procedure CreateCustomerContractWithGLAccountLine(BillingDateFormula: Text) + var + TempServiceCommitment: Record "Subscription Line" temporary; + begin + ContractTestLibrary.CreateCustomerInLCY(Customer); + GLAccount.Get(LibraryERM.CreateGLAccountWithSalesSetup()); + ContractTestLibrary.CreateServiceObjectForGLAccountWithServiceCommitments(ServiceObject, GLAccount, 1, 0, '<1M>', '<1M>', CalcDate(BillingDateFormula, WorkDate()), 1200); + ServiceObject.SetHideValidationDialog(true); + ServiceObject.Validate("End-User Customer No.", Customer."No."); + ServiceObject.Modify(false); + + ContractTestLibrary.CreateCustomerContract(CustomerContract, Customer."No."); + ContractTestLibrary.FillTempServiceCommitment(TempServiceCommitment, ServiceObject, CustomerContract); + CustomerContract.CreateCustomerContractLinesFromServiceCommitments(TempServiceCommitment); + ContractTestLibrary.SetGeneralPostingSetup(Customer."Gen. Bus. Posting Group", GLAccount."Gen. Prod. Posting Group", false, Enum::"Service Partner"::Customer); + GeneralPostingSetup.Get(Customer."Gen. Bus. Posting Group", GLAccount."Gen. Prod. Posting Group"); + if GeneralPostingSetup."Sales Line Disc. Account" = '' then begin + GeneralPostingSetup."Sales Line Disc. Account" := LibraryERM.CreateGLAccountNo(); + GeneralPostingSetup.Modify(false); + end; + end; + local procedure CreateSalesDocumentsFromCustomerContractWODeferrals() var SubscriptionLine: Record "Subscription Line"; diff --git a/src/Apps/W1/Subscription Billing/Test/Deferrals/VendorDeferralsTest.Codeunit.al b/src/Apps/W1/Subscription Billing/Test/Deferrals/VendorDeferralsTest.Codeunit.al index 6dea777684c..418dde854f8 100644 --- a/src/Apps/W1/Subscription Billing/Test/Deferrals/VendorDeferralsTest.Codeunit.al +++ b/src/Apps/W1/Subscription Billing/Test/Deferrals/VendorDeferralsTest.Codeunit.al @@ -26,6 +26,7 @@ codeunit 139913 "Vendor Deferrals Test" BillingLine: Record "Billing Line"; BillingTemplate: Record "Billing Template"; CurrExchRate: Record "Currency Exchange Rate"; + LineGLAccount: Record "G/L Account"; GLSetup: Record "General Ledger Setup"; GeneralPostingSetup: Record "General Posting Setup"; Item: Record Item; @@ -65,6 +66,9 @@ codeunit 139913 "Vendor Deferrals Test" IsInitialized: Boolean; ReleasedContractDeferralErr: Label 'Released Contract Deferrals were not reversed properly'; AmountNotMovedFromDeferralsAccountErr: Label 'Amount was not moved from Deferrals Account to Contract Account'; + CostNotPostedToLineGLAccountErr: Label 'The cost must be posted directly to the G/L account selected on the contract line when no contract deferrals are created.'; + DeferralsMissingLineGLAccountErr: Label 'Every deferral entry must carry the G/L account from the contract line.'; + ReleasedAmountNotPostedToLineGLAccountErr: Label 'Releasing the contract deferrals must post to the G/L account selected on the contract line.'; #region Tests @@ -1070,6 +1074,96 @@ codeunit 139913 "Vendor Deferrals Test" Assert.AreEqual(GLAmountBeforeRelease - VendorContractDeferral.Amount, GLAmountAfterRelease, AmountNotMovedFromDeferralsAccountErr); end; + [Test] + [HandlerFunctions('CreateVendorBillingDocsContractPageHandler,MessageHandler')] + procedure GLAccountContractLinePostsToLineGLAccountWithoutDeferrals() + var + GLEntry: Record "G/L Entry"; + ExpectedAmount: Decimal; + begin + // [SCENARIO] When a contract line of type G/L Account is invoiced without contract deferrals, + // the cost is posted directly to the G/L account selected on the contract line instead of + // the Vendor Subscription Contract Account from the General Posting Setup. + Initialize(); + + // [GIVEN] A vendor contract with a G/L Account contract line and contract deferrals disabled + CreateVendorContractWithGLAccountLine('<2M-CM>'); + ContractTestLibrary.DisableDeferralsForVendorContract(VendorContract, false); + CreateBillingProposalAndCreateBillingDocuments('<2M-CM>', '<8M+CM>'); + + // [GIVEN] The Vendor Subscription Contract Account is not set in the General Posting Setup + GeneralPostingSetup.Get(Vendor."Gen. Bus. Posting Group", LineGLAccount."Gen. Prod. Posting Group"); + GeneralPostingSetup."Vend. Sub. Contract Account" := ''; + GeneralPostingSetup.Modify(false); + + // [WHEN] The contract invoice is posted + PurchaseLine.SetRange("Document No.", PurchaseHeader."No."); + PurchaseLine.SetRange(Type, PurchaseLine.Type::"G/L Account"); + PurchaseLine.CalcSums(Amount); + ExpectedAmount := PurchaseLine.Amount; + PostedDocumentNo := LibraryPurchase.PostPurchaseDocument(PurchaseHeader, true, true); + + // [THEN] No deferrals are created and the cost is posted to the G/L account from the contract line + VendorContractDeferral.SetRange("Document No.", PostedDocumentNo); + Assert.RecordIsEmpty(VendorContractDeferral); + GLEntry.SetRange("Document No.", PostedDocumentNo); + GLEntry.SetRange("G/L Account No.", LineGLAccount."No."); + GLEntry.CalcSums(Amount); + Assert.AreEqual(ExpectedAmount, GLEntry.Amount, CostNotPostedToLineGLAccountErr); + end; + + [Test] + [HandlerFunctions('CreateVendorBillingDocsContractPageHandler,ContractDeferralsReleaseRequestPageHandler,MessageHandler')] + procedure GLAccountContractLineReleasesDeferralsToLineGLAccount() + var + GLEntry: Record "G/L Entry"; + ContractDeferralsRelease: Report "Contract Deferrals Release"; + TotalDeferralCount: Integer; + begin + // [SCENARIO] When a contract line of type G/L Account is invoiced with contract deferrals, + // the deferral entries carry the G/L account from the contract line and releasing them posts + // to that account instead of the Vendor Subscription Contract Account from the General Posting Setup. + Initialize(); + SetPostingAllowTo(0D); + + // [GIVEN] A vendor contract with a G/L Account contract line and contract deferrals enabled + CreateVendorContractWithGLAccountLine('<2M-CM>'); + CreateBillingProposalAndCreateBillingDocuments('<2M-CM>', '<8M+CM>'); + + // [GIVEN] The Vendor Subscription Contract Account is not set in the General Posting Setup + GeneralPostingSetup.Get(Vendor."Gen. Bus. Posting Group", LineGLAccount."Gen. Prod. Posting Group"); + GeneralPostingSetup."Vend. Sub. Contract Account" := ''; + GeneralPostingSetup.Modify(false); + + // [WHEN] The contract invoice is posted + PostPurchDocumentAndFetchDeferrals(); + + // [THEN] Every deferral entry carries the G/L account from the contract line + TotalDeferralCount := VendorContractDeferral.Count(); + VendorContractDeferral.SetRange("G/L Account No.", LineGLAccount."No."); + Assert.RecordCount(VendorContractDeferral, TotalDeferralCount); + Assert.AreNotEqual(0, TotalDeferralCount, DeferralsMissingLineGLAccountErr); + VendorContractDeferral.FindFirst(); + + // [THEN] The invoice posting does not post to the G/L account from the contract line + GLEntry.SetRange("Document No.", PostedDocumentNo); + GLEntry.SetRange("G/L Account No.", LineGLAccount."No."); + Assert.RecordIsEmpty(GLEntry); + + // [WHEN] The contract deferrals are released for the first deferral posting date + PostingDate := VendorContractDeferral."Posting Date"; + Commit(); // close transaction before report is called + ContractDeferralsRelease.Run(); // ContractDeferralsReleaseRequestPageHandler + + // [THEN] The released amount is posted to the G/L account from the contract line + VendorContractDeferral.SetRange("G/L Account No."); + VendorContractDeferral.SetRange(Released, true); + VendorContractDeferral.CalcSums(Amount); + GLEntry.CalcSums(Amount); + Assert.AreEqual(VendorContractDeferral.Amount, GLEntry.Amount, ReleasedAmountNotPostedToLineGLAccountErr); + Assert.AreNotEqual(0, GLEntry.Amount, ReleasedAmountNotPostedToLineGLAccountErr); + end; + #endregion Tests #region Procedures @@ -1207,6 +1301,25 @@ codeunit 139913 "Vendor Deferrals Test" ContractTestLibrary.CreateVendorContractAndCreateContractLinesForItems(VendorContract, ServiceObject, Vendor."No."); end; + local procedure CreateVendorContractWithGLAccountLine(BillingDateFormula: Text) + var + TempServiceCommitment: Record "Subscription Line" temporary; + begin + ContractTestLibrary.CreateVendorInLCY(Vendor); + LineGLAccount.Get(LibraryERM.CreateGLAccountWithPurchSetup()); + ContractTestLibrary.CreateServiceObjectForGLAccountWithServiceCommitments(ServiceObject, LineGLAccount, 0, 1, '<1M>', '<1M>', CalcDate(BillingDateFormula, WorkDate()), 1200); + + ContractTestLibrary.CreateVendorContract(VendorContract, Vendor."No."); + ContractTestLibrary.FillTempServiceCommitmentForVendor(TempServiceCommitment, ServiceObject, VendorContract); + ContractTestLibrary.CreateVendorContractLinesFromServiceCommitments(VendorContract, TempServiceCommitment); + ContractTestLibrary.SetGeneralPostingSetup(Vendor."Gen. Bus. Posting Group", LineGLAccount."Gen. Prod. Posting Group", false, Enum::"Service Partner"::Vendor); + GeneralPostingSetup.Get(Vendor."Gen. Bus. Posting Group", LineGLAccount."Gen. Prod. Posting Group"); + if GeneralPostingSetup."Purch. Line Disc. Account" = '' then begin + GeneralPostingSetup."Purch. Line Disc. Account" := LibraryERM.CreateGLAccountNo(); + GeneralPostingSetup.Modify(false); + end; + end; + local procedure FetchAndTestUpdatedVendorContractDeferral() var UpdatedVendorContractDeferral: Record "Vend. Sub. Contract Deferral";