diff --git a/.github/AL-Go-Settings.json b/.github/AL-Go-Settings.json index 6c30f81eff..ebfa1aa2b8 100644 --- a/.github/AL-Go-Settings.json +++ b/.github/AL-Go-Settings.json @@ -5,7 +5,7 @@ "runs-on": "windows-latest", "cacheImageName": "", "UsePsSession": false, - "artifact": "https://bcinsider-fvh2ekdjecfjd6gk.b02.azurefd.net/sandbox/26.0.27721.0/base", + "artifact": "https://bcinsider-fvh2ekdjecfjd6gk.b02.azurefd.net/sandbox/26.0.28000.0/base", "country": "base", "useProjectDependencies": true, "repoVersion": "26.0", diff --git a/Apps/CZ/AdvancedLocalizationPack/app/Src/Codeunits/GLEntryPostApplicationCZA.Codeunit.al b/Apps/CZ/AdvancedLocalizationPack/app/Src/Codeunits/GLEntryPostApplicationCZA.Codeunit.al index 2e65689606..34b9251268 100644 --- a/Apps/CZ/AdvancedLocalizationPack/app/Src/Codeunits/GLEntryPostApplicationCZA.Codeunit.al +++ b/Apps/CZ/AdvancedLocalizationPack/app/Src/Codeunits/GLEntryPostApplicationCZA.Codeunit.al @@ -311,6 +311,7 @@ codeunit 31370 "G/L Entry Post Application CZA" DtldGLEntryNo := 0; end; +#if not CLEAN26 [Obsolete('The local SetAmountToApply procedure is used instead.', '26.0')] procedure SetAmountToApply() var @@ -319,6 +320,7 @@ codeunit 31370 "G/L Entry Post Application CZA" begin SetAmountToApply(GLEntry, ApplyingAmount); end; +#endif local procedure SetAmountToApply(var GLEntry: Record "G/L Entry"; var ApplyingAmount: Decimal) begin diff --git a/Apps/CZ/BankingDocumentsLocalization/app/Src/Tables/PaymentOrderLineCZB.Table.al b/Apps/CZ/BankingDocumentsLocalization/app/Src/Tables/PaymentOrderLineCZB.Table.al index 1d3116ebc5..7a2b9f43e5 100644 --- a/Apps/CZ/BankingDocumentsLocalization/app/Src/Tables/PaymentOrderLineCZB.Table.al +++ b/Apps/CZ/BankingDocumentsLocalization/app/Src/Tables/PaymentOrderLineCZB.Table.al @@ -870,12 +870,13 @@ table 31257 "Payment Order Line CZB" PaymentOrderCurrency.Testfield("Amount Rounding Precision"); end; end; - +#if not CLEAN25 [Obsolete('Replaced by CreateDescription function with PlaceholderValues parameter.', '25.0')] procedure CreateDescription(DocType: Text[30]; DocNo: Text[20]; PartnerNo: Text[20]; PartnerName: Text[100]; ExtNo: Text[35]): Text[50] begin exit(CopyStr(StrSubstNo(BankAccount."Payment Order Line Descr. CZB", DocType, DocNo, PartnerNo, PartnerName, ExtNo), 1, 50)); end; +#endif procedure CreateDescription(PlaceholderValues: List of [Text[100]]) Description: Text[100] var diff --git a/Apps/CZ/CoreLocalizationPack/app/Src/Codeunits/ItemChargeAssgntHandlerCZL.Codeunit.al b/Apps/CZ/CoreLocalizationPack/app/Src/Codeunits/ItemChargeAssgntHandlerCZL.Codeunit.al new file mode 100644 index 0000000000..2ed495953e --- /dev/null +++ b/Apps/CZ/CoreLocalizationPack/app/Src/Codeunits/ItemChargeAssgntHandlerCZL.Codeunit.al @@ -0,0 +1,28 @@ +namespace Microsoft.Purchases.Document; + +using Microsoft.Purchases.Posting; + +codeunit 31180 "Item Charge Assgnt Handler CZL" +{ + Access = Internal; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Purch.-Post", 'OnPostItemChargeLineOnAfterPostItemCharge', '', false, false)] + local procedure ItemChargeAssgntWithAssignValuesOnPostItemChargeLineOnAfterPostItemCharge(sender: Codeunit "Purch.-Post"; var TempItemChargeAssgntPurch: Record "Item Charge Assignment (Purch)" temporary; PurchHeader: Record "Purchase Header"; PurchLine: Record "Purchase Line") + var + ItemChargeAssgntPurchCZL: Codeunit "Item Charge Assgnt. Purch. CZL"; + begin + if TempItemChargeAssgntPurch."Applies-to Doc. Type" = TempItemChargeAssgntPurch."Applies-to Doc. Type"::"Item Ledger Entry Positive Adjmt. CZL" then begin + ItemChargeAssgntPurchCZL.PostItemChargePerPosAdjItem(sender, TempItemChargeAssgntPurch, PurchHeader, PurchLine); + TempItemChargeAssgntPurch.Mark(true); + end; + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Charge Assgnt. (Purch.)", 'OnAssignByAmountOnAfterAssignAppliesToDocLineAmount', '', false, false)] + local procedure ItemChargeAssgntWithAssignValuesOnAssignByAmountOnAfterAssignAppliesToDocLineAmount(ItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)"; var TempItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)" temporary; PurchHeader: Record "Purchase Header") + var + ItemChargeAssgntPurchCZL: Codeunit "Item Charge Assgnt. Purch. CZL"; + begin + if ItemChargeAssignmentPurch."Applies-to Doc. Type" = ItemChargeAssignmentPurch."Applies-to Doc. Type"::"Item Ledger Entry Positive Adjmt. CZL" then + ItemChargeAssgntPurchCZL.AssignByAmountItemLedgerEntryPositiveAdjmt(ItemChargeAssignmentPurch, TempItemChargeAssignmentPurch, PurchHeader); + end; +} diff --git a/Apps/CZ/CoreLocalizationPack/app/Src/Codeunits/ItemChargeAssgntPurchCZL.Codeunit.al b/Apps/CZ/CoreLocalizationPack/app/Src/Codeunits/ItemChargeAssgntPurchCZL.Codeunit.al new file mode 100644 index 0000000000..692518e0aa --- /dev/null +++ b/Apps/CZ/CoreLocalizationPack/app/Src/Codeunits/ItemChargeAssgntPurchCZL.Codeunit.al @@ -0,0 +1,149 @@ +namespace Microsoft.Purchases.Document; + +using Microsoft.Finance.Currency; +using Microsoft.Finance.GeneralLedger.Setup; +using Microsoft.Inventory.Item; +using Microsoft.Inventory.Ledger; +using Microsoft.Inventory.Tracking; +using Microsoft.Purchases.Posting; + +codeunit 31179 "Item Charge Assgnt. Purch. CZL" +{ + procedure CreateItemEntryChargeAssgnt(var FromItemLedgerEntry: Record "Item Ledger Entry"; var FromItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)") + var + ItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)"; + Item: Record Item; + PurchaseAppliestoDocumentType: Enum "Purchase Applies-to Document Type"; + NextLineNo: Integer; + IsHandled: Boolean; + begin + IsHandled := false; + OnBeforeCreateItemEntryChargeAssgnt(FromItemLedgerEntry, FromItemChargeAssignmentPurch, IsHandled); + if IsHandled then + exit; + + if not FromItemChargeAssignmentPurch.RecordLevelLocking then + FromItemChargeAssignmentPurch.LockTable(true, true); + + NextLineNo := FromItemChargeAssignmentPurch."Line No."; + + ItemChargeAssignmentPurch.SetRange("Document Type", FromItemChargeAssignmentPurch."Document Type"); + ItemChargeAssignmentPurch.SetRange("Document No.", FromItemChargeAssignmentPurch."Document No."); + ItemChargeAssignmentPurch.SetRange("Document Line No.", FromItemChargeAssignmentPurch."Document Line No."); + ItemChargeAssignmentPurch.SetRange("Applies-to Doc. Type", ItemChargeAssignmentPurch."Applies-to Doc. Type"::Receipt); + + FromItemLedgerEntry.SetLoadFields("Document No.", "Entry No.", "Item No.", "Description"); + if FromItemLedgerEntry.FindSet(true) then + repeat + ItemChargeAssignmentPurch.SetRange("Applies-to Doc. No.", FromItemLedgerEntry."Document No."); + ItemChargeAssignmentPurch.SetRange("Applies-to Doc. Line No.", FromItemLedgerEntry."Entry No."); + if FromItemLedgerEntry.Description = '' then begin + Item.Get(FromItemLedgerEntry."Item No."); + FromItemLedgerEntry.Description := Item.Description; + end; + if not ItemChargeAssignmentPurch.FindFirst() then + InsertItemChargeAssignmentWithValues( + FromItemChargeAssignmentPurch, PurchaseAppliestoDocumentType::"Item Ledger Entry Positive Adjmt. CZL", + FromItemLedgerEntry."Document No.", FromItemLedgerEntry."Entry No.", + FromItemLedgerEntry."Item No.", FromItemLedgerEntry.Description, NextLineNo); + until FromItemLedgerEntry.Next() = 0; + end; + + procedure PostItemChargePerPosAdjItem(PurchPost: Codeunit "Purch.-Post"; var TempItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)" temporary; PurchaseHeader: Record "Purchase Header"; PurchaseLine: Record "Purchase Line") + var + GeneralLedgerSetup: Record "General Ledger Setup"; + ItemLedgerEntry: Record "Item Ledger Entry"; + DummyTrackingSpecification: Record "Tracking Specification"; + CurrencyExchangeRate: Record "Currency Exchange Rate"; + IsHandled: Boolean; + TextDeletedErr: Label 'Item Ledger Entry has been deleted.'; + begin + IsHandled := false; + OnBeforePostItemChargePerPosAdjItem(PurchPost, TempItemChargeAssignmentPurch, PurchaseHeader, PurchaseLine, IsHandled); + if IsHandled then + exit; + + GeneralLedgerSetup.Get(); + if not ItemLedgerEntry.Get(TempItemChargeAssignmentPurch."Applies-to Doc. Line No.") then + Error(TextDeletedErr); + + PurchaseLine."No." := TempItemChargeAssignmentPurch."Item No."; + PurchaseLine."Appl.-to Item Entry" := ItemLedgerEntry."Entry No."; + PurchaseLine.Amount := TempItemChargeAssignmentPurch."Amount to Assign"; + PurchaseLine."Unit Cost" := Round(PurchaseLine.Amount / ItemLedgerEntry.Quantity, GeneralLedgerSetup."Unit-Amount Rounding Precision"); + if TempItemChargeAssignmentPurch."Document Type" in [TempItemChargeAssignmentPurch."Document Type"::"Return Order", TempItemChargeAssignmentPurch."Document Type"::"Credit Memo"] then + PurchaseLine.Amount := -PurchaseLine.Amount; + + if PurchaseHeader."Currency Code" <> '' then + PurchaseLine.Amount := + CurrencyExchangeRate.ExchangeAmtFCYToLCY( + PurchaseHeader.GetUseDate(), PurchaseHeader."Currency Code", PurchaseLine.Amount, PurchaseHeader."Currency Factor"); + PurchaseLine."Inv. Discount Amount" := Round( + PurchaseLine."Inv. Discount Amount" / PurchaseLine.Quantity * TempItemChargeAssignmentPurch."Qty. to Assign", + GeneralLedgerSetup."Amount Rounding Precision"); + + PurchaseLine.Amount := Round(PurchaseLine.Amount, GeneralLedgerSetup."Amount Rounding Precision"); + PurchaseLine."Unit Cost (LCY)" := Round(PurchaseLine.Amount / ItemLedgerEntry.Quantity, GeneralLedgerSetup."Unit-Amount Rounding Precision"); + PurchaseLine."Line No." := TempItemChargeAssignmentPurch."Document Line No."; + + PurchPost.PostItemJnlLine( + PurchaseHeader, PurchaseLine, 0, 0, + ItemLedgerEntry.Quantity, ItemLedgerEntry.Quantity, + PurchaseLine."Appl.-to Item Entry", TempItemChargeAssignmentPurch."Item Charge No.", DummyTrackingSpecification); + end; + + procedure AssignByAmountItemLedgerEntryPositiveAdjmt(ItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)"; var TempItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)" temporary; PurchaseHeader: Record "Purchase Header") + var + ItemLedgerEntry: Record "Item Ledger Entry"; + CurrencyExchangeRate: Record "Currency Exchange Rate"; + begin + ItemLedgerEntry.Get(ItemChargeAssignmentPurch."Applies-to Doc. Line No."); + ItemLedgerEntry.CalcFields("Cost Amount (Actual)"); + + if PurchaseHeader."Currency Code" = '' then + TempItemChargeAssignmentPurch."Applies-to Doc. Line Amount" := Abs(ItemLedgerEntry."Cost Amount (Actual)") + else + TempItemChargeAssignmentPurch."Applies-to Doc. Line Amount" := + CurrencyExchangeRate.ExchangeAmtFCYToFCY( + PurchaseHeader."Posting Date", '', PurchaseHeader."Currency Code", + Abs(ItemLedgerEntry."Cost Amount (Actual)")); + end; + + local procedure InsertItemChargeAssignmentWithValues(FromItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)"; PurchaseAppliestoDocumentType: Enum "Purchase Applies-to Document Type"; + FromApplToDocNo: Code[20]; FromApplToDocLineNo: Integer; FromItemNo: Code[20]; FromDescription: Text[100]; var NextLineNo: Integer) + var + ItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)"; + begin + NextLineNo := NextLineNo + 10000; + + ItemChargeAssignmentPurch."Document No." := FromItemChargeAssignmentPurch."Document No."; + ItemChargeAssignmentPurch."Document Type" := FromItemChargeAssignmentPurch."Document Type"; + ItemChargeAssignmentPurch."Document Line No." := FromItemChargeAssignmentPurch."Document Line No."; + ItemChargeAssignmentPurch."Item Charge No." := FromItemChargeAssignmentPurch."Item Charge No."; + ItemChargeAssignmentPurch."Line No." := NextLineNo; + ItemChargeAssignmentPurch."Applies-to Doc. No." := FromApplToDocNo; + ItemChargeAssignmentPurch."Applies-to Doc. Type" := PurchaseAppliestoDocumentType; + ItemChargeAssignmentPurch."Applies-to Doc. Line No." := FromApplToDocLineNo; + ItemChargeAssignmentPurch."Item No." := FromItemNo; + ItemChargeAssignmentPurch.Description := FromDescription; + ItemChargeAssignmentPurch."Unit Cost" := FromItemChargeAssignmentPurch."Unit Cost"; + + OnBeforeInsertItemChargeAssgntWithAssignValues(ItemChargeAssignmentPurch, FromItemChargeAssignmentPurch); + ItemChargeAssignmentPurch.Insert(); + end; + + [IntegrationEvent(false, false)] + local procedure OnBeforeCreateItemEntryChargeAssgnt(var ItemLedgerEntry: Record "Item Ledger Entry"; var ItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)"; var IsHandled: Boolean) + begin + end; + + [IntegrationEvent(false, false)] + local procedure OnBeforePostItemChargePerPosAdjItem(PurchPost: Codeunit "Purch.-Post"; var TempItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)" temporary; PurchaseHeader: Record "Purchase Header"; PurchaseLine: Record "Purchase Line"; var IsHandled: Boolean) + begin + end; + + [IntegrationEvent(false, false)] + local procedure OnBeforeInsertItemChargeAssgntWithAssignValues(var ItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)"; FromItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)") + begin + end; +} diff --git a/Apps/CZ/CoreLocalizationPack/app/Src/EnumExtensions/PurchAppliestoDocTypeCZL.EnumExt.al b/Apps/CZ/CoreLocalizationPack/app/Src/EnumExtensions/PurchAppliestoDocTypeCZL.EnumExt.al new file mode 100644 index 0000000000..f40ce3d590 --- /dev/null +++ b/Apps/CZ/CoreLocalizationPack/app/Src/EnumExtensions/PurchAppliestoDocTypeCZL.EnumExt.al @@ -0,0 +1,9 @@ +namespace Microsoft.Purchases.Document; + +enumextension 11706 "Purch. Applies-to Doc.Type CZL" extends "Purchase Applies-to Document Type" +{ + value(11700; "Item Ledger Entry Positive Adjmt. CZL") + { + Caption = 'Item Ledger Entry Positive Adjmt.'; + } +} diff --git a/Apps/CZ/CoreLocalizationPack/app/Src/PageExtensions/ItemChargeAsgmtPurchCZL.PageExt.al b/Apps/CZ/CoreLocalizationPack/app/Src/PageExtensions/ItemChargeAsgmtPurchCZL.PageExt.al new file mode 100644 index 0000000000..527ffa2986 --- /dev/null +++ b/Apps/CZ/CoreLocalizationPack/app/Src/PageExtensions/ItemChargeAsgmtPurchCZL.PageExt.al @@ -0,0 +1,80 @@ +namespace Microsoft.Purchases.Document; + +using Microsoft.Inventory.Ledger; + +pageextension 31103 "Item Charge Asgmt. (Purch) CZL" extends "Item Charge Assignment (Purch)" +{ + actions + { + addafter(SuggestItemChargeAssignment) + { + action(GetPosAdjLedgerEntriesCZL) + { + AccessByPermission = TableData "Item Ledger Entry" = R; + ApplicationArea = Basic, Suite; + Caption = 'Get Positive Adjmt. Ledger Entries'; + Image = ReceiveLoaner; + ToolTip = 'Open the page for the selection of the posting item ledger entries.'; + + trigger OnAction() + var + ItemLedgerEntry: Record "Item Ledger Entry"; + ItemChargeAssigmentPurch: Record "Item Charge Assignment (Purch)"; + + begin + ItemChargeAssigmentPurch.SetRange("Document Type", Rec."Document Type"); + ItemChargeAssigmentPurch.SetRange("Document No.", Rec."Document No."); + ItemChargeAssigmentPurch.SetRange("Document Line No.", Rec."Document Line No."); + if not ItemChargeAssigmentPurch.FindLast() then + ItemChargeAssigmentPurch := Rec; + + ItemLedgerEntry.FilterGroup(2); + ItemLedgerEntry.SetRange("Entry Type", ItemLedgerEntry."Entry Type"::"Positive Adjmt."); + ItemLedgerEntry.SetRange(Positive, true); + ItemLedgerEntry.FilterGroup(0); + OnGetPosAdjLedgerEntrieOnActionOnAfterItemChargeAssgntPurchSetFiltersCZL(Rec, ItemLedgerEntry, PurchLine); + + OpenItemLedgerEntries(ItemChargeAssigmentPurch, ItemLedgerEntry); + end; + } + } + addlast(Category_Process) + { + actionref(GetPosAdjLedgerEntriesCZLPromotedCZL; GetPosAdjLedgerEntriesCZL) + { + } + } + } + + local procedure OpenItemLedgerEntries(var ItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)"; var ItemLedgerEntry: Record "Item Ledger Entry") + var + AssignItemChargeAssgntPurchCZL: Codeunit "Item Charge Assgnt. Purch. CZL"; + ItemLedgerEntriesPage: Page "Item Ledger Entries"; + IsHandled: Boolean; + begin + IsHandled := false; + OnBeforeOpenItemLedgerEntriesCZL(Rec, ItemChargeAssignmentPurch, IsHandled); + if IsHandled then + exit; + + ItemLedgerEntriesPage.SetTableView(ItemLedgerEntry); + ItemLedgerEntriesPage.LookupMode(true); + if ItemLedgerEntriesPage.RunModal() = Action::LookupOK then begin + ItemLedgerEntriesPage.SetSelectionFilter(ItemLedgerEntry); + if not ItemLedgerEntry.IsEmpty() then begin + ItemChargeAssignmentPurch."Unit Cost" := PurchLine2."Unit Cost"; + AssignItemChargeAssgntPurchCZL.CreateItemEntryChargeAssgnt(ItemLedgerEntry, ItemChargeAssignmentPurch); + end; + end; + end; + + [IntegrationEvent(false, false)] + local procedure OnGetPosAdjLedgerEntrieOnActionOnAfterItemChargeAssgntPurchSetFiltersCZL(var ItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)"; var ItemLedgerEntry: Record "Item Ledger Entry"; PurchaseLine: Record "Purchase Line") + begin + end; + + [IntegrationEvent(false, false)] + local procedure OnBeforeOpenItemLedgerEntriesCZL(var RecItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)"; var ItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)"; var IsHandled: Boolean) + begin + end; +} diff --git a/Apps/CZ/CoreLocalizationPack/app/Src/Permissions/CZCorePackObjectsCZL.PermissionSet.al b/Apps/CZ/CoreLocalizationPack/app/Src/Permissions/CZCorePackObjectsCZL.PermissionSet.al index 1d9f1ad536..f7c4391fb6 100644 --- a/Apps/CZ/CoreLocalizationPack/app/Src/Permissions/CZCorePackObjectsCZL.PermissionSet.al +++ b/Apps/CZ/CoreLocalizationPack/app/Src/Permissions/CZCorePackObjectsCZL.PermissionSet.al @@ -59,6 +59,8 @@ codeunit "Invt. Document Handler CZL" = X, codeunit "Invt. Document-Printed CZL" = X, codeunit "Item Handler CZL" = X, + codeunit "Item Charge Assgnt Handler CZL" = X, + codeunit "Item Charge Assgnt. Purch. CZL" = X, codeunit "Item Jnl.CheckLine Handler CZL" = X, codeunit "Item Jnl. Template Handler CZL" = X, codeunit "Item Journal Line Handler CZL" = X, diff --git a/Apps/DE/EDocumentFormatXRechnung/app/ExtensionLogo.png b/Apps/DE/EDocumentFormatXRechnung/app/ExtensionLogo.png new file mode 100644 index 0000000000..4d2c9a626c Binary files /dev/null and b/Apps/DE/EDocumentFormatXRechnung/app/ExtensionLogo.png differ diff --git a/Apps/DE/EDocumentFormatXRechnung/app/app.json b/Apps/DE/EDocumentFormatXRechnung/app/app.json new file mode 100644 index 0000000000..3382dcc73d --- /dev/null +++ b/Apps/DE/EDocumentFormatXRechnung/app/app.json @@ -0,0 +1,38 @@ +{ + "id": "fdeb586e-beff-49d8-947d-1e73ce980b34", + "name": "E-Document format for XRechnung", + "publisher": "Microsoft", + "brief": "E-Document format for XRechnung.", + "description": ": XRechnung is a customization for Germany business requirements. This app supports XRechnung 3.0.2 format for working with E-documents app.", + "version": "26.0.0.0", + "privacyStatement": "https://go.microsoft.com/fwlink/?LinkId=724009", + "EULA": "https://go.microsoft.com/fwlink/?linkid=2009120", + "help": "https://go.microsoft.com/fwlink/?LinkId=724011", + "url": "https://go.microsoft.com/fwlink/?LinkId=724011", + "logo": "ExtensionLogo.png", + "contextSensitiveHelpUrl": "https://go.microsoft.com/fwlink/?linkid=2206603", + "dependencies": [ + { + "id": "e1d97edc-c239-46b4-8d84-6368bdf67c8b", + "name": "E-Document Core", + "publisher": "Microsoft", + "version": "26.0.0.0" + } + ], + "screenshots": [], + "platform": "26.0.0.0", + "idRanges": [ + { + "from": 13914, + "to": 13917 + } + ], + "resourceExposurePolicy": { + "allowDebugging": true, + "allowDownloadingSource": true, + "includeSourceInSymbolFile": true + }, + "application": "26.0.0.0", + "target": "Cloud", + "features": ["TranslationFile"] +} \ No newline at end of file diff --git a/Apps/DE/EDocumentFormatXRechnung/app/src/ImportXRechnungDocument.Codeunit.al b/Apps/DE/EDocumentFormatXRechnung/app/src/ImportXRechnungDocument.Codeunit.al new file mode 100644 index 0000000000..ad558e1788 --- /dev/null +++ b/Apps/DE/EDocumentFormatXRechnung/app/src/ImportXRechnungDocument.Codeunit.al @@ -0,0 +1,450 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.eServices.EDocument.Formats; + +using System.Utilities; +using Microsoft.Finance.GeneralLedger.Setup; +using System.IO; +using Microsoft.Purchases.Document; +using System.Telemetry; +using Microsoft.eServices.EDocument; +using Microsoft.Purchases.Vendor; + +codeunit 13915 "Import XRechnung Document" +{ + Access = Internal; + InherentEntitlements = X; + InherentPermissions = X; + + var + FeatureTelemetry: Codeunit "Feature Telemetry"; + FeatureNameTok: Label 'E-document XRechnung Format', Locked = true; + StartEventNameTok: Label 'E-document XRechnung import started. Parsing basic information.', Locked = true; + ContinueEventNameTok: Label 'Parsing complete information for E-document XRechnung import.', Locked = true; + EndEventNameTok: Label 'E-document XRechnung import completed. %1 #%2 created.', Locked = true; + + procedure ParseBasicInfo(var EDocument: Record "E-Document"; var TempBlob: Codeunit "Temp Blob") + var + TempXMLBuffer: Record "XML Buffer" temporary; + DocumentType: Text; + DocumentNamespace: Text; + DocStream: InStream; + DocumentTypeLbl: Label '%1:%2', Comment = '%1 = Namespace, %2 = Document type'; + begin + FeatureTelemetry.LogUsage('0000EXH', FeatureNameTok, StartEventNameTok); + TempXMLBuffer.DeleteAll(); + TempBlob.CreateInStream(DocStream); + TempXMLBuffer.LoadFromStream(DocStream); + + EDocument.Direction := EDocument.Direction::Incoming; + DocumentType := GetDocumentType(TempXMLBuffer, DocumentNamespace); + + case UpperCase(DocumentType) of + 'INVOICE': + ParseInvoiceBasicInfo(EDocument, TempXMLBuffer, StrSubstNo(DocumentTypeLbl, DocumentNamespace, DocumentType)); + 'CREDITNOTE': + ParseCreditMemoBasicInfo(EDocument, TempXMLBuffer, StrSubstNo(DocumentTypeLbl, DocumentNamespace, DocumentType)); + end; + end; + + procedure ParseCompleteInfo(var EDocument: Record "E-Document"; var PurchaseHeader: Record "Purchase Header" temporary; var PurchaseLine: Record "Purchase Line" temporary; var TempBlob: Codeunit "Temp Blob") + var + TempXMLBuffer: Record "XML Buffer" temporary; + DocumentType: Text; + DocumentNamespace: Text; + DocStream: InStream; + DocumentTypeLbl: Label '%1:%2', Comment = '%1 = Namespace, %2 = Document type'; + begin + FeatureTelemetry.LogUsage('0000EXI', FeatureNameTok, ContinueEventNameTok); + TempXMLBuffer.DeleteAll(); + TempBlob.CreateInStream(DocStream); + TempXMLBuffer.LoadFromStream(DocStream); + + PurchaseHeader."Buy-from Vendor No." := EDocument."Bill-to/Pay-to No."; + PurchaseHeader."Currency Code" := EDocument."Currency Code"; + DocumentType := GetDocumentType(TempXMLBuffer, DocumentNamespace); + + case UpperCase(DocumentType) of + 'INVOICE': + CreateInvoice(EDocument, PurchaseHeader, PurchaseLine, TempXMLBuffer, StrSubstNo(DocumentTypeLbl, DocumentNamespace, DocumentType)); + 'CREDITNOTE': + CreateCreditMemo(EDocument, PurchaseHeader, PurchaseLine, TempXMLBuffer, StrSubstNo(DocumentTypeLbl, DocumentNamespace, DocumentType)); + end; + FeatureTelemetry.LogUsage('0000EXJ', FeatureNameTok, StrSubstNo(EndEventNameTok, EDocument."Document Type", EDocument."Incoming E-Document No.")); + end; + + local procedure GetDocumentType(var TempXMLBuffer: Record "XML Buffer" temporary; var Namespace: Text): Text + var + InvalidXMLFileErr: Label 'Invalid XML file'; + begin + TempXMLBuffer.Reset(); + TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); + TempXMLBuffer.SetRange("Parent Entry No.", 0); + if not TempXMLBuffer.FindFirst() then + Error(InvalidXMLFileErr); + + Namespace := TempXMLBuffer.Namespace; + TempXMLBuffer.Reset(); + exit(TempXMLBuffer.Name); + end; + + local procedure GetNodeByPath(var TempXMLBuffer: Record "XML Buffer" temporary; XPath: Text): Text + begin + TempXMLBuffer.Reset(); + TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Element); + TempXMLBuffer.SetRange(Path, XPath); + if TempXMLBuffer.FindFirst() then + exit(TempXMLBuffer.Value); + end; + + local procedure GetAttributeByPath(var TempXMLBuffer: Record "XML Buffer" temporary; XPath: Text): Text + begin + TempXMLBuffer.Reset(); + TempXMLBuffer.SetRange(Type, TempXMLBuffer.Type::Attribute); + TempXMLBuffer.SetFilter(Path, XPath); + if TempXMLBuffer.FindFirst() then + exit(TempXMLBuffer.Value); + end; + + local procedure ParseAccountingSupplierParty(var EDocument: Record "E-Document"; var TempXMLBuffer: Record "XML Buffer" temporary; DocumentType: Text) + var + Vendor: Record Vendor; + EDocumentImportHelper: Codeunit "E-Document Import Helper"; + VendorName, VendorAddress : Text; + VATRegistrationNo: Text[20]; + VendorNo: Code[20]; + begin + if GetAttributeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingSupplierParty/cac:Party/cbc:EndpointID/@schemeID') in ['EM', '0198'] then + VATRegistrationNo := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingSupplierParty/cac:Party/cbc:EndpointID'), 1, MaxStrLen(VATRegistrationNo)); + + if VATRegistrationNo = '' then + if GetAttributeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingSupplierParty/cac:Party/cac:PartyIdentification/cbc:ID/@schemeID') in ['EM', '0198'] then + VATRegistrationNo := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingSupplierParty/cac:Party/cac:PartyIdentification/cbc:ID'), 1, MaxStrLen(VATRegistrationNo)); + + VendorNo := EDocumentImportHelper.FindVendor('', '', VATRegistrationNo); + if VendorNo = '' then begin + VendorName := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name'), 1, MaxStrLen(VATRegistrationNo)); + VendorAddress := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:StreetName'), 1, MaxStrLen(VATRegistrationNo)); + VendorNo := EDocumentImportHelper.FindVendorByNameAndAddress(VendorName, VendorAddress); + EDocument."Bill-to/Pay-to Name" := CopyStr(VendorName, 1, MaxStrLen(EDocument."Bill-to/Pay-to Name")); + end; + + Vendor := EDocumentImportHelper.GetVendor(EDocument, VendorNo); + if Vendor."No." <> '' then begin + EDocument."Bill-to/Pay-to No." := Vendor."No."; + EDocument."Bill-to/Pay-to Name" := Vendor.Name; + end; + end; + + local procedure ParseAccountingCustomerParty(var EDocument: Record "E-Document"; var TempXMLBuffer: Record "XML Buffer" temporary; DocumentType: Text) + begin + EDocument."Receiving Company Name" := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName'), 1, MaxStrLen(EDocument."Receiving Company Name")); + EDocument."Receiving Company Address" := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party/cac:PostalAddress/cbc:StreetName'), 1, MaxStrLen(EDocument."Receiving Company Address")); + + if GetAttributeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party/cbc:EndpointID/@schemeID') = '0094' then + EDocument."Receiving Company GLN" := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party/cbc:EndpointID'), 1, MaxStrLen(EDocument."Receiving Company GLN")); + if EDocument."Receiving Company GLN" = '' then + if GetAttributeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party/cac:PartyIdentification/cbc:ID/@schemeID') = '0094' then + EDocument."Receiving Company GLN" := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party/cac:PartyIdentification/cbc:ID'), 1, MaxStrLen(EDocument."Receiving Company GLN")); + if GetAttributeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party/cbc:EndpointID/@schemeID') in ['EM', '0198'] then + EDocument."Receiving Company VAT Reg. No." := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party/cbc:EndpointID'), 1, MaxStrLen(EDocument."Receiving Company VAT Reg. No.")); + if EDocument."Receiving Company VAT Reg. No." = '' then + if GetAttributeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party/cac:PartyIdentification/cbc:ID/@schemeID') in ['EM', '0198'] then + EDocument."Receiving Company VAT Reg. No." := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party/cac:PartyIdentification/cbc:ID'), 1, MaxStrLen(EDocument."Receiving Company VAT Reg. No.")); + end; + + local procedure CreateAllowanceChargeLines(var EDocument: Record "E-Document"; var PurchaseHeader: Record "Purchase Header" temporary; var PurchaseLine: record "Purchase Line" temporary; var TempXMLBuffer: Record "XML Buffer" temporary; DocumentType: Text) + var + LineNo: Integer; + begin + + TempXMLBuffer.Reset(); + TempXMLBuffer.SetFilter(Path, '/' + DocumentType + '/cac:AllowanceCharge*'); + + PurchaseLine.FindLast(); + LineNo := PurchaseLine."Line No." + 10000; + + if TempXMLBuffer.FindSet() then + repeat + case TempXMLBuffer.Path of + '/' + DocumentType + '/cac:AllowanceCharge/cbc:ChargeIndicator': + if TempXMLBuffer.Value = 'true' then begin + SetGLAccountAndInsertLine(EDocument, PurchaseLine, LineNo); + + PurchaseLine.Init(); + PurchaseLine."Document Type" := PurchaseHeader."Document Type"; + PurchaseLine."Document No." := PurchaseHeader."No."; + PurchaseLine."Line No." := LineNo; + PurchaseLine.Quantity := 1; + PurchaseLine.Type := PurchaseLine.Type::"G/L Account"; + end; + '/' + DocumentType + '/cac:AllowanceCharge/cbc:Amount': + if TempXMLBuffer.Value <> '' then begin + Evaluate(PurchaseLine."Direct Unit Cost", TempXMLBuffer.Value, 9); + Evaluate(PurchaseLine.Amount, TempXMLBuffer.Value, 9); + end; + '/' + DocumentType + '/cac:AllowanceCharge/cbc:AllowanceChargeReason': + PurchaseLine.Description := CopyStr(TempXMLBuffer.Value, 1, MaxStrLen(PurchaseLine.Description)); + + end; + until TempXMLBuffer.Next() = 0; + + SetGLAccountAndInsertLine(EDocument, PurchaseLine, LineNo); + end; + + local procedure SetGLAccountAndInsertLine(var EDocument: Record "E-Document"; var PurchaseLine: record "Purchase Line" temporary; var LineNo: Integer) + var + EDocumentImportHelper: Codeunit "E-Document Import Helper"; + RecRef: RecordRef; + begin + if PurchaseLine."Line No." = LineNo then begin + RecRef.GetTable(PurchaseLine); + EDocumentImportHelper.FindGLAccountForLine(EDocument, RecRef); + PurchaseLine."No." := RecRef.Field(PurchaseLine.FieldNo("No.")).Value; + PurchaseLine.Insert(true); + LineNo += 10000; + end; + end; + + local procedure GetLastLineNo(PurchaseHeader: Record "Purchase Header"): Integer + var + PurchaseLine: Record "Purchase Line"; + begin + PurchaseLine.SetRange("Document No.", PurchaseHeader."No."); + PurchaseLine.SetRange("Document Type", PurchaseHeader."Document Type"); + if PurchaseLine.FindLast() then + exit(PurchaseLine."Line No."); + exit(0); + end; + + #region Invoice + + local procedure ParseInvoiceBasicInfo(var EDocument: Record "E-Document"; var TempXMLBuffer: Record "XML Buffer" temporary; DocumentType: Text) + var + GeneralLedgerSetup: Record "General Ledger Setup"; + DueDate, IssueDate : Text; + CurrencyCode: Text[10]; + begin + EDocument."Document Type" := EDocument."Document Type"::"Purchase Invoice"; + EDocument."Incoming E-Document No." := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cbc:ID'), 1, MaxStrLen(EDocument."Document No.")); + ParseAccountingSupplierParty(EDocument, TempXMLBuffer, DocumentType); + ParseAccountingCustomerParty(EDocument, TempXMLBuffer, DocumentType); + + DueDate := GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cbc:DueDate'); + if DueDate <> '' then + Evaluate(EDocument."Due Date", DueDate, 9); + IssueDate := GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cbc:IssueDate'); + if IssueDate <> '' then + Evaluate(EDocument."Document Date", IssueDate, 9); + Evaluate(EDocument."Amount Excl. VAT", GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount'), 9); + Evaluate(EDocument."Amount Incl. VAT", GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount'), 9); + + CurrencyCode := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cbc:DocumentCurrencyCode'), 1, MaxStrLen(EDocument."Currency Code")); + GeneralLedgerSetup.Get(); + if CurrencyCode <> GeneralLedgerSetup."LCY Code" then + EDocument."Currency Code" := CurrencyCode; + end; + + local procedure CreateInvoice(var EDocument: Record "E-Document"; var PurchaseHeader: Record "Purchase Header" temporary; var PurchaseLine: Record "Purchase Line" temporary; var TempXMLBuffer: Record "XML Buffer" temporary; DocumentType: Text) + var + LastLineNo: Integer; + begin + PurchaseHeader."Document Type" := PurchaseHeader."Document Type"::Invoice; + PurchaseHeader."No." := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cbc:ID'), 1, MaxStrLen(PurchaseHeader."No.")); + PurchaseHeader.Insert(true); + + LastLineNo := GetLastLineNo(PurchaseHeader); + + TempXMLBuffer.Reset(); + if TempXMLBuffer.FindSet() then + repeat + ParseInvoice(PurchaseHeader, PurchaseLine, TempXMLBuffer.Path, TempXMLBuffer.Value, DocumentType, LastLineNo); + until TempXMLBuffer.Next() = 0; + + // Insert last line + PurchaseLine.Insert(true); + PurchaseHeader.Modify(true); + + CreateAllowanceChargeLines(EDocument, PurchaseHeader, PurchaseLine, TempXMLBuffer, DocumentType); + end; + + local procedure ParseInvoice(var PurchaseHeader: Record "Purchase Header" temporary; var PurchaseLine: Record "Purchase Line" temporary; Path: Text; Value: Text; DocumentType: Text; var LastLineNo: Integer) + begin + case Path of + '/' + DocumentType + '/cbc:ID': + PurchaseHeader."Vendor Invoice No." := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Vendor Invoice No.")); + '/' + DocumentType + '/cbc:DueDate': + if Value <> '' then + Evaluate(PurchaseHeader."Due Date", Value, 9); + '/' + DocumentType + '/cbc:IssueDate': + if Value <> '' then begin + Evaluate(PurchaseHeader."Document Date", Value, 9); + PurchaseHeader."Posting Date" := PurchaseHeader."Document Date"; + end; + '/' + DocumentType + '/cbc:BuyerReference': + PurchaseHeader."Your Reference" := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Your Reference")); + '/' + DocumentType + '/cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:Name': + begin + PurchaseHeader."Buy-from Contact" := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Buy-from Contact")); + PurchaseHeader."Pay-to Contact" := PurchaseHeader."Buy-from Contact"; + end; + '/' + DocumentType + '/cac:LegalMonetaryTotal/cbc:AllowanceTotalAmount': + if Value <> '' then + Evaluate(PurchaseHeader."Invoice Discount Value", Value, 9); + //Lines + '/' + DocumentType + '/cac:InvoiceLine': + begin + if PurchaseLine."Document No." <> '' then + PurchaseLine.Insert(true); + + PurchaseLine.Init(); + PurchaseLine."Document Type" := PurchaseHeader."Document Type"; + PurchaseLine."Document No." := PurchaseHeader."No."; + PurchaseLine."Line No." := LastLineNo + 10000; + LastLineNo := PurchaseLine."Line No."; + end; + '/' + DocumentType + '/cac:InvoiceLine/cbc:InvoicedQuantity': + if Value <> '' then + Evaluate(PurchaseLine.Quantity, Value, 9); + '/' + DocumentType + '/cac:InvoiceLine/cbc:InvoicedQuantity/@unitCode': + PurchaseLine."Unit of Measure Code" := CopyStr(Value, 1, MaxStrLen(PurchaseLine."Unit of Measure Code")); + '/' + DocumentType + '/cac:InvoiceLine/cbc:LineExtensionAmount': + begin + if Value <> '' then + Evaluate(PurchaseLine.Amount, Value, 9); + PurchaseLine."VAT Base Amount" := PurchaseLine.Amount; + end; + '/' + DocumentType + '/cac:InvoiceLine/cac:Item/cbc:Description': + PurchaseLine."Description 2" := CopyStr(Value, 1, MaxStrLen(PurchaseLine."Description 2")); + '/' + DocumentType + '/cac:InvoiceLine/cac:Item/cbc:Name': + PurchaseLine.Description := CopyStr(Value, 1, MaxStrLen(PurchaseLine.Description)); + '/' + DocumentType + '/cac:InvoiceLine/cac:Item/cac:SellersItemIdentification/cbc:ID': + PurchaseLine."Item Reference No." := CopyStr(Value, 1, MaxStrLen(PurchaseLine."Item Reference No.")); + '/' + DocumentType + '/cac:InvoiceLine/cac:Item/cac:StandardItemIdentification/cbc:ID': + PurchaseLine."No." := CopyStr(Value, 1, MaxStrLen(PurchaseLine."No.")); + '/' + DocumentType + '/cac:InvoiceLine/cac:Item/cac:ClassifiedTaxCategory/cbc:Percent': + if Value <> '' then + Evaluate(PurchaseLine."VAT %", Value, 9); + '/' + DocumentType + '/cac:InvoiceLine/cac:Price/cbc:PriceAmount': + if Value <> '' then + Evaluate(PurchaseLine."Direct Unit Cost", Value, 9); + end; + end; + #endregion + + #region Credit Memo + local procedure ParseCreditMemoBasicInfo(var EDocument: Record "E-Document"; var TempXMLBuffer: Record "XML Buffer" temporary; DocumentType: Text) + var + GeneralLedgerSetup: Record "General Ledger Setup"; + DueDate, IssueDate : Text; + CurrencyCode: Text[10]; + begin + EDocument."Document Type" := EDocument."Document Type"::"Purchase Credit Memo"; + EDocument."Incoming E-Document No." := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cbc:ID'), 1, MaxStrLen(EDocument."Document No.")); + ParseAccountingSupplierParty(EDocument, TempXMLBuffer, DocumentType); + ParseAccountingCustomerParty(EDocument, TempXMLBuffer, DocumentType); + + DueDate := GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cbc:DueDate'); + if DueDate <> '' then + Evaluate(EDocument."Due Date", DueDate, 9); + IssueDate := GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cbc:IssueDate'); + if IssueDate <> '' then + Evaluate(EDocument."Document Date", IssueDate, 9); + Evaluate(EDocument."Amount Excl. VAT", GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount'), 9); + Evaluate(EDocument."Amount Incl. VAT", GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount'), 9); + + CurrencyCode := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cbc:DocumentCurrencyCode'), 1, MaxStrLen(EDocument."Currency Code")); + GeneralLedgerSetup.Get(); + if CurrencyCode <> GeneralLedgerSetup."LCY Code" then + EDocument."Currency Code" := CurrencyCode; + end; + + local procedure CreateCreditMemo(var EDocument: Record "E-Document"; var PurchaseHeader: Record "Purchase Header" temporary; var PurchaseLine: Record "Purchase Line" temporary; var TempXMLBuffer: Record "XML Buffer" temporary; DocumentType: Text) + var + LastLineNo: Integer; + begin + PurchaseHeader."Document Type" := PurchaseHeader."Document Type"::"Credit Memo"; + PurchaseHeader."No." := CopyStr(GetNodeByPath(TempXMLBuffer, '/' + DocumentType + '/cbc:ID'), 1, MaxStrLen(PurchaseHeader."No.")); + PurchaseHeader.Insert(true); + + LastLineNo := GetLastLineNo(PurchaseHeader); + + TempXMLBuffer.Reset(); + if TempXMLBuffer.FindSet() then + repeat + ParseCreditMemo(PurchaseHeader, PurchaseLine, TempXMLBuffer.Path, TempXMLBuffer.Value, DocumentType, LastLineNo); + until TempXMLBuffer.Next() = 0; + + // Insert last line + PurchaseLine.Insert(true); + PurchaseHeader.Modify(true); + + CreateAllowanceChargeLines(EDocument, PurchaseHeader, PurchaseLine, TempXMLBuffer, DocumentType); + end; + + local procedure ParseCreditMemo(var PurchaseHeader: Record "Purchase Header" temporary; var PurchaseLine: Record "Purchase Line" temporary; Path: Text; Value: Text; DocumentType: Text; var LastLineNo: Integer) + begin + case Path of + '/' + DocumentType + '/cbc:ID': + PurchaseHeader."Vendor Invoice No." := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Vendor Invoice No.")); + '/' + DocumentType + '/cbc:DueDate': + if Value <> '' then + Evaluate(PurchaseHeader."Due Date", Value, 9); + '/' + DocumentType + '/cbc:IssueDate': + if Value <> '' then begin + Evaluate(PurchaseHeader."Document Date", Value, 9); + PurchaseHeader."Posting Date" := PurchaseHeader."Document Date"; + end; + '/' + DocumentType + '/cbc:BuyerReference': + PurchaseHeader."Your Reference" := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Your Reference")); + '/' + DocumentType + '/cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:Name': + begin + PurchaseHeader."Buy-from Contact" := CopyStr(Value, 1, MaxStrLen(PurchaseHeader."Buy-from Contact")); + PurchaseHeader."Pay-to Contact" := PurchaseHeader."Buy-from Contact"; + end; + '/' + DocumentType + '/cac:LegalMonetaryTotal/cbc:AllowanceTotalAmount': + if Value <> '' then + Evaluate(PurchaseHeader."Invoice Discount Value", Value, 9); + //Lines + '/' + DocumentType + '/cac:CreditNoteLine': + begin + if PurchaseLine."Document No." <> '' then + PurchaseLine.Insert(true); + + PurchaseLine.Init(); + PurchaseLine."Document Type" := PurchaseHeader."Document Type"; + PurchaseLine."Document No." := PurchaseHeader."No."; + PurchaseLine."Line No." := LastLineNo + 10000; + LastLineNo := PurchaseLine."Line No."; + end; + '/' + DocumentType + '/cac:CreditNoteLine/cbc:CreditedQuantity': + if Value <> '' then + Evaluate(PurchaseLine.Quantity, Value, 9); + '/' + DocumentType + '/cac:CreditNoteLine/cbc:CreditedQuantity/@unitCode': + PurchaseLine."Unit of Measure Code" := CopyStr(Value, 1, MaxStrLen(PurchaseLine."Unit of Measure Code")); + '/' + DocumentType + '/cac:CreditNoteLine/cbc:LineExtensionAmount': + begin + if Value <> '' then + Evaluate(PurchaseLine.Amount, Value, 9); + PurchaseLine."VAT Base Amount" := PurchaseLine.Amount; + end; + '/' + DocumentType + '/cac:CreditNoteLine/cac:Item/cbc:Description': + PurchaseLine."Description 2" := CopyStr(Value, 1, MaxStrLen(PurchaseLine."Description 2")); + '/' + DocumentType + '/cac:CreditNoteLine/cac:Item/cbc:Name': + PurchaseLine.Description := CopyStr(Value, 1, MaxStrLen(PurchaseLine.Description)); + '/' + DocumentType + '/cac:CreditNoteLine/cac:Item/cac:SellersItemIdentification/cbc:ID': + PurchaseLine."Item Reference No." := CopyStr(Value, 1, MaxStrLen(PurchaseLine."Item Reference No.")); + '/' + DocumentType + '/cac:CreditNoteLine/cac:Item/cac:StandardItemIdentification/cbc:ID': + PurchaseLine."No." := CopyStr(Value, 1, MaxStrLen(PurchaseLine."No.")); + '/' + DocumentType + '/cac:CreditNoteLine/cac:Item/cac:ClassifiedTaxCategory/cbc:Percent': + if Value <> '' then + Evaluate(PurchaseLine."VAT %", Value, 9); + '/' + DocumentType + '/cac:CreditNoteLine/cac:Price/cbc:PriceAmount': + if Value <> '' then + Evaluate(PurchaseLine."Direct Unit Cost", Value, 9); + end; + end; + #endregion +} \ No newline at end of file diff --git a/Apps/DE/EDocumentFormatXRechnung/app/src/XRechnungFormat.Codeunit.al b/Apps/DE/EDocumentFormatXRechnung/app/src/XRechnungFormat.Codeunit.al new file mode 100644 index 0000000000..8a9435f8c1 --- /dev/null +++ b/Apps/DE/EDocumentFormatXRechnung/app/src/XRechnungFormat.Codeunit.al @@ -0,0 +1,73 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.eServices.EDocument.Formats; + +using System.Utilities; +using Microsoft.Purchases.Document; +using Microsoft.eServices.EDocument; + +codeunit 13914 "XRechnung Format" implements "E-Document" +{ + InherentEntitlements = X; + InherentPermissions = X; + + var + EDocImportXRechnung: Codeunit "Import XRechnung Document"; + + procedure Check(var SourceDocumentHeader: RecordRef; EDocumentService: Record "E-Document Service"; EDocumentProcessingPhase: Enum "E-Document Processing Phase") + begin + end; + + procedure Create(EDocumentService: Record "E-Document Service"; var EDocument: Record "E-Document"; var SourceDocumentHeader: RecordRef; var SourceDocumentLines: RecordRef; var TempBlob: Codeunit "Temp Blob") + begin + end; + + procedure CreateBatch(EDocumentService: Record "E-Document Service"; var EDocuments: Record "E-Document"; var SourceDocumentHeaders: RecordRef; var SourceDocumentsLines: RecordRef; var TempBlob: Codeunit "Temp Blob") + begin + end; + + procedure GetBasicInfoFromReceivedDocument(var EDocument: Record "E-Document"; var TempBlob: Codeunit "Temp Blob") + begin + EDocImportXRechnung.ParseBasicInfo(EDocument, TempBlob); + end; + + procedure GetCompleteInfoFromReceivedDocument(var EDocument: Record "E-Document"; var CreatedDocumentHeader: RecordRef; var CreatedDocumentLines: RecordRef; var TempBlob: Codeunit "Temp Blob") + var + TempPurchaseHeader: Record "Purchase Header" temporary; + TempPurchaseLine: Record "Purchase Line" temporary; + begin + EDocImportXRechnung.ParseCompleteInfo(EDocument, TempPurchaseHeader, TempPurchaseLine, TempBlob); + + CreatedDocumentHeader.GetTable(TempPurchaseHeader); + CreatedDocumentLines.GetTable(TempPurchaseLine); + end; + + [EventSubscriber(ObjectType::Table, Database::"E-Document Service", 'OnAfterValidateEvent', 'Document Format', false, false)] + local procedure OnAfterValidateDocumentFormat(var Rec: Record "E-Document Service"; var xRec: Record "E-Document Service"; CurrFieldNo: Integer) + var + EDocServiceSupportedType: Record "E-Doc. Service Supported Type"; + begin + if Rec."Document Format" <> Rec."Document Format"::XRechnung then + exit; + + EDocServiceSupportedType.SetRange("E-Document Service Code", Rec.Code); + if not EDocServiceSupportedType.IsEmpty() then + exit; + + EDocServiceSupportedType.Init(); + EDocServiceSupportedType."E-Document Service Code" := Rec.Code; + EDocServiceSupportedType."Source Document Type" := EDocServiceSupportedType."Source Document Type"::"Sales Invoice"; + EDocServiceSupportedType.Insert(); + + EDocServiceSupportedType."Source Document Type" := EDocServiceSupportedType."Source Document Type"::"Sales Credit Memo"; + EDocServiceSupportedType.Insert(); + + EDocServiceSupportedType."Source Document Type" := EDocServiceSupportedType."Source Document Type"::"Purchase Invoice"; + EDocServiceSupportedType.Insert(); + + EDocServiceSupportedType."Source Document Type" := EDocServiceSupportedType."Source Document Type"::"Purchase Credit Memo"; + EDocServiceSupportedType.Insert(); + end; +} \ No newline at end of file diff --git a/Apps/DE/EDocumentFormatXRechnung/app/src/XRechnungFormat.EnumExt.al b/Apps/DE/EDocumentFormatXRechnung/app/src/XRechnungFormat.EnumExt.al new file mode 100644 index 0000000000..9fe3cc7fa5 --- /dev/null +++ b/Apps/DE/EDocumentFormatXRechnung/app/src/XRechnungFormat.EnumExt.al @@ -0,0 +1,13 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.eServices.EDocument.Formats; +using Microsoft.eServices.EDocument; +enumextension 13914 "XRechnung Format" extends "E-Document Format" +{ + value(13914; "XRechnung") + { + Implementation = "E-Document" = "XRechnung Format"; + } +} \ No newline at end of file diff --git a/Apps/DK/FIK/app/src/codeunits/FIKManagement.Codeunit.al b/Apps/DK/FIK/app/src/codeunits/FIKManagement.Codeunit.al index 72bbf4a659..9702cb21bc 100644 --- a/Apps/DK/FIK/app/src/codeunits/FIKManagement.Codeunit.al +++ b/Apps/DK/FIK/app/src/codeunits/FIKManagement.Codeunit.al @@ -89,10 +89,13 @@ Codeunit 13650 FIKManagement END; PaymentMethod.PaymentTypeValidation::"FIK 01", PaymentMethod.PaymentTypeValidation::"FIK 73": ERROR(PmtReferenceErr, PaymentMethod.PaymentTypeValidation); - ELSE begin + else begin OnEvaluateFIKCasePaymentTypeValidationElse(PaymentReference, PaymentMethod, Result, IsHandled); if not IsHandled then - ERROR(FIKPmtErr); + if PaymentMethod.PaymentTypeValidation = PaymentMethod.PaymentTypeValidation::Domestic then + Result := true + else + ERROR(FIKPmtErr); end; END; IF NOT Result THEN diff --git a/Apps/DK/FIK/test/src/CashMgmtE2EScenarios.Codeunit.al b/Apps/DK/FIK/test/src/CashMgmtE2EScenarios.Codeunit.al index 522108fa8c..7b9a0c4307 100644 --- a/Apps/DK/FIK/test/src/CashMgmtE2EScenarios.Codeunit.al +++ b/Apps/DK/FIK/test/src/CashMgmtE2EScenarios.Codeunit.al @@ -190,6 +190,37 @@ codeunit 148031 "Cash Mgmt E2E Scenarios" Assert.IsFalse(ImpGenJournalLine.IsApplied(), 'Gen jnl. line should not be applied.'); end; + [Test] + procedure ApplyDomesticFIKToInvoiceWithCopyInvNoToPmtRef(); + var + Vendor: Record Vendor; + InvGenJournalLine: Record "Gen. Journal Line"; + GenJournalBatch: Record "Gen. Journal Batch"; + PmtGenJournalBatch: Record "Gen. Journal Batch"; + PaymentMethod: Record "Payment Method"; + PmtGenJournalLine: Record "Gen. Journal Line"; + begin + // [GIVEN] "Copy Invoice No. To Payment Reference" is enabled in Purchase & Payables setup + SetCopyInvNoToPmtRef(true); + + // [GIVEN] A vendor with domestic payment method + LibraryPaymentExportDK.CreateVendorWithBankAccount(Vendor); + LibraryPaymentExportDK.AddPaymentTypeInfoToVendor(Vendor, PaymentMethod.PaymentTypeValidation::Domestic, ''); + LibraryERM.SelectGenJnlBatch(GenJournalBatch); + // [GIVEN] Posted invoice + PostVendorInvoice(Vendor, InvGenJournalLine, GenJournalBatch); + + // [GIVEN] Payment journal line with "Payment Reference" = "X" + LibraryPaymentExportDK.CreateGenJournalBatch(PmtGenJournalBatch, GenJournalBatch."Bal. Account Type"::"Bank Account", LibraryERM.CreateBankAccountNo(), true); + LibraryPaymentExportDK.CreateVendorPmtJnlLine(PmtGenJournalLine, PmtGenJournalBatch, Vendor."No."); + PmtGenJournalLine.Validate("Payment Reference", LibraryUtility.GenerateGUID()); + PmtGenJournalLine.Modify(true); + // [WHEN] Apply payment to posted invoice + ApplyVendorInvoiceToPmt(InvGenJournalLine, PmtGenJournalLine); + // [THEN] Payment is applied to the invoice + PmtGenJournalLine.TestField("Applies-to Doc. No."); + end; + local procedure ApplyVendorInvoiceToPmt(var GenJournalLine: Record "Gen. Journal Line"; var PmtGenJournalLine: Record "Gen. Journal Line"); var VendorLedgerEntry: Record "Vendor Ledger Entry"; @@ -434,6 +465,15 @@ codeunit 148031 "Cash Mgmt E2E Scenarios" DataExchDef.MODIFY(); end; + local procedure SetCopyInvNoToPmtRef(NewValue: Boolean); + var + PurchasesPayablesSetup: Record "Purchases & Payables Setup"; + begin + PurchasesPayablesSetup.Get(); + PurchasesPayablesSetup.Validate("Copy Inv. No. To Pmt. Ref.", NewValue); + PurchasesPayablesSetup.Modify(); + end; + [RequestPageHandler] procedure TransmitHandler(var VoidTransmitElecPmnts: TestRequestPage "Void/Transmit Elec. Pmnts"); begin diff --git a/Apps/IN/INGST/app/GSTStockTransfer/src/codeunit/GSTTransferOrderReceipt.Codeunit.al b/Apps/IN/INGST/app/GSTStockTransfer/src/codeunit/GSTTransferOrderReceipt.Codeunit.al index e159f5ac9a..83fa2bbccd 100644 --- a/Apps/IN/INGST/app/GSTStockTransfer/src/codeunit/GSTTransferOrderReceipt.Codeunit.al +++ b/Apps/IN/INGST/app/GSTStockTransfer/src/codeunit/GSTTransferOrderReceipt.Codeunit.al @@ -396,7 +396,7 @@ codeunit 18390 "GST Transfer Order Receipt" local procedure InitRevaluationEntryForGSTAndUnrealizedProfit(var ItemJournalLine: Record "Item Journal Line"; var ValueEntry: Record "Value Entry") begin InitRevaluationEntryGST(ItemJournalLine); - InitRevaluationEntryUnrealizedProfit(ItemJournalLine, ValueEntry); + InitRevaluationEntryUnrealizedProfit(ItemJournalLine); end; local procedure FillDetailLedgBufferTransfer(DocNo: Code[20]) @@ -1086,14 +1086,18 @@ codeunit 18390 "GST Transfer Order Receipt" if not TransferReceiptLine.Get(ItemJournalLine."Document No.", ItemJournalLine."Document Line No.") then exit; - InitRevaluationEntry(ItemJournalLine, (ItemJournalCustom / TransferReceiptLine."Quantity")); + if ItemLedgerEntryNo <> 0 then + InitRevaluationEntry(ItemJournalLine, (ItemJournalCustom / TransferReceiptLine."Quantity"), ItemLedgerEntryNo); end; - local procedure InitRevaluationEntryUnrealizedProfit(var ItemJournalLine: Record "Item Journal Line"; var ValueEntry: Record "Value Entry") + local procedure InitRevaluationEntryUnrealizedProfit(var ItemJournalLine: Record "Item Journal Line") var SourceCodeSetup: Record "Source Code Setup"; TransferHeader: Record "Transfer Header"; TransferLine: Record "Transfer Line"; + ItemLedgerEntry: Record "Item Ledger Entry"; + ValueEntry2: Record "Value Entry"; + ItemRegister: Record "Item Register"; TransferReceiptLine: Record "Transfer Receipt Line"; Location: Record Location; TransferPriceDiff: Decimal; @@ -1121,20 +1125,36 @@ codeunit 18390 "GST Transfer Order Receipt" RoundDiffAmt := TransferLine.Amount - (-TransferCost) else RoundDiffAmt := Round((TransferLine.Amount / TransferLine.Quantity) * TransferLine."Qty. to Receive", 0.01, '=') - (-TransferCost); - TotalTransferPriceDiff := 0; - AmntUnitCost := ValueEntry."Cost Amount (Actual)" / ValueEntry."Item Ledger Entry Quantity"; - TransferPriceDiff := Round((TransferLine."Transfer Price" / ItemJournalLine."Qty. per Unit of Measure") - AmntUnitCost); - if TransferPriceDiff <> 0 then begin - TotalTransferPriceDiff += TransferPriceDiff * ItemJournalLine.Quantity; - if (TotalTransferPriceDiff <> RoundDiffAmt) and (ItemJournalLine."Lot No." = '') then - TransferPriceDiff := TransferPriceDiff - (TotalTransferPriceDiff - RoundDiffAmt); - - InitRevaluationEntry(ItemJournalLine, TransferPriceDiff); - end; + ItemRegister.FindLast(); + ItemLedgerEntry.Reset(); + ItemLedgerEntry.SetCurrentKey("Location Code", "Posting Date", "Document No.", "Item No."); + ItemLedgerEntry.SetRange("Entry No.", ItemRegister."From Entry No.", ItemRegister."To Entry No."); + ItemLedgerEntry.SetRange("Location Code", TransferHeader."Transfer-to Code"); + ItemLedgerEntry.SetRange("Posting Date", TransferHeader."Posting Date"); + ItemLedgerEntry.SetRange("Document No.", ItemJournalLine."Document No."); + ItemLedgerEntry.SetRange("Document Line No.", TransferLine."Line No."); + ItemLedgerEntry.SetRange("Item No.", TransferLine."Item No."); + if ItemLedgerEntry.FindSet() then + repeat + TotalTransferPriceDiff := 0; + ValueEntry2.Reset(); + ValueEntry2.SetRange("Item Ledger Entry No.", ItemLedgerEntry."Entry No."); + ValueEntry2.FindFirst(); + if ValueEntry2."Item Ledger Entry Quantity" <> 0 then + AmntUnitCost := ValueEntry2."Cost Amount (Actual)" / ValueEntry2."Item Ledger Entry Quantity"; + TransferPriceDiff := Round((TransferLine."Transfer Price" / ItemJournalLine."Qty. per Unit of Measure") - AmntUnitCost); + if TransferPriceDiff <> 0 then begin + TotalTransferPriceDiff += TransferPriceDiff * ItemJournalLine.Quantity; + if (TotalTransferPriceDiff <> RoundDiffAmt) and (ItemJournalLine."Lot No." = '') then + TransferPriceDiff := TransferPriceDiff - (TotalTransferPriceDiff - RoundDiffAmt); + + InitRevaluationEntry(ItemJournalLine, TransferPriceDiff, ItemLedgerEntry."Entry No."); + end; + until ItemLedgerEntry.Next() = 0; end; - local procedure InitRevaluationEntry(var ItemJournalLine: Record "Item Journal Line"; UnitCostRevalued: Decimal) + local procedure InitRevaluationEntry(var ItemJournalLine: Record "Item Journal Line"; UnitCostRevalued: Decimal; EntryNo: Integer) var SourceCodeSetup: Record "Source Code Setup"; begin @@ -1150,7 +1170,7 @@ codeunit 18390 "GST Transfer Order Receipt" TempItemJnlLine."Value Entry Type" := TempItemJnlLine."Value Entry Type"::Revaluation; TempItemJnlLine.Validate("Item No.", ItemJournalLine."Item No."); TempItemJnlLine."Source Code" := SourceCodeSetup."Revaluation Journal"; - TempItemJnlLine.Validate("Applies-to Entry", ItemLedgerEntryNo); + TempItemJnlLine.Validate("Applies-to Entry", EntryNo); TempItemJnlLine.Validate("Unit Cost (Revalued)", (TempItemJnlLine."Unit Cost (Revalued)" + UnitCostRevalued)); TempItemJnlLine.Description := StrSubstNo(TransferReceiptNoLbl, ItemJournalLine."Document No."); TempItemJnlLine."Line No." := LineNo; diff --git a/Apps/US/HybridGP_US/app/src/Codeunits/GPCloudMigrationUS.Codeunit.al b/Apps/US/HybridGP_US/app/src/Codeunits/GPCloudMigrationUS.Codeunit.al index 3c0f713e07..97668d9e46 100644 --- a/Apps/US/HybridGP_US/app/src/Codeunits/GPCloudMigrationUS.Codeunit.al +++ b/Apps/US/HybridGP_US/app/src/Codeunits/GPCloudMigrationUS.Codeunit.al @@ -7,7 +7,6 @@ using Microsoft.Finance.VAT.Reporting; #endif using Microsoft.Purchases.Vendor; - codeunit 42004 "GP Cloud Migration US" { var diff --git a/Apps/US/IRSForms/app/Permissions/IRSFormsObjects.PermissionSet.al b/Apps/US/IRSForms/app/Permissions/IRSFormsObjects.PermissionSet.al index c5d888530a..21f85fea39 100644 --- a/Apps/US/IRSForms/app/Permissions/IRSFormsObjects.PermissionSet.al +++ b/Apps/US/IRSForms/app/Permissions/IRSFormsObjects.PermissionSet.al @@ -47,6 +47,7 @@ permissionset 10032 "IRS Forms - Objects" codeunit "IRS Forms Facade" = X, codeunit "IRS Reporting Period" = X, report "IRS 1099 Create Form Docs" = X, + report "IRS 1099 FIRE" = X, report "IRS 1099 Print" = X, report "IRS 1099 Send Email" = X; } diff --git a/Apps/US/IRSForms/app/src/Extensions/IRS1099BaseAppSubscribers.Codeunit.al b/Apps/US/IRSForms/app/src/Extensions/IRS1099BaseAppSubscribers.Codeunit.al index b2481cc7d6..4e09461569 100644 --- a/Apps/US/IRSForms/app/src/Extensions/IRS1099BaseAppSubscribers.Codeunit.al +++ b/Apps/US/IRSForms/app/src/Extensions/IRS1099BaseAppSubscribers.Codeunit.al @@ -141,7 +141,7 @@ codeunit 10032 "IRS 1099 BaseApp Subscribers" PurchHeader.Validate("IRS 1099 Reporting Period", PeriodNo); PurchHeader.Validate("IRS 1099 Form No.", IRS1099VendorFormBoxSetup."Form No."); PurchHeader.Validate("IRS 1099 Form Box No.", IRS1099VendorFormBoxSetup."Form Box No."); - if ModifyRecord then + if ModifyRecord and (PurchHeader."No." <> '') then PurchHeader.Modify(true); end; diff --git a/Apps/US/IRSForms/app/src/FIRE/HelperFIRE.Codeunit.al b/Apps/US/IRSForms/app/src/FIRE/HelperFIRE.Codeunit.al new file mode 100644 index 0000000000..26eb2363ee --- /dev/null +++ b/Apps/US/IRSForms/app/src/FIRE/HelperFIRE.Codeunit.al @@ -0,0 +1,380 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.Finance.VAT.Reporting; + +using Microsoft.Foundation.Address; +using Microsoft.Foundation.Company; +using Microsoft.Purchases.Payables; + +codeunit 10048 "Helper FIRE" +{ + Access = Internal; + InherentEntitlements = X; + InherentPermissions = X; + + var + FormBox: Record "IRS 1099 Form Box"; + FormatAddress: Codeunit "Format Address"; + PeriodNoGlobal: Text[4]; + FormBoxes: array[4, 30] of Code[10]; + Amounts: array[4, 30] of Decimal; + Totals: array[4, 30] of Decimal; + CodeNotSetupErr: Label 'The 1099 code %1 has not been setup in the initialization.', Comment = '%1 = 1099 Code'; + Unknown1099CodeErr: Label 'Invoice %1 on vendor %2 has unknown 1099 code %3.', Comment = '%1 = Invoice Entry No., %2 = Vendor No., %3 = 1099 Code'; + + procedure FillFormBoxNoArray(PeriodNo: Text[4]) + begin + PeriodNoGlobal := PeriodNo; + + // Fill in the Codes used for 1099's + Clear(FormBoxes); + + FormBoxes[1, 1] := 'MISC-01'; + FormBoxes[1, 2] := 'MISC-02'; + FormBoxes[1, 3] := 'MISC-03'; + FormBoxes[1, 4] := 'MISC-04'; + FormBoxes[1, 5] := 'MISC-05'; + FormBoxes[1, 6] := 'MISC-06'; + FormBoxes[1, 7] := 'MISC-07'; + FormBoxes[1, 8] := 'MISC-08'; + FormBoxes[1, 9] := 'MISC-09'; + FormBoxes[1, 10] := 'MISC-10'; + FormBoxes[1, 11] := 'MISC-11'; + FormBoxes[1, 12] := 'MISC-12'; + FormBoxes[1, 13] := 'MISC-13'; + FormBoxes[1, 14] := 'MISC-14'; + FormBoxes[1, 15] := 'MISC-15'; + + FormBoxes[2, 1] := 'DIV-01-A'; + FormBoxes[2, 2] := 'DIV-01-B'; + FormBoxes[2, 3] := 'DIV-02-A'; + FormBoxes[2, 5] := 'DIV-05'; + FormBoxes[2, 6] := 'DIV-02-B'; + FormBoxes[2, 7] := 'DIV-02-C'; + FormBoxes[2, 8] := 'DIV-02-D'; + FormBoxes[2, 9] := 'DIV-03'; + FormBoxes[2, 10] := 'DIV-04'; + FormBoxes[2, 11] := 'DIV-06'; + FormBoxes[2, 12] := 'DIV-07'; + FormBoxes[2, 13] := 'DIV-09'; + FormBoxes[2, 14] := 'DIV-10'; + FormBoxes[2, 15] := 'DIV-12'; + FormBoxes[2, 16] := 'DIV-02-E'; + FormBoxes[2, 17] := 'DIV-02-F'; + FormBoxes[2, 18] := 'DIV-13'; + + FormBoxes[3, 1] := 'INT-01'; + FormBoxes[3, 2] := 'INT-02'; + FormBoxes[3, 3] := 'INT-03'; + FormBoxes[3, 4] := 'INT-04'; + FormBoxes[3, 5] := 'INT-05'; + FormBoxes[3, 6] := 'INT-06'; + FormBoxes[3, 8] := 'INT-08'; + FormBoxes[3, 9] := 'INT-09'; + FormBoxes[3, 10] := 'INT-10'; + FormBoxes[3, 11] := 'INT-11'; + FormBoxes[3, 12] := 'INT-12'; + FormBoxes[3, 13] := 'INT-13'; + + FormBoxes[4, 1] := 'NEC-01'; + FormBoxes[4, 2] := 'NEC-02'; + FormBoxes[4, 4] := 'NEC-04'; + + OnRunOnAfterInitCodeValues(FormBoxes); + end; + + local procedure GetFormNo(FormTypeIndex: Integer): Code[20] + begin + case FormTypeIndex of + 1: + exit('MISC'); + 2: + exit('DIV'); + 3: + exit('INT'); + 4: + exit('NEC'); + end; + end; + + procedure GetAmt(FormBoxNo: Code[20]; FormTypeIndex: Integer; EndLine: Integer): Decimal + var + FormBoxNoIndex: Integer; + begin + FormBoxNoIndex := 1; + while (FormBoxes[FormTypeIndex, FormBoxNoIndex] <> FormBoxNo) and (FormBoxNoIndex <= EndLine) do + FormBoxNoIndex := FormBoxNoIndex + 1; + + if (FormBoxes[FormTypeIndex, FormBoxNoIndex] = FormBoxNo) and (FormBoxNoIndex <= EndLine) then + exit(Amounts[FormTypeIndex, FormBoxNoIndex]); + + Error(CodeNotSetupErr, FormBoxNo); + end; + + procedure UpdateLines(InvoiceEntry: Record "Vendor Ledger Entry"; FormTypeIndex: Integer; EndLine: Integer; FormBoxNo: Code[20]; Amount: Decimal): Integer + var + FormBoxNoIndex: Integer; + begin + FormBoxNoIndex := 1; + while (FormBoxes[FormTypeIndex, FormBoxNoIndex] <> FormBoxNo) and (FormBoxNoIndex <= EndLine) do + FormBoxNoIndex := FormBoxNoIndex + 1; + + if (FormBoxes[FormTypeIndex, FormBoxNoIndex] = FormBoxNo) and (FormBoxNoIndex <= EndLine) then begin + Amounts[FormTypeIndex, FormBoxNoIndex] += Amount; + Totals[FormTypeIndex, FormBoxNoIndex] += Amount; + end else + Error(Unknown1099CodeErr, InvoiceEntry."Entry No.", InvoiceEntry."Vendor No.", FormBoxNo); + exit(FormBoxNoIndex); // returns code index found + end; + + procedure AnyAmount(FormTypeIndex: Integer; EndLine: Integer): Boolean + var + FormBoxNoIndex: Integer; + begin + for FormBoxNoIndex := 1 to EndLine do + if FormBox.Get(PeriodNoGlobal, GetFormNo(FormTypeIndex), FormBoxes[FormTypeIndex, FormBoxNoIndex]) then begin + if FormBox."Minimum Reportable Amount" < 0.0 then + if Amounts[FormTypeIndex, FormBoxNoIndex] <> 0.0 then begin + Amounts[FormTypeIndex, FormBoxNoIndex] := -Amounts[FormTypeIndex, FormBoxNoIndex]; + exit(true); + end; + if FormBox."Minimum Reportable Amount" >= 0.0 then + if Amounts[FormTypeIndex, FormBoxNoIndex] <> 0 then begin + if Amounts[FormTypeIndex, FormBoxNoIndex] >= FormBox."Minimum Reportable Amount" then + exit(true); + Totals[FormTypeIndex, FormBoxNoIndex] := Totals[FormTypeIndex, FormBoxNoIndex] - Amounts[FormTypeIndex, FormBoxNoIndex]; + Amounts[FormTypeIndex, FormBoxNoIndex] := 0; + end; + end; + exit(false); + end; + + procedure FormatMoneyAmount(Amount: Decimal; Length: Integer): Text[250] + var + AmtStr: Text[32]; + begin + AmtStr := CopyStr(StripNonNumerics(Format(Round(Abs(Amount) * 100, 1))), 1, MaxStrLen(AmtStr)); + + // left zero-padding + if Length - StrLen(AmtStr) > 0 then + AmtStr := CopyStr('0000000000000000000' + AmtStr, 1, MaxStrLen(AmtStr)); + AmtStr := DelStr(AmtStr, 1, StrLen(AmtStr) - Length); + exit(AmtStr); + end; + + procedure FormatAmount(Amount: Integer; Length: Integer): Text[250] + var + AmtStr: Text[30]; + begin + AmtStr := Format(Amount); + + // left zero-padding + if Length - StrLen(AmtStr) > 0 then + AmtStr := CopyStr('000000000000000000' + AmtStr, 1, MaxStrLen(AmtStr)); + AmtStr := DelStr(AmtStr, 1, StrLen(AmtStr) - Length); + exit(AmtStr); + end; + + procedure StripNonNumerics(Text: Text[80]): Text[250] + begin + exit(DelChr(Text, '=', '-,. ')); + end; + + procedure EditCompanyInfo(var CompInfo: Record "Company Information") + begin + CompInfo."Federal ID No." := CopyStr(StripNonNumerics(CompInfo."Federal ID No."), 1, MaxStrLen(CompInfo."Federal ID No.")); + end; + + procedure SwitchZipCodeParts(var ZIP: Code[20]) + begin + if StrLen(ZIP) > 5 then + ZIP := PadStr(CopyStr(ZIP, 6), 5) + CopyStr(ZIP, 1, 5) + else + ZIP := CopyStr(' ' + ZIP, 1, MaxStrLen(ZIP)); + end; + + procedure FormatCompanyAddress(var CompanyInfo: Record "Company Information"; var CompanyAddress: array[8] of Text[30]) + begin + CompanyInfo.Get(); + FormatAddress.Company(CompanyAddress, CompanyInfo); + end; + + procedure BuildAddressLine(CompanyInfo: Record "Company Information"): Text[40] + var + Address3: Text[40]; + begin + // Format City/State/Zip address line + if StrLen(CompanyInfo.City + ', ' + CompanyInfo.County + ' ' + CompanyInfo."Post Code") > MaxStrLen(Address3) then + Address3 := CompanyInfo.City + else + if (CompanyInfo.City <> '') and (CompanyInfo.County <> '') then + Address3 := CopyStr(CompanyInfo.City + ', ' + CompanyInfo.County + ' ' + CompanyInfo."Post Code", 1, MaxStrLen(Address3)) + else + Address3 := CopyStr(DelChr(CompanyInfo.City + ' ' + CompanyInfo.County + ' ' + CompanyInfo."Post Code", '<>'), 1, MaxStrLen(Address3)); + exit(Address3); + end; + + procedure ClearAmts() + begin + Clear(Amounts); + end; + + procedure AmtCodes(var CodeNos: Text[12]; FormTypeIndex: Integer; EndLine: Integer) + var + ActualCodePos: array[30] of Integer; + FormBoxNoIndex: Integer; + begin + Clear(CodeNos); + + case FormTypeIndex of + 1: // MISC + for FormBoxNoIndex := 1 to EndLine do + if Amounts[FormTypeIndex, FormBoxNoIndex] <> 0.0 then + case FormBoxNoIndex of + 9: + IncrCodeNos(CodeNos, ActualCodePos, 'A', 10); // Crop Insurance Proceeds + 10: + IncrCodeNos(CodeNos, ActualCodePos, 'C', 12); // gross legal proceeds + 11: + IncrCodeNos(CodeNos, ActualCodePos, 'F', 15); // fish purchased for resale + 12: + IncrCodeNos(CodeNos, ActualCodePos, 'D', 13); // 409A deferral + 14: + IncrCodeNos(CodeNos, ActualCodePos, 'B', 11); // excess golden parachutes + 15: + IncrCodeNos(CodeNos, ActualCodePos, 'E', 14); // 409A Income + else + IncrCodeNos(CodeNos, ActualCodePos, Format(FormBoxNoIndex), FormBoxNoIndex); + end; + 2: // DIV + begin + if EndLine > 1 then + // special check for DIV complex amounts + if GetTotalOrdinaryDividendsAmt() <> 0 then + CodeNos := CopyStr(InsStr(CodeNos, Format(1), 1), 1, MaxStrLen(CodeNos)); + AmtCodesDIV(CodeNos, FormTypeIndex, 2, EndLine); + end; + 3: // INT + AmtCodesINT(CodeNos, FormTypeIndex, 1, EndLine); + 4: // NEC + CodeNos := '1'; + end; + end; + + local procedure AmtCodesDIV(var CodeNos: Text[12]; FormTypeIndex: Integer; StartLine: Integer; EndLine: Integer) + var + FormBoxNoIndex: Integer; + NewCode: Text; + begin + for FormBoxNoIndex := StartLine to EndLine do + if Amounts[FormTypeIndex, FormBoxNoIndex] <> 0.0 then begin + case FormBoxNoIndex of + 10: + NewCode := InsStr(CodeNos, 'A', FormBoxNoIndex); // FIT withheld + 12: + NewCode := InsStr(CodeNos, 'C', FormBoxNoIndex); // Foreign tax paid + 13: + NewCode := InsStr(CodeNos, 'D', FormBoxNoIndex); // Cash liquidation distributions + 14: + NewCode := InsStr(CodeNos, 'E', FormBoxNoIndex); // Noncash liquidation distributions + 15: + NewCode := InsStr(CodeNos, 'F', FormBoxNoIndex); // Exempt-interest dividends + 16: + NewCode := InsStr(CodeNos, 'G', FormBoxNoIndex); // Specified private activity bond interest dividends + 17: + NewCode := InsStr(CodeNos, 'H', FormBoxNoIndex); // Section 897 ordinary dividends + 18: + NewCode := InsStr(CodeNos, 'J', FormBoxNoIndex); // Section 897 capital gain + else + NewCode := InsStr(CodeNos, Format(FormBoxNoIndex), FormBoxNoIndex); + end; + CodeNos := CopyStr(NewCode, 1, MaxStrLen(CodeNos)); + end; + end; + + local procedure AmtCodesINT(var CodeNos: Text[12]; FormTypeIndex: Integer; StartLine: Integer; EndLine: Integer) + var + FormBoxNoIndex: Integer; + NewCode: Text; + begin + for FormBoxNoIndex := StartLine to EndLine do + if Amounts[FormTypeIndex, FormBoxNoIndex] <> 0.0 then begin + case FormBoxNoIndex of + 10: + NewCode := InsStr(CodeNos, 'A', FormBoxNoIndex); // Market discount + 11: + NewCode := InsStr(CodeNos, 'B', FormBoxNoIndex); // Bond premium + 12: + NewCode := InsStr(CodeNos, 'E', FormBoxNoIndex); // Bond premium on Treasury obligation + 13: + NewCode := InsStr(CodeNos, 'D', FormBoxNoIndex); // Bond premium on tax exempt bond + else + NewCode := InsStr(CodeNos, Format(FormBoxNoIndex), FormBoxNoIndex); + end; + CodeNos := CopyStr(NewCode, 1, MaxStrLen(CodeNos)); + end; + end; + + procedure GetTotal(FormBoxNo: Code[10]; FormTypeIndex: Integer; EndLine: Integer): Decimal + var + FormBoxNoIndex: Integer; + begin + FormBoxNoIndex := 1; + while (FormBoxes[FormTypeIndex, FormBoxNoIndex] <> FormBoxNo) and (FormBoxNoIndex <= EndLine) do + FormBoxNoIndex := FormBoxNoIndex + 1; + + if (FormBoxes[FormTypeIndex, FormBoxNoIndex] = FormBoxNo) and (FormBoxNoIndex <= EndLine) then + exit(Totals[FormTypeIndex, FormBoxNoIndex]); + + Error(CodeNotSetupErr, FormBoxNo); + end; + + procedure ClearTotals() + begin + Clear(Totals); + end; + + procedure DirectSalesCheck(FormBoxNoIndex: Integer): Boolean + begin + if FormBox.Get(PeriodNoGlobal, 'MISC', FormBoxes[1, FormBoxNoIndex]) then + if Amounts[1, FormBoxNoIndex] >= FormBox."Minimum Reportable Amount" then + exit(true) + else + exit(false); + end; + + local procedure GetTotalOrdinaryDividendsAmt(): Decimal + begin + exit(Amounts[2, 1] + Amounts[2, 2] + Amounts[2, 11] + Amounts[2, 5]); + end; + + local procedure IncrCodeNos(var CodeNos: Text[12]; var ActualCodePosArray: array[30] of Integer; AmountCode: Text[1]; ExpectedCodePos: Integer) + var + i: Integer; + ActualCodePos: Integer; + begin + if ExpectedCodePos > 2 then begin + i := ExpectedCodePos; + ActualCodePos := 0; + while (i > 2) and (ActualCodePos = 0) do begin + ActualCodePos := ActualCodePosArray[i - 1]; + i -= 1; + end; + if ActualCodePos <> 0 then + for i := (ExpectedCodePos + 1) to ArrayLen(ActualCodePosArray) do + if ActualCodePosArray[i] <> 0 then + ActualCodePosArray[i] += 1; + end; + if ActualCodePos = 0 then + ActualCodePos := StrLen(CodeNos) + 1; + CodeNos := CopyStr(InsStr(CodeNos, AmountCode, ActualCodePos), 1, MaxStrLen(CodeNos)); + ActualCodePosArray[ExpectedCodePos] := ActualCodePos + 1; + end; + + [IntegrationEvent(false, false)] + local procedure OnRunOnAfterInitCodeValues(var Codes: array[4, 30] of Code[10]); + begin + end; +} \ No newline at end of file diff --git a/Apps/US/IRSForms/app/src/FIRE/IRS1099FIRE.Report.al b/Apps/US/IRSForms/app/src/FIRE/IRS1099FIRE.Report.al new file mode 100644 index 0000000000..5f215dc56f --- /dev/null +++ b/Apps/US/IRSForms/app/src/FIRE/IRS1099FIRE.Report.al @@ -0,0 +1,1480 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.Finance.VAT.Reporting; + +using Microsoft.Finance.GeneralLedger.Journal; +using Microsoft.Foundation.Address; +using Microsoft.Foundation.Company; +using Microsoft.Purchases.Payables; +using System.Reflection; +using Microsoft.Purchases.Vendor; +using Microsoft.Utilities; +using System.Utilities; +using System.Telemetry; + +report 10039 "IRS 1099 FIRE" +{ + ApplicationArea = BasicUS; + Caption = 'IRS 1099 FIRE'; + ProcessingOnly = true; + UsageCategory = ReportsAndAnalysis; + + dataset + { + dataitem("T Record"; "Integer") + { + DataItemTableView = sorting(Number); + MaxIteration = 1; + + trigger OnAfterGetRecord() + begin + WriteTRec(); + end; + } + dataitem(InitialData; Integer) + { + DataItemTableView = sorting(Number); + MaxIteration = 1; + + trigger OnPreDataItem() + var + VendorFiltered: Record Vendor; + FormTypeIndex: Integer; + IsDirectSales: Boolean; + begin + ProgressDialog.Open(ExportingTxt + ProcessTransactionsARecTxt); + VendorsProcessed := 0; + Helper.ClearTotals(); + + VendorFiltered.CopyFilters(VendorData); + VendorTotalCount := VendorFiltered.Count(); + if VendorFiltered.FindSet() then + repeat + VendorsProcessed += 1; + UpdateProgressDialog(1, Format(Round(VendorsProcessed / VendorTotalCount * 100, 1))); + + Calc1099AmountForVendorInvoices(VendorFiltered."No.", PeriodDateGlobal); + until VendorFiltered.Next() = 0; + ProgressDialog.Close(); + + for FormTypeIndex := 1 to FormTypeCount do begin + AnyRecs[FormTypeIndex] := Helper.AnyAmount(FormTypeIndex, FormTypeLastNo[FormTypeIndex]); + Helper.AmtCodes(CodeNos[FormTypeIndex], FormTypeIndex, FormTypeLastNo[FormTypeIndex]); + // special case for 1099-MISC only + if FormTypeIndex = MiscTypeIndex then begin + InvoiceEntry.Reset(); + InvoiceEntry.SetFilter("IRS 1099 Form Box No.", IRS1099CodeFilter[MiscTypeIndex]); + IsDirectSales := + Helper.DirectSalesCheck( + Helper.UpdateLines(InvoiceEntry, MiscTypeIndex, FormTypeLastNo[MiscTypeIndex], GetFullMiscCode(7), 0.0)); + if IsDirectSales then begin + CodeNos[FormTypeIndex] := '1'; + DirectSales := '1' + end else + DirectSales := ' '; + end; + end; + end; + } + dataitem(VendorData; Vendor) + { + DataItemTableView = sorting("No."); + RequestFilterFields = "No."; + RequestFilterHeading = 'Vendor Filter'; + + trigger OnAfterGetRecord() + var + FormTypeIndex: Integer; + begin + // write IRS text lines to array for each vendor for each 1099 type + VendorsProcessed += 1; + UpdateProgressDialog(1, Format(Round(VendorsProcessed / VendorTotalCount * 100, 1))); + + Helper.ClearAmts(); + "Post Code" := CopyStr(Helper.StripNonNumerics("Post Code"), 1, MaxStrLen("Post Code")); + + Calc1099AmountForVendorInvoices("No.", PeriodDateGlobal); + + for FormTypeIndex := 1 to FormTypeCount do begin + WriteThis := Helper.AnyAmount(FormTypeIndex, FormTypeLastNo[FormTypeIndex]); + if WriteThis then begin + PayeeCount[FormTypeIndex] := PayeeCount[FormTypeIndex] + 1; + PayeeCountTotal := PayeeCountTotal + 1; + case FormTypeIndex of + MiscTypeIndex: + AddMiscBRecLine(); + DivTypeIndex: + AddDivBRecLine(); + IntTypeIndex: + AddIntBRecLine(); + NecTypeIndex: + AddNecBRecLine(); + end; + end; + end; + end; + + trigger OnPreDataItem() + var + EmptyList: List of [Text]; + FormTypeIndex: Integer; + begin + ProgressDialog.Open(ExportingTxt + ProcessTransactionsBRecTxt); + VendorTotalCount := VendorData.Count(); + VendorsProcessed := 0; + + Helper.ClearTotals(); + for FormTypeIndex := 1 to FormTypeCount do begin + Clear(EmptyList); + IRSVendorLines.Insert(FormTypeIndex, EmptyList); + end; + end; + + trigger OnPostDataItem() + begin + ProgressDialog.Close(); + end; + } + dataitem("A Record"; "Integer") + { + DataItemTableView = sorting(Number); + MaxIteration = 4; + dataitem(Vendor; Vendor) + { + DataItemTableView = sorting("No."); + } + dataitem("B Record"; Integer) + { + DataItemTableView = sorting(Number); + MaxIteration = 1; + + trigger OnAfterGetRecord() + var + VendorLinesGroup: List of [Text]; + Line: Text; + begin + if not AnyRecs[FormType] then + CurrReport.Skip(); + + VendorLinesGroup := IRSVendorLines.Get(FormType); + foreach Line in VendorLinesGroup do begin + IncrementSequenceNo(); + UpdateSequenceNoInRecLine(Line); + IRSData.Add(Line); + end; + end; + } + dataitem("C Record"; "Integer") + { + DataItemTableView = sorting(Number); + MaxIteration = 1; + + trigger OnAfterGetRecord() + begin + if not AnyRecs[FormType] then + CurrReport.Skip(); + + case FormType of + MiscTypeIndex: + WriteMISCCRec(); + DivTypeIndex: + WriteDIVCRec(); + IntTypeIndex: + WriteINTCRec(); + NecTypeIndex: + WriteNECCRec(); + end; + end; + } + + trigger OnAfterGetRecord() + begin + // 1 iteration per 1099 type + FormType := FormType + 1; + EndLine := FormTypeLastNo[FormType]; + + if AnyRecs[FormType] then begin + WriteARec(); + ARecNum := ARecNum + 1; + end else + CurrReport.Skip(); + end; + } + dataitem("F Record"; "Integer") + { + DataItemTableView = sorting(Number); + MaxIteration = 1; + + trigger OnAfterGetRecord() + begin + WriteFRec(); + end; + + trigger OnPostDataItem() + var + FirstLineStart: Text; + FirstLineEnd: Text; + PayeeTotalStr: Text[8]; + begin + // insert payee totals + FirstLineStart := IRSData.Get(1).Substring(1, 295); + FirstLineEnd := IRSData.Get(1).Substring(304); // skip 8 chars for the total + PayeeTotalStr := CopyStr(Helper.FormatAmount(PayeeCountTotal, MaxStrLen(PayeeTotalStr)), 1, MaxStrLen(PayeeTotalStr)); + IRSData.Set(1, FirstLineStart + PayeeTotalStr + FirstLineEnd); + end; + + trigger OnPreDataItem() + begin + if not AnyRecs[FormType] then + CurrReport.Skip(); + end; + } + } + + requestpage + { + SaveValues = true; + + layout + { + area(content) + { + group(Options) + { + Caption = 'Options'; + field(YearField; Year) + { + ApplicationArea = BasicUS; + Caption = 'Calendar Year'; + ToolTip = 'Specifies the tax year for the 1099 forms that you want to print. The default is the work date year. The taxes may apply to the previous calendar year so you may want to change this date if nothing prints.'; + + trigger OnValidate() + begin + if (Year < 1980) or (Year > 2060) then + Error(IncorrectYearErr); + end; + } + field(TCCField; TCC) + { + ApplicationArea = BasicUS; + Caption = 'Transmitter Control Code'; + ToolTip = 'Specifies the control code of the transmitter that is used to electronically file 1099 forms.'; + + trigger OnValidate() + begin + if TCC = '' then + Error(EmptyTCCErr); + end; + } + group(TransmitterInformation) + { + Caption = 'Transmitter Information'; + field(TransmitterInfoName; TransmitterInfo.Name) + { + ApplicationArea = BasicUS; + Caption = 'Transmitter Name'; + ToolTip = 'Specifies the name of the transmitter that is used to electronically file 1099 forms.'; + } + field(TransmitterInfoAddress; TransmitterInfo.Address) + { + ApplicationArea = BasicUS; + Caption = 'Street Address'; + ToolTip = 'Specifies the address of the vendor.'; + } + field(TransmitterInfoCity; TransmitterInfo.City) + { + ApplicationArea = BasicUS; + Caption = 'City'; + ToolTip = 'Specifies the city in the vendor''s address.'; + } + field(TransmitterInfoCounty; TransmitterInfo.County) + { + ApplicationArea = BasicUS; + Caption = 'State'; + ToolTip = 'Specifies the state as a part of the address.'; + } + field(TransmitterInfoPostCode; TransmitterInfo."Post Code") + { + ApplicationArea = BasicUS; + Caption = 'ZIP Code'; + ToolTip = 'Specifies the vendor''s ZIP code as a part of the address.'; + } + field(TransmitterInfoFederalIDNo; TransmitterInfo."Federal ID No.") + { + ApplicationArea = BasicUS; + Caption = 'Employer ID'; + ToolTip = 'Specifies the employer at the vendor.'; + } + field(ContactNameField; ContactName) + { + ApplicationArea = BasicUS; + Caption = 'Contact Name'; + ToolTip = 'Specifies the name of the contact at the vendor.'; + + trigger OnValidate() + begin + if ContactName = '' then + Error(EmptyContactNameErr); + end; + } + field(ContactPhoneNoField; ContactPhoneNo) + { + ApplicationArea = BasicUS; + Caption = 'Contact Phone No.'; + ToolTip = 'Specifies the phone number of the contact at the vendor.'; + + trigger OnValidate() + begin + if ContactPhoneNo = '' then + Error(EmptyContactPhoneNoErr); + end; + } + field(ContactEmailField; ContactEmail) + { + ApplicationArea = BasicUS; + Caption = 'Contact E-Mail'; + ToolTip = 'Specifies the email address of the contact at the vendor.'; + } + } + field(bTestFileField; bTestFile) + { + ApplicationArea = BasicUS; + Caption = 'Test File'; + ToolTip = 'Specifies you want to print a test file of the information that will be filed electronically.'; + + trigger OnValidate() + begin + bTestFileOnAfterValidate(); + end; + } + group(VendorInformation) + { + Caption = 'Vendor Information'; + field(VendIndicatorField; VendIndicator) + { + ApplicationArea = BasicUS; + Caption = 'Vendor Indicator'; + OptionCaption = 'Vendor Software,In-House Software'; + ToolTip = 'Specifies the type of vendor indicator that you want to use, including Vendor Software and In-House Software.'; + } + field(VendorInfoName; TempVendorInfo.Name) + { + ApplicationArea = BasicUS; + Caption = 'Vendor Name'; + ToolTip = 'Specifies the vendor''s name.'; + + trigger OnValidate() + begin + if TempVendorInfo.Name = '' then + Error(EmptyVendorInfoErr); + end; + } + field(VendorInfoAddress; TempVendorInfo.Address) + { + ApplicationArea = BasicUS; + Caption = 'Vendor Street Address'; + ToolTip = 'Specifies the vendor''s address.'; + + trigger OnValidate() + begin + if TempVendorInfo.Address = '' then + Error(EmptyVendorInfoErr); + end; + } + field(VendorInfoCity; TempVendorInfo.City) + { + ApplicationArea = BasicUS; + Caption = 'Vendor City'; + ToolTip = 'Specifies the vendors city as a part of the address.'; + + trigger OnValidate() + begin + if TempVendorInfo.City = '' then + Error(EmptyVendorInfoErr); + end; + } + field(VendorInfoCounty; TempVendorInfo.County) + { + ApplicationArea = BasicUS; + Caption = 'Vendor State'; + ToolTip = 'Specifies the vendor''s state as a part of the address.'; + + trigger OnValidate() + begin + if TempVendorInfo.County = '' then + Error(EmptyVendorInfoErr); + end; + } + field(VendorInfoPostCode; TempVendorInfo."Post Code") + { + ApplicationArea = BasicUS; + Caption = 'Vendor ZIP Code'; + ToolTip = 'Specifies the vendor''s ZIP code as a part of the address.'; + + trigger OnValidate() + begin + if TempVendorInfo."Post Code" = '' then + Error(EmptyVendorInfoErr); + end; + } + field(VendContactNameField; VendContactName) + { + ApplicationArea = BasicUS; + Caption = 'Vendor Contact Name'; + ToolTip = 'Specifies the name of the contact at the vendor.'; + + trigger OnValidate() + begin + if VendContactName = '' then + Error(EmptyVendContNameErr); + end; + } + field(VendContactPhoneNoField; VendContactPhoneNo) + { + ApplicationArea = BasicUS; + Caption = 'Vendor Contact Phone No.'; + ToolTip = 'Specifies the phone number of the contact at the vendor.'; + + trigger OnValidate() + begin + if VendContactPhoneNo = '' then + Error(EmptyVendContPhoneNoErr); + end; + } + field(VendorInfoEmail; TempVendorInfo."E-Mail") + { + ApplicationArea = BasicUS; + Caption = 'Vendor E-Mail'; + ToolTip = 'Specifies the vendor''s email address.'; + + trigger OnValidate() + begin + if TempVendorInfo."E-Mail" = '' then + Error(EmptyVendorInfoErr); + end; + } + } + } + } + } + + actions + { + } + + trigger OnOpenPage() + begin + Year := Date2DMY(WorkDate(), 3); /*default to current working year*/ + CompanyInfo.Get(); + Helper.EditCompanyInfo(CompanyInfo); + TransmitterInfo := CompanyInfo; + Helper.EditCompanyInfo(CompanyInfo); + + end; + } + + labels + { + } + + trigger OnInitReport() + begin + FeatureTelemetry.LogUsage('0000ODO', FIRE1099FeatureNameTxt, RunMagMediaReportMsg); + TestFile := ' '; + PriorYear := ' '; + SequenceNo := 0; + end; + + trigger OnPostReport() + var + TempBlob: Codeunit "Temp Blob"; + TypeHelper: Codeunit "Type Helper"; + InStream: InStream; + BlobOutStream: OutStream; + IRSDataLine: Text; + CRLF: Text[2]; + begin + CRLF := TypeHelper.CRLFSeparator(); + TempBlob.CreateOutStream(BlobOutStream); + foreach IRSDataLine in IRSData do + BlobOutStream.WriteText(IRSDataLine + CRLF); + OnBeforeDownloadFile(TempBlob); + + TempBlob.CreateInStream(InStream); + if FileName = '' then + FileName := ClientFileNameTxt; + DownloadFromStream(InStream, '', '', '*.txt', FileName); + end; + + trigger OnPreReport() + begin + if TCC = '' then + Error(EmptyTCCErr); + if ContactPhoneNo = '' then + Error(EmptyContactPhoneNoErr); + if ContactName = '' then + Error(EmptyContactNameErr); + if VendContactName = '' then + Error(EmptyVendContNameErr); + if VendContactPhoneNo = '' then + Error(EmptyVendContPhoneNoErr); + if TempVendorInfo.Name = '' then + Error(EmptyVendorInfoErr); + if TempVendorInfo.Address = '' then + Error(EmptyVendorInfoErr); + if TempVendorInfo.City = '' then + Error(EmptyVendorInfoErr); + if TempVendorInfo.County = '' then + Error(EmptyVendorInfoErr); + if TempVendorInfo."Post Code" = '' then + Error(EmptyVendorInfoErr); + if TempVendorInfo."E-Mail" = '' then + Error(EmptyVendorInfoErr); + + FormType := 0; + + // Create date range which covers the entire calendar year + PeriodDateGlobal[1] := DMY2Date(1, 1, Year); + PeriodDateGlobal[2] := DMY2Date(31, 12, Year); + + Clear(PayeeCount); + Clear(ARecNum); + + FormTypeCount := 4; + MiscTypeIndex := 1; + DivTypeIndex := 2; + IntTypeIndex := 3; + NecTypeIndex := 4; + FormTypeLastNo[MiscTypeIndex] := 17; + FormTypeLastNo[DivTypeIndex] := 18; + FormTypeLastNo[IntTypeIndex] := 13; + FormTypeLastNo[NecTypeIndex] := 4; + IRS1099CodeFilter[MiscTypeIndex] := 'MISC-..MISC-99'; + IRS1099CodeFilter[DivTypeIndex] := 'DIV-..DIV-99'; + IRS1099CodeFilter[IntTypeIndex] := 'INT-..INT-99'; + IRS1099CodeFilter[NecTypeIndex] := 'NEC-..NEC-99'; + ReturnType[MiscTypeIndex] := 'A '; + ReturnType[DivTypeIndex] := '1 '; + ReturnType[IntTypeIndex] := '6 '; + ReturnType[NecTypeIndex] := 'NE'; + Helper.FillFormBoxNoArray(Format(Year)); + end; + + var + CompanyInfo: Record "Company Information"; + TransmitterInfo: Record "Company Information"; + TempVendorInfo: Record "Company Information" temporary; + TempAppliedEntry: Record "Vendor Ledger Entry" temporary; + InvoiceEntry: Record "Vendor Ledger Entry"; + EntryAppMgt: Codeunit "Entry Application Management"; + FeatureTelemetry: Codeunit "Feature Telemetry"; + Helper: Codeunit "Helper FIRE"; + IRSData: List of [Text]; + PeriodDateGlobal: array[2] of Date; + Year: Integer; + DirectSales: Text[1]; + ReturnType: array[4] of Text[2]; + CodeNos: array[4] of Text[12]; + WriteThis: Boolean; + AnyRecs: array[4] of Boolean; + MiscTypeIndex: Integer; + DivTypeIndex: Integer; + IntTypeIndex: Integer; + NecTypeIndex: Integer; + FormTypeCount: Integer; + VendorTotalCount: Integer; + VendorsProcessed: Integer; + FormTypeLastNo: array[4] of Integer; + IRS1099CodeFilter: array[4] of Text; + IRSVendorLines: List of [List of [Text]]; + EndLine: Integer; + Invoice1099Amount: Decimal; + FormType: Integer; + TestFile: Text[1]; + PriorYear: Text[1]; + TCC: Code[5]; + ContactName: Text[40]; + ContactPhoneNo: Text[30]; + ContactEmail: Text[35]; + VendContactName: Text[40]; + VendContactPhoneNo: Text[30]; + PayeeCount: array[4] of Integer; + PayeeCountTotal: Integer; + ARecNum: Integer; + bTestFile: Boolean; + ProgressDialog: Dialog; + VendIndicator: Option "Vendor Software","In-House Software"; + SequenceNo: Integer; + EmptyContactPhoneNoErr: Label 'You must enter the phone number of the person to be contacted if IRS/MCC encounters problems with the file or transmission.'; + EmptyContactNameErr: Label 'You must enter the name of the person to be contacted if IRS/MCC encounters problems with the file or transmission.'; + EmptyVendContPhoneNoErr: Label 'You must enter the phone number of the person to be contacted if IRS/MCC has any software questions.'; + EmptyVendContNameErr: Label 'You must enter the name of the person to be contacted if IRS/MCC has any software questions.'; + EmptyTCCErr: Label 'You must enter the Transmitter Control Code assigned to you by the IRS.'; + EmptyVendorInfoErr: Label 'You must enter all software vendor address information.'; + IncorrectYearErr: Label 'You must enter a valid year, eg 1993.'; + ClientFileNameTxt: Label 'IRSTAX.txt'; + ExportingTxt: Label 'Exporting...\'; + ProcessTransactionsARecTxt: label 'Processing transactions for A records: #1###', Comment = '#1 - percent of processed vendors'; + ProcessTransactionsBRecTxt: label 'Processing transactions for B records: #1###', Comment = '#1 - percent of processed vendors'; + FIRE1099FeatureNameTxt: Label 'IRS Forms FIRE', Locked = true; + RunMagMediaReportMsg: Label 'Run IRS 1099 FIRE report', Locked = true; + MiscCodeTok: Label 'MISC-', Locked = true; + NecCodeTok: Label 'NEC-', Locked = true; + HashTagTok: Label '#', Locked = true; + BlankTagTok: Label ' ', Locked = true; + FileName: Text; + + procedure ProcessVendorInvoices(VendorNo: Code[20]; PeriodDate: array[2] of Date) + var + IRS1099Adjustment: Record "IRS 1099 Vendor Form Box Adj."; + TempIRS1099Adjustment: Record "IRS 1099 Vendor Form Box Adj." temporary; + begin + // search for invoices paid off by this payment + EntryAppMgt.GetAppliedVendorEntries(TempAppliedEntry, VendorNo, PeriodDate, true); + // search for invoices with 1099 amounts + TempAppliedEntry.SetFilter("Document Type", '%1|%2', TempAppliedEntry."Document Type"::Invoice, TempAppliedEntry."Document Type"::"Credit Memo"); + TempAppliedEntry.SetFilter("IRS 1099 Reporting Amount", '<>0'); + case FormType of + 1: + TempAppliedEntry.SetRange("IRS 1099 Form Box No.", 'MISC-', 'MISC-99'); + 2: + TempAppliedEntry.SetRange("IRS 1099 Form Box No.", 'DIV-', 'DIV-99'); + 3: + TempAppliedEntry.SetRange("IRS 1099 Form Box No.", 'INT-', 'INT-99'); + 4: + TempAppliedEntry.SetRange("IRS 1099 Form Box No.", 'NEC-', 'NEC-99'); + end; + if TempAppliedEntry.FindSet() then + repeat + Calculate1099Amount(TempAppliedEntry, TempAppliedEntry."Amount to Apply"); + if GetAdjustmentRec(IRS1099Adjustment, TempAppliedEntry) then begin + TempIRS1099Adjustment := IRS1099Adjustment; + if not TempIRS1099Adjustment.Find() then begin + Helper.UpdateLines( + TempAppliedEntry, FormType, EndLine, TempAppliedEntry."IRS 1099 Form Box No.", IRS1099Adjustment.Amount); + TempIRS1099Adjustment.Insert(); + end; + end; + until TempAppliedEntry.Next() = 0; + end; + + procedure Calculate1099Amount(VendorLedgerEntry: Record "Vendor Ledger Entry"; AppliedAmount: Decimal) + begin + VendorLedgerEntry.CalcFields(Amount); + Invoice1099Amount := -AppliedAmount * VendorLedgerEntry."IRS 1099 Reporting Amount" / VendorLedgerEntry.Amount; + Helper.UpdateLines(VendorLedgerEntry, FormType, EndLine, VendorLedgerEntry."IRS 1099 Form Box No.", Invoice1099Amount); + end; + + local procedure Calc1099AmountForVendorInvoices(VendorNo: Code[20]; StartEndDate: array[2] of Date) + var + TempApplVendorLedgerEntry: Record "Vendor Ledger Entry" temporary; + TempIRS1099Adjustment: Record "IRS 1099 Vendor Form Box Adj." temporary; + IRS1099Adjustment: Record "IRS 1099 Vendor Form Box Adj."; + FormTypeIndex: Integer; + begin + EntryAppMgt.GetAppliedVendorEntries(TempApplVendorLedgerEntry, VendorNo, StartEndDate, true); + for FormTypeIndex := 1 to FormTypeCount do begin + TempApplVendorLedgerEntry.SetFilter("Document Type", '%1|%2', Enum::"Gen. Journal Document Type"::Invoice, Enum::"Gen. Journal Document Type"::"Credit Memo"); + TempApplVendorLedgerEntry.SetFilter("IRS 1099 Reporting Amount", '<>0'); + TempApplVendorLedgerEntry.SetFilter("IRS 1099 Form Box No.", IRS1099CodeFilter[FormTypeIndex]); + if TempApplVendorLedgerEntry.FindSet() then + repeat + Calculate1099Amount(TempApplVendorLedgerEntry, FormTypeIndex); + if GetAdjustmentRec(IRS1099Adjustment, TempApplVendorLedgerEntry) then begin + TempIRS1099Adjustment := IRS1099Adjustment; + if not TempIRS1099Adjustment.Find() then begin + Helper.UpdateLines( + TempApplVendorLedgerEntry, FormTypeIndex, FormTypeLastNo[FormTypeIndex], TempApplVendorLedgerEntry."IRS 1099 Form Box No.", IRS1099Adjustment.Amount); + TempIRS1099Adjustment.Insert(); + end; + end; + until TempApplVendorLedgerEntry.Next() = 0; + end; + end; + + local procedure Calculate1099Amount(AppliedVendorLedgerEntry: Record "Vendor Ledger Entry"; FormTypeIndex: Integer) + begin + AppliedVendorLedgerEntry.CalcFields(Amount); + Invoice1099Amount := -AppliedVendorLedgerEntry."Amount to Apply" * AppliedVendorLedgerEntry."IRS 1099 Reporting Amount" / AppliedVendorLedgerEntry.Amount; + Helper.UpdateLines(AppliedVendorLedgerEntry, FormTypeIndex, FormTypeLastNo[FormTypeIndex], AppliedVendorLedgerEntry."IRS 1099 Form Box No.", Invoice1099Amount); + end; + + local procedure GetForeignEntityIndicator(TempVendorInformation: Record "Company Information" temporary): Text[1] + var + PostCode: Record "Post Code"; + begin + PostCode.SetRange(Code, TempVendorInformation."Post Code"); + PostCode.SetRange(City, TempVendorInformation.City); + if PostCode.FindFirst() then + if PostCode."Country/Region Code" in ['US', 'USA'] then + exit(' ') + else + exit('1'); + exit(' '); + end; + + procedure WriteTRec() + begin + // T Record - 1 per transmission, 750 length + IncrementSequenceNo(); + IRSData.Add('T' + + StrSubstNo('#1##', CopyStr(Format(Year), 1, 4)) + + StrSubstNo(PriorYear) + // Prior Year Indicator + StrSubstNo('#1#######', Helper.StripNonNumerics(TransmitterInfo."Federal ID No.")) + + StrSubstNo('#1###', TCC) + // Transmitter Control Code + StrSubstNo(' ') + // replacement character + StrSubstNo(' ') + // blank 5 + StrSubstNo(TestFile) + + StrSubstNo(' ') + // Foreign Entity Code + StrSubstNo('#1##############################################################################', + TransmitterInfo.Name) + + StrSubstNo('#1################################################', CompanyInfo.Name) + + StrSubstNo(' ') + // 2nd Payer Name + StrSubstNo('#1######################################', CompanyInfo.Address) + + StrSubstNo('#1######################################', CompanyInfo.City) + + StrSubstNo('#1', CopyStr(CompanyInfo.County, 1, 2)) + + StrSubstNo('#1#######', Helper.StripNonNumerics(CompanyInfo."Post Code")) + + StrSubstNo(' ') + // blank 15 + StrSubstNo('#1######', Helper.FormatAmount(PayeeCountTotal, 8)) + // Payee total + StrSubstNo('#1######################################', ContactName) + + StrSubstNo('#1#############', ContactPhoneNo) + + StrSubstNo('#1################################################', ContactEmail) + // 359-408 + StrSubstNo(' ') + // Tape file indicator + StrSubstNo('#1####', ' ') + // place for media number (not required) + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo('#1######', Helper.FormatAmount(SequenceNo, 8)) + // sequence number for all rec types + StrSubstNo(' ') + + StrSubstNo('%1', CopyStr(Format(VendIndicator), 1, 1)) + + StrSubstNo('#1######################################', TempVendorInfo.Name) + + StrSubstNo('#1######################################', TempVendorInfo.Address) + + StrSubstNo('#1######################################', TempVendorInfo.City) + + StrSubstNo('#1', CopyStr(TempVendorInfo.County, 1, 2)) + + StrSubstNo('#1#######', Helper.StripNonNumerics(TempVendorInfo."Post Code")) + + StrSubstNo('#1######################################', VendContactName) + + StrSubstNo('#1#############', VendContactPhoneNo) + + StrSubstNo('#1##################', TempVendorInfo."E-Mail") + // 20 chars + StrSubstNo(' ') + + StrSubstNo('%1', GetForeignEntityIndicator(TempVendorInfo)) + // position 740 + StrSubstNo(' ')); + end; + + procedure WriteARec() + begin + // A Record - 1 per Payer per 1099 type, 750 length + IncrementSequenceNo(); + IRSData.Add('A' + + StrSubstNo('#1##', CopyStr(Format(Year), 1, 4)) + + StrSubstNo(' ') + // 6 blanks + StrSubstNo('#1#######', Helper.StripNonNumerics(CompanyInfo."Federal ID No.")) + // TIN + StrSubstNo('#1##', ' ') + // Payer Name Control + StrSubstNo(' ') + + StrSubstNo(ReturnType[FormType]) + + StrSubstNo('#1##############', CodeNos[FormType]) + // Amount Codes 16 + StrSubstNo(' ') + // 8 blanks + StrSubstNo(' ') + // Foreign Entity Code + StrSubstNo('#1######################################', CompanyInfo.Name) + + StrSubstNo(' ') + // 2nd Payer Name + StrSubstNo(' ') + // Transfer Agent Indicator + StrSubstNo('#1######################################', CompanyInfo.Address) + + StrSubstNo('#1######################################', CompanyInfo.City) + + StrSubstNo('#1', CompanyInfo.County) + + StrSubstNo('#1#######', Helper.StripNonNumerics(CompanyInfo."Post Code")) + + StrSubstNo('#1#############', CompanyInfo."Phone No.") + + StrSubstNo(' ') + // blank 50 + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo('#1######', Helper.FormatAmount(SequenceNo, 8)) + // sequence number for all rec types + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ')); + end; + + procedure WriteMiscBRec() + begin + IncrementSequenceNo(); + IRSData.Add(GetMiscBRec()); + end; + + local procedure AddMiscBRecLine() + begin + IRSVendorLines.Get(MiscTypeIndex).Add(GetMiscBRec()); + end; + + local procedure GetMiscBRec(): Text + var + FormTypeIndex: Integer; + LastNo: Integer; + begin + FormTypeIndex := MiscTypeIndex; + LastNo := FormTypeLastNo[MiscTypeIndex]; + + exit('B' + + StrSubstNo('#1##', CopyStr(Format(Year), 1, 4)) + + StrSubstNo(' ') + // correction indicator + StrSubstNo(' ') + // name control + StrSubstNo(' ') + // Type of TIN + StrSubstNo('#1#######', Helper.StripNonNumerics(VendorData."Federal ID No.")) + // TIN + StrSubstNo('#1##################', VendorData."No.") + // Payer's Payee Account # + StrSubstNo(' ') + // Blank 14 + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt(GetFullMiscCode(1), FormTypeIndex, LastNo), 12)) + // Payment 1 + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt(GetFullMiscCode(2), FormTypeIndex, LastNo), 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt(GetFullMiscCode(3), FormTypeIndex, LastNo), 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt(GetFullMiscCode(4), FormTypeIndex, LastNo), 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt(GetFullMiscCode(5), FormTypeIndex, LastNo), 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt(GetFullMiscCode(6), FormTypeIndex, LastNo), 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount( + Helper.GetAmt(GetFullMiscCode(8), FormTypeIndex, LastNo), 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt(GetFullMiscCode(9), FormTypeIndex, LastNo), 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt(GetFullMiscCode(14), FormTypeIndex, LastNo), 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt(GetFullMiscCode(10), FormTypeIndex, LastNo), 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount( + Helper.GetAmt(GetFullMiscCode(12), FormTypeIndex, LastNo), 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount( + Helper.GetAmt(GetFullMiscCode(11), FormTypeIndex, LastNo), 12)) + // Fish purchased for resale + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(' ') + // blank 16 + StrSubstNo(' ') + // Foreign Country Indicator + StrSubstNo('#1######################################', VendorData.Name) + + StrSubstNo('#1######################################', VendorData."Name 2") + + StrSubstNo('#1######################################', VendorData.Address) + + StrSubstNo(' ') + // blank 40 + StrSubstNo('#1######################################', VendorData.City) + + StrSubstNo('#1', VendorData.County) + + StrSubstNo('#1#######', VendorData."Post Code") + + StrSubstNo(' ') + + StrSubstNo('#1######', Helper.FormatAmount(SequenceNo, 8)) + // sequence number for all rec types + StrSubstNo(' ') + + StrSubstNo(' ') + // Second TIN Notice (Optional) (544) + StrSubstNo(' ') + // Blank (545-546) + StrSubstNo(DirectSales) + // Direct Sales Indicator (547) + StrSubstNo(Format(VendorData."FATCA Requirement", 0, 2)) + // FATCA Filing Requirement Indicator (548) + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + // Blank (549-662) + StrSubstNo(' ') + + StrSubstNo(' ') + // Special Data Entries (663-722) + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt(GetFullMiscCode(15), FormTypeIndex, LastNo), 12)) + // State Income Tax Withheld (723-734) + StrSubstNo('#1##########', Helper.FormatMoneyAmount(0, 12)) + // Local Income Tax Withheld (735-746) + StrSubstNo(' ') + // Combined Federal/State Code (747-748) + StrSubstNo(' ') // Blank (749-750) + ); + end; + + procedure WriteDivBRec() + begin + IncrementSequenceNo(); + IRSData.Add(GetDivBRec()); + end; + + local procedure AddDivBRecLine() + begin + IRSVendorLines.Get(DivTypeIndex).Add(GetDivBRec()); + end; + + local procedure GetDivBRec(): Text + var + FormTypeIndex: Integer; + LastNo: Integer; + begin + FormTypeIndex := DivTypeIndex; + LastNo := FormTypeLastNo[DivTypeIndex]; + + exit('B' + // Type (1) + StrSubstNo('#1##', CopyStr(Format(Year), 1, 4)) + // Payment Year (2-5) + StrSubstNo(' ') + // Corrected Return Indicator (6) + StrSubstNo(' ') + // Name Control (7-10) + StrSubstNo(' ') + // Type of TIN (11) + StrSubstNo('#1#######', Helper.StripNonNumerics(VendorData."Federal ID No.")) + // Payee's TIN (12-20) + StrSubstNo('#1##################', VendorData."No.") + // Payer's Account Number for Payee (21-40) + StrSubstNo(' ') + // Payer's Office Code (41-44) and Blank (45-54) + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('DIV-01-A', FormTypeIndex, LastNo) + + Helper.GetAmt('DIV-01-B', FormTypeIndex, LastNo) + + Helper.GetAmt('DIV-05', FormTypeIndex, LastNo) + + Helper.GetAmt('DIV-06', FormTypeIndex, LastNo), 12)) + // ordinary dividends 1 (55-66) + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('DIV-01-B', FormTypeIndex, LastNo), 12)) + // 2 (67-78) + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('DIV-02-A', FormTypeIndex, LastNo) + + Helper.GetAmt('DIV-02-B', FormTypeIndex, LastNo) + + Helper.GetAmt('DIV-02-C', FormTypeIndex, LastNo) + + Helper.GetAmt('DIV-02-D', FormTypeIndex, LastNo), 12)) + // total capital gains 3 (79-90) + StrSubstNo('#1##########', Helper.FormatMoneyAmount(0, 12)) + // 4 (91-102) + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('DIV-05', FormTypeIndex, LastNo), 12)) + // 5-Section 199A Dividends (103-114) + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('DIV-02-B', FormTypeIndex, LastNo), 12)) + // 6-Unrecaptured Section 1250 gain (115-126) + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('DIV-02-C', FormTypeIndex, LastNo), 12)) + // 7-Section 1202 gain (127-138) + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('DIV-02-D', FormTypeIndex, LastNo), 12)) + // 8-Collectibles (28%) gain (139-150) + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('DIV-03', FormTypeIndex, LastNo), 12)) + // 9-Nondividend distributions (151-162) + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('DIV-04', FormTypeIndex, LastNo), 12)) + // fed W/H A (163-174) + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('DIV-06', FormTypeIndex, LastNo), 12)) + // investment. expenses B (175-186) + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('DIV-07', FormTypeIndex, LastNo), 12)) + // Foreign Taxc Paid C (187-198) + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('DIV-09', FormTypeIndex, LastNo), 12)) + // cash liquidation D (199-210) + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('DIV-10', FormTypeIndex, LastNo), 12)) + // non-cash liquidation E (211-222) + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('DIV-12', FormTypeIndex, LastNo), 12)) + // Exempt-interest dividends F (223-234) + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('DIV-13', FormTypeIndex, LastNo), 12)) + // Specified private activity bond... G (235-246) + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount( + Helper.GetAmt('DIV-02-E', FormTypeIndex, LastNo), 12)) + // Section 897 Ordinary Dividens (247-258) + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount( + Helper.GetAmt('DIV-02-F', FormTypeIndex, LastNo), 12)) + // Section 897 Capital Gains (259-270) + GetBlankedStringWithLength(16) + // blank 16 (271-286) + GetBlankedStringWithLength(1) + // Foreign Country Indicator (287) + StrSubstNo(GetHashTagStringWithLength(40), GetFullVendorName(VendorData)) + + GetBlankedStringWithLength(40) + // blank 40 + StrSubstNo(GetHashTagStringWithLength(40), VendorData.Address) + + GetBlankedStringWithLength(40) + // blank 40 + StrSubstNo(GetHashTagStringWithLength(40), VendorData.City) + + StrSubstNo('#1', VendorData.County) + + StrSubstNo('#1#######', VendorData."Post Code") + + StrSubstNo(' ') + + StrSubstNo('#1######', Helper.FormatAmount(SequenceNo, 8)) + // sequence (500-507) number for all rec types + StrSubstNo(' ') + + StrSubstNo(' ') + // Second TIN Notice (Optional) (544) + StrSubstNo(' ') + // Blank (545-546) + StrSubstNo(' ') + // Foreign Country or U.S. Possession (547-586) + StrSubstNo(Format(VendorData."FATCA Requirement", 0, 2)) + // FATCA Filing Requirement Indicator (587) + StrSubstNo(' ') + + StrSubstNo(' ') + // Blank (588-662) + StrSubstNo(' ') + + StrSubstNo(' ') + // Special Data Entries (663-722) + StrSubstNo('#1##########', Helper.FormatMoneyAmount(0, 12)) + // State Income Tax Withheld (723-734) + StrSubstNo('#1##########', Helper.FormatMoneyAmount(0, 12)) + // Local Income Tax Withheld (735-746) + StrSubstNo(' ') + // Combined Federal/State Code (747-748) + StrSubstNo(' ') // Blank (749-750) + ); + end; + + procedure WriteIntBRec() + begin + IncrementSequenceNo(); + IRSData.Add(GetIntBRec()); + end; + + local procedure AddIntBRecLine() + begin + IRSVendorLines.Get(IntTypeIndex).Add(GetIntBRec()); + end; + + local procedure GetIntBRec(): Text + var + FormTypeIndex: Integer; + LastNo: Integer; + begin + FormTypeIndex := IntTypeIndex; + LastNo := FormTypeLastNo[IntTypeIndex]; + + exit('B' + + StrSubstNo('#1##', CopyStr(Format(Year), 1, 4)) + + StrSubstNo(' ') + // correction indicator + StrSubstNo(' ') + // name control + StrSubstNo(' ') + // Type of TIN + StrSubstNo('#1#######', Helper.StripNonNumerics(VendorData."Federal ID No.")) + // TIN + StrSubstNo('#1##################', VendorData."No.") + // Payer's Payee Account # + StrSubstNo(' ') + // Blank 14 + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('INT-01', FormTypeIndex, LastNo), 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('INT-02', FormTypeIndex, LastNo), 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('INT-03', FormTypeIndex, LastNo), 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('INT-04', FormTypeIndex, LastNo), 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('INT-05', FormTypeIndex, LastNo), 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('INT-06', FormTypeIndex, LastNo), 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('INT-08', FormTypeIndex, LastNo) + + Helper.GetAmt('INT-09', FormTypeIndex, LastNo), 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('INT-09', FormTypeIndex, LastNo), 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('INT-10', FormTypeIndex, LastNo), 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('INT-11', FormTypeIndex, LastNo), 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('INT-13', FormTypeIndex, LastNo), 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount( + Helper.GetAmt('INT-12', FormTypeIndex, LastNo), 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo('#1##########', Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(' ') + // blank 16 + StrSubstNo(' ') + // Foreign Country Indicator + StrSubstNo('#1######################################', VendorData.Name) + + StrSubstNo('#1######################################', VendorData."Name 2") + + StrSubstNo('#1######################################', VendorData.Address) + + StrSubstNo(' ') + // blank 40 + StrSubstNo('#1######################################', VendorData.City) + + StrSubstNo('#1', VendorData.County) + + StrSubstNo('#1#######', VendorData."Post Code") + + StrSubstNo(' ') + + StrSubstNo('#1######', Helper.FormatAmount(SequenceNo, 8)) + // sequence number for all rec types + StrSubstNo(' ') + + StrSubstNo(' ') + // Second TIN Notice (Optional) (544) + StrSubstNo(' ') + // Blank (545-546) + StrSubstNo(' ') + // Foreign Country or U.S. Possession (547-586) + StrSubstNo(' ') + // CUSIP Number (587-599) + StrSubstNo(Format(VendorData."FATCA Requirement", 0, 2)) + // FATCA Filing Requirement Indicator (600) + StrSubstNo(' ') + + StrSubstNo(' ') + // Blank (601-662) + StrSubstNo(' ') + + StrSubstNo(' ') + // Special Data Entries (663-722) + StrSubstNo('#1##########', Helper.FormatMoneyAmount(0, 12)) + // State Income Tax Withheld (723-734) + StrSubstNo('#1##########', Helper.FormatMoneyAmount(0, 12)) + // Local Income Tax Withheld (735-746) + StrSubstNo(' ') + // Combined Federal/State Code (747-748) + StrSubstNo(' ') // Blank (749-750) + ); + end; + + procedure WriteNecBRec() + begin + IncrementSequenceNo(); + IRSData.Add(GetNecBRec()); + end; + + local procedure AddNecBRecLine() + begin + IRSVendorLines.Get(NecTypeIndex).Add(GetNecBRec()); + end; + + local procedure GetNecBRec(): Text + var + FormTypeIndex: Integer; + LastNo: Integer; + begin + FormTypeIndex := NecTypeIndex; + LastNo := FormTypeLastNo[NecTypeIndex]; + + exit('B' + + StrSubstNo('#1##', CopyStr(Format(Year), 1, 4)) + + ' ' + + ' ' + + ' ' + + StrSubstNo(GetHashTagStringWithLength(9), Helper.StripNonNumerics(VendorData."Federal ID No.")) + + StrSubstNo(GetHashTagStringWithLength(20), VendorData."No.") + + ' ' + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount( + Helper.GetAmt(GetFullNecCode(1), FormTypeIndex, LastNo), 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount( + Helper.GetAmt(GetFullNecCode(4), FormTypeIndex, LastNo), 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + ' ' + // blank 16 + ' ' + + StrSubstNo(GetHashTagStringWithLength(40), VendorData.Name) + + StrSubstNo(GetHashTagStringWithLength(40), VendorData."Name 2") + + StrSubstNo(GetHashTagStringWithLength(40), VendorData.Address) + + ' ' + + StrSubstNo(GetHashTagStringWithLength(40), VendorData.City) + + StrSubstNo(GetHashTagStringWithLength(0), VendorData.County) + + StrSubstNo(GetHashTagStringWithLength(9), VendorData."Post Code") + + ' ' + + StrSubstNo(GetHashTagStringWithLength(8), Helper.FormatAmount(SequenceNo, 8)) + + ' ' + + ' ' + + ' ' + + StrSubstNo(Format(VendorData."FATCA Requirement", 0, 2)) + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + StrSubstNo(GetHashTagStringWithLength(12), Helper.FormatMoneyAmount(0, 12)) + + ' ' + + ' ' + ); + end; + + procedure WriteMISCCRec() + var + FormTypeIndex: Integer; + LastNo: Integer; + begin + FormTypeIndex := MiscTypeIndex; + LastNo := FormTypeLastNo[MiscTypeIndex]; + + // C Record - 1 per Payer per 1099 type + IncrementSequenceNo(); + IRSData.Add('C' + + StrSubstNo('#1######', Helper.FormatAmount(PayeeCount[MiscTypeIndex], 8)) + + StrSubstNo(' ') + // Blank 6 + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal(GetFullMiscCode(1), FormTypeIndex, LastNo), 18)) + // Payment 1 + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal(GetFullMiscCode(2), FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal(GetFullMiscCode(3), FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal(GetFullMiscCode(4), FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal(GetFullMiscCode(5), FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal(GetFullMiscCode(6), FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal(GetFullMiscCode(8), FormTypeIndex, LastNo), 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal(GetFullMiscCode(9), FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal(GetFullMiscCode(14), FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal(GetFullMiscCode(10), FormTypeIndex, LastNo), 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount( + Helper.GetTotal(GetFullMiscCode(12), FormTypeIndex, LastNo), 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount( + Helper.GetTotal(GetFullMiscCode(11), FormTypeIndex, LastNo), 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo('#1######', Helper.FormatAmount(SequenceNo, 8)) + // sequence number for all rec types + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ')); + end; + + procedure WriteDIVCRec() + var + FormTypeIndex: Integer; + LastNo: Integer; + begin + FormTypeIndex := DivTypeIndex; + LastNo := FormTypeLastNo[DivTypeIndex]; + + // C Record - 1 per Payer per 1099 type + IncrementSequenceNo(); + IRSData.Add('C' + + StrSubstNo('#1######', Helper.FormatAmount(PayeeCount[DivTypeIndex], 8)) + + StrSubstNo(' ') + // Blank 6 + StrSubstNo('#1################', Helper.FormatMoneyAmount(// ordinary dividends + Helper.GetTotal('DIV-01-A', FormTypeIndex, LastNo) + + Helper.GetTotal('DIV-01-B', FormTypeIndex, LastNo) + + Helper.GetTotal('DIV-05', FormTypeIndex, LastNo) + + Helper.GetTotal('DIV-06', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('DIV-01-B', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount(// total capital gains + Helper.GetTotal('DIV-02-A', FormTypeIndex, LastNo) + + Helper.GetTotal('DIV-02-B', FormTypeIndex, LastNo) + + Helper.GetTotal('DIV-02-C', FormTypeIndex, LastNo) + + Helper.GetTotal('DIV-02-D', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('DIV-05', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('DIV-02-B', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('DIV-02-C', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('DIV-02-D', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount(// Nondividend dist. 9 + Helper.GetTotal('DIV-03', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount(// fed income tax W/H A + Helper.GetTotal('DIV-04', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount(// investment. expenses B + Helper.GetTotal('DIV-06', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('DIV-07', FormTypeIndex, LastNo), 18)) + // Foreign Taxc Paid C + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('DIV-09', FormTypeIndex, LastNo), 18)) + // cash liquidation D + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('DIV-10', FormTypeIndex, LastNo), 18)) + // non-cash liquidation E + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('DIV-12', FormTypeIndex, LastNo), 18)) + // Exempt-interest dividends F + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount( + Helper.GetTotal('DIV-13', FormTypeIndex, LastNo), 18)) + // Specified private activity bond interest dividends G + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount( + Helper.GetTotal('DIV-02-E', FormTypeIndex, LastNo), 18)) + // Specified 897 Ordinary Dividends + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount( + Helper.GetTotal('DIV-02-F', FormTypeIndex, LastNo), 18)) + // Specified 897 Capital Gains + GetBlankedStringWithLength(160) + + StrSubstNo('#1######', Helper.FormatAmount(SequenceNo, 8)) + // sequence number for all rec types + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ')); + end; + + procedure WriteINTCRec() + var + FormTypeIndex: Integer; + LastNo: Integer; + begin + FormTypeIndex := IntTypeIndex; + LastNo := FormTypeLastNo[IntTypeIndex]; + + // C Record - 1 per Payer per 1099 type + IncrementSequenceNo(); + IRSData.Add('C' + + StrSubstNo('#1######', Helper.FormatAmount(PayeeCount[IntTypeIndex], 8)) + + StrSubstNo(' ') + // Blank 6 + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('INT-01', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('INT-02', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('INT-03', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('INT-04', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('INT-05', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('INT-06', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('INT-08', FormTypeIndex, LastNo) + + Helper.GetTotal('INT-09', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('INT-09', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('INT-10', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('INT-11', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('INT-13', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount( + Helper.GetTotal('INT-12', FormTypeIndex, LastNo), 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo('#1################', Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo('#1######', Helper.FormatAmount(SequenceNo, 8)) + // sequence number for all rec types + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ')); + end; + + procedure WriteNECCRec() + var + FormTypeIndex: Integer; + LastNo: Integer; + begin + FormTypeIndex := NecTypeIndex; + LastNo := FormTypeLastNo[NecTypeIndex]; + + // C Record - 1 per Payer per 1099 type + IncrementSequenceNo(); + IRSData.Add('C' + + StrSubstNo(GetHashTagStringWithLength(8), Helper.FormatAmount(PayeeCount[NecTypeIndex], 8)) + + ' ' + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount( + Helper.GetTotal(GetFullNecCode(1), FormTypeIndex, LastNo), 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount( + Helper.GetTotal(GetFullNecCode(4), FormTypeIndex, LastNo), 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + StrSubstNo(GetHashTagStringWithLength(18), Helper.FormatMoneyAmount(0, 18)) + + ' ' + + ' ' + + ' ' + + ' ' + + StrSubstNo(GetHashTagStringWithLength(8), Helper.FormatAmount(SequenceNo, 8)) + + ' ' + + ' ' + + ' ' + + ' ' + + ' '); + end; + + procedure WriteFRec() + begin + // F Record - 1 + IncrementSequenceNo(); + IRSData.Add('F' + + StrSubstNo('#1######', Helper.FormatAmount(ARecNum, 8)) + // number of A recs. + StrSubstNo('#1########', Helper.FormatAmount(0, 10)) + // 21 zeros + StrSubstNo('#1#########', Helper.FormatAmount(0, 11)) + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo('#1######', Helper.FormatAmount(SequenceNo, 8)) + // sequence number for all rec types + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ') + + StrSubstNo(' ')); + end; + + local procedure GetFullMiscCode(Number: Integer): Code[10] + begin + exit(GetFullCode(MiscCodeTok, Number)); + end; + + local procedure GetFullNecCode(Number: Integer): Code[10] + begin + exit(GetFullCode(NecCodeTok, Number)); + end; + + local procedure GetFullCode(Prefix: Text; Number: Integer) FullCode: Code[10] + begin + FullCode += Prefix; + if Number < 10 then + FullCode += Format(0); + exit(FullCode + Format(Number)); + end; + + local procedure GetHashTagStringWithLength(Length: Integer) Result: Text + var + j: Integer; + begin + Result += HashTagTok + Format(1); + for j := 1 to (Length - 2) do + Result += HashTagTok; + exit(Result); + end; + + local procedure GetBlankedStringWithLength(Length: Integer) Result: Text + var + j: Integer; + begin + for j := 1 to Length do + Result += BlankTagTok; + exit(Result); + end; + + procedure IncrementSequenceNo() + begin + SequenceNo := SequenceNo + 1; + end; + + local procedure bTestFileOnAfterValidate() + begin + if bTestFile then + TestFile := 'T'; + end; + + procedure InitializeRequest(NewFileName: Text) + begin + FileName := NewFileName; + end; + + local procedure GetFullVendorName(Vendor: Record Vendor): Text + begin + exit(Vendor.Name + Vendor."Name 2"); + end; + + local procedure UpdateSequenceNoInRecLine(var RecLine: Text) + var + StartString: Text; + EndString: Text; + SequenceText: Text; + SeqNoStartPos: Integer; + SeqNoEndPos: Integer; + begin + SeqNoStartPos := 500; + SeqNoEndPos := 507; + StartString := RecLine.Substring(1, SeqNoStartPos - 1); // before the sequence number + EndString := RecLine.Substring(SeqNoEndPos + 1); // after the sequence number + SequenceText := Helper.FormatAmount(SequenceNo, 8); + RecLine := StartString + SequenceText + EndString; // insert sequence number to 500-507 position + end; + + local procedure UpdateProgressDialog(Number: Integer; NewText: Text) + begin + if GuiAllowed() then + ProgressDialog.Update(Number, NewText + '%'); + end; + + local procedure GetAdjustmentRec(var IRS1099Adjustment: Record "IRS 1099 Vendor Form Box Adj."; VendorLedgerEntry: Record "Vendor Ledger Entry"): Boolean + begin + if VendorLedgerEntry."IRS 1099 Form Box No." = '' then + exit(false); + exit( + IRS1099Adjustment.Get( + Date2DMY(VendorLedgerEntry."Posting Date", 3), VendorLedgerEntry."Vendor No.", + VendorLedgerEntry."IRS 1099 Form No.", VendorLedgerEntry."IRS 1099 Form Box No.")); + end; + + [IntegrationEvent(false, false)] + local procedure OnBeforeDownloadFile(var TempBlob: Codeunit "Temp Blob") + begin + end; +} diff --git a/Apps/US/IRSForms/test/src/IRS1099DocumentTests.Codeunit.al b/Apps/US/IRSForms/test/src/IRS1099DocumentTests.Codeunit.al index 6792a06e31..a5def40252 100644 --- a/Apps/US/IRSForms/test/src/IRS1099DocumentTests.Codeunit.al +++ b/Apps/US/IRSForms/test/src/IRS1099DocumentTests.Codeunit.al @@ -833,6 +833,36 @@ codeunit 148010 "IRS 1099 Document Tests" // [THEN] There is only one form document line for MISC-01 and "Y" Assert.RecordCount(IRS1099FormDocLine, 1); +#if not CLEAN25 + UnbindSubscription(IRSFormsEnableFeature); +#endif + end; + + [Test] + procedure ValidatePostingDateWhenPurchHeaderNotInitiazed() + var + PurchaseHeader: Record "Purchase Header"; +#if not CLEAN25 +#pragma warning disable AL0432 + IRSFormsEnableFeature: Codeunit "IRS Forms Enable Feature"; +#pragma warning restore AL0432 +#endif + begin + // [FEATURE] [UT] + // [SCENARIO 560148] It is possible to validate the posting date in the not initialized purchase header + + Initialize(); + + // [GIVEN] IRS Forms app is enabled +#if not CLEAN25 + BindSubscription(IRSFormsEnableFeature); +#endif + + // [WHEN] Validate Posting Date field of the purchase header with current date + PurchaseHeader.Validate("Posting Date", WorkDate()); + // [THEN] Posting date is equal current date in the purchase header + PurchaseHeader.TestField("Posting Date", WorkDate()); + #if not CLEAN25 UnbindSubscription(IRSFormsEnableFeature); #endif diff --git a/Apps/W1/APIV2/app/src/pages/APIV2PurchaseCreditMemos.Page.al b/Apps/W1/APIV2/app/src/pages/APIV2PurchaseCreditMemos.Page.al index fc6db6ddfb..fb1c537366 100644 --- a/Apps/W1/APIV2/app/src/pages/APIV2PurchaseCreditMemos.Page.al +++ b/Apps/W1/APIV2/app/src/pages/APIV2PurchaseCreditMemos.Page.al @@ -130,6 +130,15 @@ page 30083 "APIV2 - Purchase Credit Memos" Caption = 'Vendor Name'; Editable = false; } + field(vendorCreditMemoNumber; Rec."Vendor Cr. Memo No.") + { + Caption = 'Vendor Credit Memo No.'; + + trigger OnValidate() + begin + RegisterFieldSet(Rec.FieldNo("Vendor Cr. Memo No.")); + end; + } field(payToVendorId; Rec."Pay-to Vendor Id") { Caption = 'Pay-to Vendor Id'; diff --git a/Apps/W1/APIV2/test/src/APIV2PurchCrMemosE2E.Codeunit.al b/Apps/W1/APIV2/test/src/APIV2PurchCrMemosE2E.Codeunit.al index 7cd21a3080..ce5dd663f5 100644 --- a/Apps/W1/APIV2/test/src/APIV2PurchCrMemosE2E.Codeunit.al +++ b/Apps/W1/APIV2/test/src/APIV2PurchCrMemosE2E.Codeunit.al @@ -143,6 +143,45 @@ codeunit 139865 "APIV2 - Purch. Cr. Memos E2E" Assert.AreEqual(CurrencyCode, PurchaseHeader."Currency Code", 'The credit memo should have the correct currency code'); end; + [Test] + procedure TestPostCreditMemoWithVendorCreditMemoNo() + var + PurchaseHeader: Record "Purchase Header"; + Vendor: Record Vendor; + VendorNo: Text; + ResponseText: Text; + CreditMemoNumber: Text; + TargetURL: Text; + CreditMemoJSON: Text; + VendorCreditMemoNumber: Text; + begin + // [SCENARIO] Create posted and unposted with specific vendor credit memo number set and use HTTP POST to create them + + // [GIVEN] A credit memo with vendor credit memo number set + LibraryPurchase.CreateVendor(Vendor); + VendorNo := Vendor."No."; + VendorCreditMemoNumber := LibraryRandom.RandText(15).ToUpper(); + + CreditMemoJSON := LibraryGraphMgt.AddPropertytoJSON('', 'vendorNumber', VendorNo); + CreditMemoJSON := LibraryGraphMgt.AddPropertytoJSON(CreditMemoJSON, 'vendorCreditMemoNumber', VendorCreditMemoNumber); + Commit(); + + // [WHEN] we POST the JSON to the web service + TargetURL := LibraryGraphMgt.CreateTargetURL('', Page::"APIV2 - Purchase Credit Memos", CreditMemoServiceNameTxt); + LibraryGraphMgt.PostToWebService(TargetURL, CreditMemoJSON, ResponseText); + + // [THEN] the response text should contain the credit memo ID + Assert.AreNotEqual('', ResponseText, 'response JSON should not be blank'); + Assert.IsTrue( + LibraryGraphMgt.GetObjectIDFromJSON(ResponseText, 'number', CreditMemoNumber), + 'Could not find purchase credit memo number'); + LibraryGraphMgt.VerifyIDInJson(ResponseText); + + // [THEN] the credit memo should exist in the tables + GetPurchaseCreditMemoHeaderByVendorAndNumber(VendorNo, CreditMemoNumber, PurchaseHeader, 'The unposted credit memo should exist'); + Assert.AreEqual(VendorCreditMemoNumber, PurchaseHeader."Vendor Cr. Memo No.", 'The credit memo should have the correct vendor credit memo number'); + end; + [Test] procedure TestModifyCreditMemos() begin diff --git a/Apps/W1/AutomaticAccountCodes/app/src/Tables/AutoAccPageSetup.Table.al b/Apps/W1/AutomaticAccountCodes/app/src/Tables/AutoAccPageSetup.Table.al index 7aaf58e4ee..fcdbadf259 100644 --- a/Apps/W1/AutomaticAccountCodes/app/src/Tables/AutoAccPageSetup.Table.al +++ b/Apps/W1/AutomaticAccountCodes/app/src/Tables/AutoAccPageSetup.Table.al @@ -14,6 +14,7 @@ table 4857 "Auto. Acc. Page Setup" fields { +#if not CLEANSCHEMA29 #pragma warning disable AS0105 field(1; Id; Enum "AAC Page Setup Key") { @@ -28,6 +29,7 @@ table 4857 "Auto. Acc. Page Setup" ObsoleteReason = 'Automatic Acc.functionality will be moved to a new app.'; } #pragma warning restore AS0105 +#endif field(2; ObjectId; Integer) { DataClassification = SystemMetadata; @@ -42,4 +44,4 @@ table 4857 "Auto. Acc. Page Setup" } } } -#endif \ No newline at end of file +#endif diff --git a/Apps/W1/BankAccRecWithAI/app/src/BankRecAIMatchingImpl.Codeunit.al b/Apps/W1/BankAccRecWithAI/app/src/BankRecAIMatchingImpl.Codeunit.al index df8b923e14..a488c40322 100644 --- a/Apps/W1/BankAccRecWithAI/app/src/BankRecAIMatchingImpl.Codeunit.al +++ b/Apps/W1/BankAccRecWithAI/app/src/BankRecAIMatchingImpl.Codeunit.al @@ -147,6 +147,56 @@ codeunit 7250 "Bank Rec. AI Matching Impl." until (TempBankAccLedgerEntryMatchingBuffer.Next() = 0); end; + local procedure FindDateFilterOnLedgerEntryBuffer(LedgerLines: Text; var TempBankAccLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; CandidateLedgerEntryNos: List of [Integer]; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var FromDate: Date; var ToDate: Date): Boolean + var + LocaLedgerEntryLines: Text; + LocalCandidateEntryNos: List of [Integer]; + NumberOfDaysBack: array[6] of Text; + I, Candidate : integer; + TelemetryDimensions: Dictionary of [Text, Text]; + begin + // we must set a date filer to reduce the ledger entry candidate list until it gets under the threshold size + NumberOfDaysBack[1] := ''; + NumberOfDaysBack[2] := '<-30D>'; + NumberOfDaysBack[3] := '<-15D>'; + NumberOfDaysBack[4] := '<-7D>'; + NumberOfDaysBack[5] := '<-3D>'; + NumberOfDaysBack[6] := '<-1D>'; + ToDate := BankAccReconciliationLine."Transaction Date"; + if ToDate = 0D then + ToDate := Today(); + + for I := 1 to 6 do begin + LocaLedgerEntryLines := LedgerLines; + Clear(LocalCandidateEntryNos); + foreach Candidate in CandidateLedgerEntryNos do + LocalCandidateEntryNos.Add(Candidate); + + TempBankAccLedgerEntryMatchingBuffer.Reset(); + if NumberOfDaysBack[I] <> '' then begin + FromDate := CalcDate(NumberOfDaysBack[I], ToDate); + TempBankAccLedgerEntryMatchingBuffer.SetRange("Posting Date", FromDate, ToDate); + end; + + TempBankAccLedgerEntryMatchingBuffer.FindSet(); + BuildBankRecLedgerEntries(LocaLedgerEntryLines, TempBankAccLedgerEntryMatchingBuffer, LocalCandidateEntryNos); + + // if ledger entry part of the prompt is small enough, we are done + // we have set a good enough date filter, and FromDate and ToDate are passed back as reference + if AOAIToken.GetGPT4TokenCount(LocaLedgerEntryLines) < LedgerEntryInputThreshold() then begin + Session.LogMessage('0000OEL', SuccessfullyFilteredBLEListTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::All, 'Category', FeatureName(), 'DateFormula', NumberOfDaysBack[I]); + exit(true); + end; + end; + + TelemetryDimensions.Add('Category', FeatureName()); + TelemetryDimensions.Add('DateFormula', NumberOfDaysBack[I]); + TelemetryDimensions.Add('ToDate', Format(ToDate, 0, 9)); + TelemetryDimensions.Add('TokenSizeLedgerEntryList', Format(AOAIToken.GetGPT4TokenCount(LocaLedgerEntryLines))); + Session.LogMessage('0000OEM', UnableToFilterBLEListUnderTokenLimitTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::All, TelemetryDimensions); + exit(false) + end; + procedure BuildBankRecStatementLines(var StatementLines: Text; var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"): Text begin if (StatementLines = '') then @@ -198,15 +248,21 @@ codeunit 7250 "Bank Rec. AI Matching Impl." procedure PromptSizeThreshold(): Integer begin - // this is because we are using GPT4 which has a 32K token limit + // this is because we are using GPT4 which has a 100K token limit // on top of that, we are setting aside a number of tokens for the response in MaxTokens()) exit(18000); end; + procedure LedgerEntryInputThreshold(): Integer + begin + // this is the max size of the part of the prompt that carries information about ledger entries + exit(10000); + end; + procedure MaxTokens(): Integer begin // this is specifying how many tokens of the AI Model token limit are set aside (reserved) for the response - exit(4000); + exit(4096); end; internal procedure GetAzureKeyVaultSecret(var SecretValue: SecretText; SecretName: Text): Boolean; @@ -402,8 +458,9 @@ codeunit 7250 "Bank Rec. AI Matching Impl." TopSimilarityScore: array[5] of Decimal; BankRecLineDescription: Text; AmountEquals: Boolean; - EntryAddedToTop5: Boolean; + EntryAddedToTop5, LedgerEntryBufferFilterSet : Boolean; CandidateLedgerEntryNos: List of [Integer]; + FromDate, ToDate : Date; begin TempBankAccLedgerEntryMatchingBuffer.RESET(); TempBankStatementMatchingBuffer.RESET(); @@ -423,6 +480,8 @@ codeunit 7250 "Bank Rec. AI Matching Impl." // Find the top 5 ledger entries closest to the statement line TempBankAccLedgerEntryMatchingBuffer.RESET(); TempBankAccLedgerEntryMatchingBuffer.FindSet(); + FromDate := 0D; + ToDate := 0D; // Initialize TopLedgerEntries and TopSimilarityScore for i := 1 to 5 do begin @@ -460,7 +519,7 @@ codeunit 7250 "Bank Rec. AI Matching Impl." if (TopSimilarityScore[i] = 0) or (i = 5) then begin // Add the new entry TopLedgerEntries[i] := TempBankAccLedgerEntryMatchingBuffer; - TopSimilarityScore[i] := SimilarityScore; + TopSimilarityScore[i] := 0.5; EntryAddedToTop5 := true; break; end; @@ -475,9 +534,28 @@ codeunit 7250 "Bank Rec. AI Matching Impl." // Apply filters to the ledger entries (TempBankAccLedgerEntryMatchingBuffer) from the top 5 ledger entries. TopBankLedgerEntriesFilterTxt := BuildLedgerEntriesFilter(TopLedgerEntries, TopSimilarityScore); - TempBankAccLedgerEntryMatchingBuffer.SetFilter("Entry No.", TopBankLedgerEntriesFilterTxt); - TempBankAccLedgerEntryMatchingBuffer.FindSet(); - BuildBankRecLedgerEntries(BankRecLedgerEntriesTxt, TempBankAccLedgerEntryMatchingBuffer, CandidateLedgerEntryNos); + if TopBankLedgerEntriesFilterTxt = '' then begin + LedgerEntryBufferFilterSet := FindDateFilterOnLedgerEntryBuffer(BankRecLedgerEntriesTxt, TempBankAccLedgerEntryMatchingBuffer, CandidateLedgerEntryNos, BankAccReconciliationLineCopy, FromDate, ToDate); + TempBankAccLedgerEntryMatchingBuffer.Reset(); + if FromDate <> 0D then + TempBankAccLedgerEntryMatchingBuffer.SetRange("Posting Date", FromDate, ToDate); + end else begin + TempBankAccLedgerEntryMatchingBuffer.SetFilter("Entry No.", TopBankLedgerEntriesFilterTxt); + LedgerEntryBufferFilterSet := true; + end; + + if LedgerEntryBufferFilterSet then begin + TempBankAccLedgerEntryMatchingBuffer.FindSet(); + BuildBankRecLedgerEntries(BankRecLedgerEntriesTxt, TempBankAccLedgerEntryMatchingBuffer, CandidateLedgerEntryNos); + end; + + // if you were unable to set the filter, and your bank ledger entry list is not completely empty + // just let it be, we have some candidates and we go with them + // otherwise, throw an error that there are too many entries, we couldn't even build the first candidate list + if not LedgerEntryBufferFilterSet and (BankRecLedgerEntriesTxt = '') then begin + Session.LogMessage('0000OEN', TooManyOpenLedgerEntriesTelemetryErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::All, 'Category', FeatureName()); + Error(TooManyOpenLedgerEntriesErr); + end; CompletePromptTokenCount := TaskPromptTokenCount + AOAIToken.GetGPT4TokenCount(BankRecStatementLinesTxt) + AOAIToken.GetGPT4TokenCount(BankRecLedgerEntriesTxt); if (CompletePromptTokenCount >= PromptSizeThreshold()) then begin @@ -604,6 +682,8 @@ codeunit 7250 "Bank Rec. AI Matching Impl." CopilotCapability.ModifyCapability(Enum::"Copilot Capability"::"Bank Account Reconciliation", Enum::"Copilot Availability"::"Generally Available", LearnMoreUrlTxt); UpgradeTag.SetUpgradeTag(GetRegisterBankAccRecCopilotGACapabilityUpgradeTag()); + + Commit(); end; local procedure MatchIsAcceptable(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; MatchedLineNoTxt: Text; MatchedEntryNoTxt: Text): Boolean @@ -658,11 +738,15 @@ codeunit 7250 "Bank Rec. AI Matching Impl." var AOAIToken: Codeunit "AOAI Token"; MatchedByCopilotTxt: label 'Matched by Copilot based on semantic similarity.', Comment = 'Copilot is a Microsoft service name and must not be translated'; + SuccessfullyFilteredBLEListTxt: label 'Successfully filtered bank account ledger entries based on posting date.', Locked = true; + UnableToFilterBLEListUnderTokenLimitTxt: label 'Unable to filter the bank account ledger entry list.', Locked = true; ConstructingPromptFailedErr: label 'There was an error with sending the call to Copilot. Log a Business Central support request about this.', Comment = 'Copilot is a Microsoft service name and must not be translated'; TelemetryConstructingPromptFailedErr: label 'There was an error with constructing the chat completion prompt from the Key Vault.', Locked = true; TelemetryApproximateTokenCountExceedsLimitTxt: label 'The approximate token count for the Copilot request exceeded the limit. Sending request in chunks.', Locked = true; TelemetryChatCompletionErr: label 'Chat completion request was unsuccessful. Response code: %1', Locked = true; LearnMoreUrlTxt: Label 'https://go.microsoft.com/fwlink/?linkid=2248547', Locked = true; ContentAreaCaptionTxt: label 'Reconciling %1 statement %2 for %3', Comment = '%1 - bank account code, %2 - statement number, %3 - statement date'; + TooManyOpenLedgerEntriesErr: label 'The number of open ledger entries for this bank account exceeds the capacity of data that we can send to Copilot. Open the bank account reconciliation card and use action ''Match Automatically'' to reconcile this statement. Perform the reconciliation month by month - reconciling the oldest open ledger entries first, then moving on to newer ones.'; + TooManyOpenLedgerEntriesTelemetryErr: label 'Throwing error that we are unable to build ledger entry candidate list.', Locked = true; InputWithReservedWordsFound: Boolean; } \ No newline at end of file diff --git a/Apps/W1/ContosoCoffeeDemoDataset/app/Permissions/ContosoDemoEdit.PermissionSet.al b/Apps/W1/ContosoCoffeeDemoDataset/app/Permissions/ContosoDemoEdit.PermissionSet.al index d96a45c897..da4282d7dc 100644 --- a/Apps/W1/ContosoCoffeeDemoDataset/app/Permissions/ContosoDemoEdit.PermissionSet.al +++ b/Apps/W1/ContosoCoffeeDemoDataset/app/Permissions/ContosoDemoEdit.PermissionSet.al @@ -7,7 +7,7 @@ permissionset 4767 "Contoso Demo - Edit" { Access = Public; Assignable = true; - Caption = 'E-Document Core - Edit'; + Caption = 'Contoso Demo - Edit'; IncludedPermissionSets = "Contoso Demo - Read"; diff --git a/Apps/W1/ContosoCoffeeDemoDataset/app/Permissions/ContosoDemoRead.PermissionSet.al b/Apps/W1/ContosoCoffeeDemoDataset/app/Permissions/ContosoDemoRead.PermissionSet.al index 61430c2154..591543f776 100644 --- a/Apps/W1/ContosoCoffeeDemoDataset/app/Permissions/ContosoDemoRead.PermissionSet.al +++ b/Apps/W1/ContosoCoffeeDemoDataset/app/Permissions/ContosoDemoRead.PermissionSet.al @@ -7,19 +7,19 @@ permissionset 4766 "Contoso Demo - Read" { Access = Public; Assignable = true; - Caption = 'E-Document Core - Edit'; + Caption = 'Contoso Demo - Edit'; IncludedPermissionSets = "Contoso Demo - Objects"; Permissions = - tabledata "EService Demo Data Setup" = IM, - tabledata "FA Module Setup" = IM, - tabledata "Human Resources Module Setup" = IM, - tabledata "Jobs Module Setup" = IM, - tabledata "Manufacturing Module Setup" = IM, - tabledata "Service Module Setup" = IM, - tabledata "Warehouse Module Setup" = IM, - tabledata "Contoso Coffee Demo Data Setup" = IM, - tabledata "Contoso Demo Data Module" = IM, - tabledata "Contoso Module Dependency" = IM; + tabledata "EService Demo Data Setup" = R, + tabledata "FA Module Setup" = R, + tabledata "Human Resources Module Setup" = R, + tabledata "Jobs Module Setup" = R, + tabledata "Manufacturing Module Setup" = R, + tabledata "Service Module Setup" = R, + tabledata "Warehouse Module Setup" = R, + tabledata "Contoso Coffee Demo Data Setup" = R, + tabledata "Contoso Demo Data Module" = R, + tabledata "Contoso Module Dependency" = R; } diff --git a/Apps/W1/DataSearch/App/DataSearchObjectMapping.Codeunit.al b/Apps/W1/DataSearch/App/DataSearchObjectMapping.Codeunit.al index 60de449cab..efb58c7e0a 100644 --- a/Apps/W1/DataSearch/App/DataSearchObjectMapping.Codeunit.al +++ b/Apps/W1/DataSearch/App/DataSearchObjectMapping.Codeunit.al @@ -856,7 +856,7 @@ codeunit 2685 "Data Search Object Mapping" WarehouseActivityLine: Record "Warehouse Activity Line"; begin RecRef.SetTable(WarehouseActivityLine); - WarehouseActivityHeader.Get(WarehouseActivityLine."No."); + WarehouseActivityHeader.Get(WarehouseActivityLine."Activity Type", WarehouseActivityLine."No."); RecRef.GetTable(WarehouseActivityHeader); end; @@ -866,7 +866,7 @@ codeunit 2685 "Data Search Object Mapping" RegisteredWhseActivityLine: Record "Registered Whse. Activity Line"; begin RecRef.SetTable(RegisteredWhseActivityLine); - RegisteredWhseActivityHdr.Get(RegisteredWhseActivityLine."No."); + RegisteredWhseActivityHdr.Get(RegisteredWhseActivityLine."Activity Type", RegisteredWhseActivityLine."No."); RecRef.GetTable(RegisteredWhseActivityHdr); end; diff --git a/Apps/W1/EDocument/app/src/Integration/EDocumentIntegration.Enum.al b/Apps/W1/EDocument/app/src/Integration/EDocumentIntegration.Enum.al index 795495b8c2..b9a65fcc5b 100644 --- a/Apps/W1/EDocument/app/src/Integration/EDocumentIntegration.Enum.al +++ b/Apps/W1/EDocument/app/src/Integration/EDocumentIntegration.Enum.al @@ -1,4 +1,5 @@ -// ------------------------------------------------------------------------------------------------ +#if not CLEANSCHEMA29 +// ------------------------------------------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. // ------------------------------------------------------------------------------------------------ @@ -10,6 +11,7 @@ enum 6143 "E-Document Integration" enum 6143 "E-Document Integration" implements "E-Document Integration" #endif { +#if not CLEAN26 ObsoleteTag = '26.0'; ObsoleteState = Pending; ObsoleteReason = 'Use sender, receiver and action integration enums instead'; @@ -17,7 +19,7 @@ enum 6143 "E-Document Integration" implements "E-Document Integration" Extensible = true; Access = Public; -#if not CLEAN26 + value(0; "No Integration") { ObsoleteReason = 'Use sender, receiver and action integration enums instead'; @@ -26,4 +28,5 @@ enum 6143 "E-Document Integration" implements "E-Document Integration" Implementation = "E-Document Integration" = "E-Document No Integration"; } #endif -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Apps/W1/EDocument/test/src/Mock/EDocIntegrationMock.EnumExt.al b/Apps/W1/EDocument/test/src/Mock/EDocIntegrationMock.EnumExt.al index e598f62808..21d31c2d08 100644 --- a/Apps/W1/EDocument/test/src/Mock/EDocIntegrationMock.EnumExt.al +++ b/Apps/W1/EDocument/test/src/Mock/EDocIntegrationMock.EnumExt.al @@ -1,3 +1,4 @@ +#if not CLEAN26 enumextension 139616 "E-Doc Integration Mock" extends "E-Document Integration" { #pragma warning disable PTE0023 // The IDs should have been in the range [139500..139899] @@ -12,4 +13,5 @@ enumextension 139616 "E-Doc Integration Mock" extends "E-Document Integration" #endif #pragma warning restore PTE0023 // The IDs should have been in the range [139500..139899] -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Microsoft365/app/src/IntegrationImpl.Codeunit.al b/Apps/W1/EDocumentConnectors/Microsoft365/app/src/IntegrationImpl.Codeunit.al index 6bc7232c3b..20fea3ef8f 100644 --- a/Apps/W1/EDocumentConnectors/Microsoft365/app/src/IntegrationImpl.Codeunit.al +++ b/Apps/W1/EDocumentConnectors/Microsoft365/app/src/IntegrationImpl.Codeunit.al @@ -98,6 +98,11 @@ codeunit 6382 "Integration Impl." implements IDocumentReceiver, IDocumentSender, end; end; + internal procedure SecurityAuditLogSetupStatusDescription(Action: Text; SetupTableName: Text): Text + begin + exit(Action + ' ' + SetupTableName + ConnectorTelemetrySnippetTxt); + end; + [EventSubscriber(ObjectType::Table, Database::"E-Document Log", OnBeforeExportDataStorage, '', false, false)] local procedure HandleOnBeforeExportDataStorage(EDocumentLog: Record "E-Document Log"; var FileName: Text) var @@ -115,6 +120,7 @@ codeunit 6382 "Integration Impl." implements IDocumentReceiver, IDocumentSender, var DriveProcessing: Codeunit "Drive Processing"; SendNotSupportedErr: label 'Sending document is not supported in this context.'; + ConnectorTelemetrySnippetTxt: label ' for Microsoft 365 E-Document connector.', Locked = true; NoFileErr: label 'No previewable attachment exists for this %2.', Comment = '%1 - a table caption'; NoFileContentErr: label 'Previewing file %1 failed. The file was found in table %2, but it has no content.', Comment = '%1 - a file name; %2 - a table caption'; } \ No newline at end of file diff --git a/Apps/W1/EDocumentConnectors/Microsoft365/app/src/OneDriveSetup.Table.al b/Apps/W1/EDocumentConnectors/Microsoft365/app/src/OneDriveSetup.Table.al index 907605d638..e0bd9834ff 100644 --- a/Apps/W1/EDocumentConnectors/Microsoft365/app/src/OneDriveSetup.Table.al +++ b/Apps/W1/EDocumentConnectors/Microsoft365/app/src/OneDriveSetup.Table.al @@ -29,13 +29,16 @@ table 6381 "OneDrive Setup" var FeatureTelemetry: Codeunit "Feature Telemetry"; DriveProcessing: Codeunit "Drive Processing"; + IntegrationImpl: Codeunit "Integration Impl."; begin if Rec.Enabled then begin if (Rec."Imp. Documents Folder" = '') or (Rec."Documents Folder" = '') then Error(URLsMustBeSpecifiedErr); FeatureTelemetry.LogUptake('0000OB9', DriveProcessing.FeatureName(), Enum::"Feature Uptake Status"::Used); FeatureTelemetry.LogUsage('0000OBA', DriveProcessing.FeatureName(), 'OneDrive'); - end; + Session.LogSecurityAudit(Rec.TableName(), SecurityOperationResult::Success, IntegrationImpl.SecurityAuditLogSetupStatusDescription(Rec.FieldName(Enabled), Rec.TableName()), AuditCategory::CustomerFacing); + end else + Session.LogSecurityAudit(Rec.TableName(), SecurityOperationResult::Success, IntegrationImpl.SecurityAuditLogSetupStatusDescription('Disabled', Rec.TableName()), AuditCategory::CustomerFacing); end; } field(3; "Documents Folder"; Text[2048]) diff --git a/Apps/W1/EDocumentConnectors/Microsoft365/app/src/SharepointSetup.Table.al b/Apps/W1/EDocumentConnectors/Microsoft365/app/src/SharepointSetup.Table.al index b59eb6dbb1..5f2be98338 100644 --- a/Apps/W1/EDocumentConnectors/Microsoft365/app/src/SharepointSetup.Table.al +++ b/Apps/W1/EDocumentConnectors/Microsoft365/app/src/SharepointSetup.Table.al @@ -30,6 +30,7 @@ table 6382 "Sharepoint Setup" var FeatureTelemetry: Codeunit "Feature Telemetry"; DriveProcessing: Codeunit "Drive Processing"; + IntegrationImpl: Codeunit "Integration Impl."; begin if Rec.Enabled then begin if (Rec."Imp. Documents Folder" = '') or (Rec."Documents Folder" = '') then @@ -37,7 +38,9 @@ table 6382 "Sharepoint Setup" FeatureTelemetry.LogUptake('0000OBB', DriveProcessing.FeatureName(), Enum::"Feature Uptake Status"::Used); FeatureTelemetry.LogUsage('0000OBC', DriveProcessing.FeatureName(), 'Sharepoint'); - end; + Session.LogSecurityAudit(Rec.TableName(), SecurityOperationResult::Success, IntegrationImpl.SecurityAuditLogSetupStatusDescription(Rec.FieldName(Enabled), Rec.TableName()), AuditCategory::CustomerFacing); + end else + Session.LogSecurityAudit(Rec.TableName(), SecurityOperationResult::Success, IntegrationImpl.SecurityAuditLogSetupStatusDescription('Disabled', Rec.TableName()), AuditCategory::CustomerFacing); end; } field(3; "Documents Folder"; Text[2048]) diff --git a/Apps/W1/Email - Outlook REST API/app/src/EmailOutlookAPIClient.Codeunit.al b/Apps/W1/Email - Outlook REST API/app/src/EmailOutlookAPIClient.Codeunit.al index 1046caa3cb..c82af31b28 100644 --- a/Apps/W1/Email - Outlook REST API/app/src/EmailOutlookAPIClient.Codeunit.al +++ b/Apps/W1/Email - Outlook REST API/app/src/EmailOutlookAPIClient.Codeunit.al @@ -329,17 +329,17 @@ codeunit 4508 "Email - Outlook API Client" implements "Email - Outlook API Clien FilterParameters := '$filter='; if Filters."Unread Emails" then - FilterParameters := FilterParameters + 'isRead ne true&'; + FilterParameters := FilterParameters + 'isRead ne true and '; if Filters."Draft Emails" then - FilterParameters := FilterParameters + 'isDraft eq true&' + FilterParameters := FilterParameters + 'isDraft eq true and ' else - FilterParameters := FilterParameters + 'isDraft ne true&'; + FilterParameters := FilterParameters + 'isDraft ne true and '; if Filters."Earliest Email" <> 0DT then - FilterParameters := FilterParameters + 'receivedDateTime ge ' + Format(Filters."Earliest Email", 0, 9) + '&'; + FilterParameters := FilterParameters + 'receivedDateTime ge ' + Format(Filters."Earliest Email", 0, 9) + ' and '; if FilterParameters <> '$filter=' then begin QueryParameters := QueryParameters + FilterParameters; - QueryParameters := CopyStr(QueryParameters, 1, StrLen(QueryParameters) - 1); + QueryParameters := CopyStr(QueryParameters, 1, StrLen(QueryParameters) - 5); end; RequestUri := RequestUri + QueryParameters; diff --git a/Apps/W1/Email - Outlook REST API/app/src/EmailOutlookAPIHelper.Codeunit.al b/Apps/W1/Email - Outlook REST API/app/src/EmailOutlookAPIHelper.Codeunit.al index 04a9f86ccc..2389bf1af1 100644 --- a/Apps/W1/Email - Outlook REST API/app/src/EmailOutlookAPIHelper.Codeunit.al +++ b/Apps/W1/Email - Outlook REST API/app/src/EmailOutlookAPIHelper.Codeunit.al @@ -393,7 +393,6 @@ codeunit 4509 "Email - Outlook API Helper" local procedure CreateEmailInboxFromJsonObject(var EmailInbox: Record "Email Inbox"; OutlookAccount: Record "Email - Outlook Account"; var Filters: Record "Email Retrieval Filters"; EmailJsonObject: JsonObject) var - EmailInboxDelete: Record "Email Inbox"; EmailMessage: Codeunit "Email Message"; BodyObject: JsonObject; SenderObject: JsonObject; @@ -427,11 +426,6 @@ codeunit 4509 "Email - Outlook API Helper" SenderName := GetTextFromJsonObject(SenderObject, 'name'); SenderEmail := GetTextFromJsonObject(SenderObject, 'address'); - if DoesExternalMessageIdExist(ExternalMessageId) then begin - EmailInboxDelete.SetRange("External Message Id", ExternalMessageId); - EmailInboxDelete.DeleteAll(); - end; - HTMLBody := Filters."Body Type" = Filters."Body Type"::HTML; EmailMessage.Create('', Subject, Body, HTMLBody, true); @@ -455,14 +449,6 @@ codeunit 4509 "Email - Outlook API Helper" EmailInbox.Mark(true); end; - local procedure DoesExternalMessageIdExist(ExternalMessageId: Text): Boolean - var - EmailInbox: Record "Email Inbox"; - begin - EmailInbox.SetRange("External Message Id", ExternalMessageId); - exit(not EmailInbox.IsEmpty()); - end; - local procedure AddAttachmentsToMessage(EmailJsonObject: JsonObject; var EmailMessage: Codeunit "Email Message") var AttachmentsArray: JsonArray; diff --git a/Apps/W1/ExcelReports/app/src/Financials/TrialBalance.Codeunit.al b/Apps/W1/ExcelReports/app/src/Financials/TrialBalance.Codeunit.al index 2e171cf03e..db5481274f 100644 --- a/Apps/W1/ExcelReports/app/src/Financials/TrialBalance.Codeunit.al +++ b/Apps/W1/ExcelReports/app/src/Financials/TrialBalance.Codeunit.al @@ -53,7 +53,13 @@ codeunit 4410 "Trial Balance" end; local procedure InsertDimensionFiltersFromDimensionValues(var DimensionValue: Record "Dimension Value"; var DimensionFilters: List of [Code[20]]) + var + IsHandled: Boolean; begin + IsHandled := false; + OnBeforeInsertDimensionFiltersFromDimensionValues(DimensionValue, DimensionFilters, IsHandled); + if IsHandled then + exit; DimensionFilters.Add(''); if not DimensionValue.FindSet() then exit; @@ -163,4 +169,9 @@ codeunit 4410 "Trial Balance" InsertedDimensionValues.Copy(DimensionValue); InsertedDimensionValues.Insert(); end; + + [IntegrationEvent(true, false)] + local procedure OnBeforeInsertDimensionFiltersFromDimensionValues(var DimensionValue: Record "Dimension Value"; var DimensionFilters: List of [Code[20]]; var IsHandled: Boolean) + begin + end; } \ No newline at end of file diff --git a/Apps/W1/HybridBaseDeployment/app/src/pages/MigrationUserMapping.Page.al b/Apps/W1/HybridBaseDeployment/app/src/pages/MigrationUserMapping.Page.al index 7451bfac92..a8fc091f1f 100644 --- a/Apps/W1/HybridBaseDeployment/app/src/pages/MigrationUserMapping.Page.al +++ b/Apps/W1/HybridBaseDeployment/app/src/pages/MigrationUserMapping.Page.al @@ -158,7 +158,7 @@ page 4022 "Migration User Mapping" var User: Record User; User2: Record User; - UserManagement: Codeunit "User Management"; + UserCodeunit: Codeunit User; begin Rec.Reset(); Rec.SetFilter("Dest User ID", '<>%1', ''); @@ -173,7 +173,7 @@ page 4022 "Migration User Mapping" User2."User Name" := Rec."Source User ID"; User2.Modify(true); end; - UserManagement.RenameUser(Rec."Dest User ID", Rec."Source User ID"); + UserCodeunit.RenameUser(Rec."Dest User ID", Rec."Source User ID"); end; until Rec.Next() = 0; end; diff --git a/Apps/W1/HybridGP/app/src/Migration/Support/HelperFunctions.codeunit.al b/Apps/W1/HybridGP/app/src/Migration/Support/HelperFunctions.codeunit.al index 2e8ec087a7..f25f940596 100644 --- a/Apps/W1/HybridGP/app/src/Migration/Support/HelperFunctions.codeunit.al +++ b/Apps/W1/HybridGP/app/src/Migration/Support/HelperFunctions.codeunit.al @@ -103,11 +103,15 @@ codeunit 4037 "Helper Functions" SavedJrnlLinesFoundMsg: Label 'Saved journal lines are found. In order to use the wizard, you will need to delete the journal lines before you migrate your data.'; MigrationNotSupportedErr: Label 'This migration does not support the "Specific" costing method. Verify your costing method in Inventory Setup.'; PostingGroupCodeTxt: Label 'GP', Locked = true; +#if not CLEAN25 DocNoOutofBalanceMsg: Label 'Document No. %1 is out of balance by %2. Transactions will not be created. Please check the amount in the import file.', Comment = '%1 = Balance Amount', Locked = true; +#endif CustomerBatchNameTxt: Label 'GPCUST', Locked = true; VendorBatchNameTxt: Label 'GPVEND', Locked = true; BankBatchNameTxt: Label 'GPBANK', Locked = true; +#if not CLEAN25 GlDocNoTxt: Label 'G00001', Locked = true; +#endif MigrationTypeTxt: Label 'Great Plains'; CloudMigrationTok: Label 'CloudMigration', Locked = true; GeneralTemplateNameTxt: Label 'GENERAL', Locked = true; @@ -1325,6 +1329,7 @@ codeunit 4037 "Helper Functions" Session.LogMessage('00007GK', StrSubstNo(FinishedTelemetryTxt, DurationAsInt), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GetTelemetryCategory()); end; +#if not CLEAN25 [Obsolete('This procedure will be soon removed.', '25.0')] procedure PostGLBatch(JournalBatchName: Code[10]) var @@ -1351,6 +1356,7 @@ codeunit 4037 "Helper Functions" if GenJournalLine.FindFirst() then codeunit.Run(codeunit::"Gen. Jnl.-Post Batch", GenJournalLine); end; +#endif local procedure SafePostGLBatch(JournalBatchName: Code[10]) var @@ -1366,6 +1372,7 @@ codeunit 4037 "Helper Functions" end; end; +#if not CLEAN25 [Obsolete('This procedure will be soon removed.', '25.0')] procedure PostStatisticalAccBatch(JournalBatchName: Code[10]) var @@ -1375,6 +1382,7 @@ codeunit 4037 "Helper Functions" if StatisticalAccJournalLine.FindFirst() then Codeunit.Run(Codeunit::"Stat. Acc. Post. Batch", StatisticalAccJournalLine); end; +#endif local procedure SafePostStatisticalAccBatch(JournalBatchName: Code[10]) var diff --git a/Apps/W1/LatePaymentPredictor/app/src/LatePaymentUpgrade.Codeunit.al b/Apps/W1/LatePaymentPredictor/app/src/LatePaymentUpgrade.Codeunit.al index ce23b547f5..d833b7c06f 100644 --- a/Apps/W1/LatePaymentPredictor/app/src/LatePaymentUpgrade.Codeunit.al +++ b/Apps/W1/LatePaymentPredictor/app/src/LatePaymentUpgrade.Codeunit.al @@ -25,8 +25,10 @@ codeunit 1958 "Late Payment Upgrade" exit; LPFeatureTableHelper.SetFiltersOnSalesInvoiceHeaderToAddToInput(LppSalesInvoiceHeaderInput, ''); LppSalesInvoiceHeaderInput.Open(); - while LppSalesInvoiceHeaderInput.Read() do; - LPMachineLearningSetup."Posting Date OnLastML" := LppSalesInvoiceHeaderInput.PostingDate; + while LppSalesInvoiceHeaderInput.Read() do + if LPMachineLearningSetup."Posting Date OnLastML" < LppSalesInvoiceHeaderInput.PostingDate then + LPMachineLearningSetup."Posting Date OnLastML" := LppSalesInvoiceHeaderInput.PostingDate; + LppSalesInvoiceHeaderInput.Close(); LPMachineLearningSetup.Modify(true); end; diff --git a/Apps/W1/PowerBIReports/app/src/Core/Codeunits/SetupHelper.Codeunit.al b/Apps/W1/PowerBIReports/app/src/Core/Codeunits/SetupHelper.Codeunit.al index 47a2cd54c4..91f0d50173 100644 --- a/Apps/W1/PowerBIReports/app/src/Core/Codeunits/SetupHelper.Codeunit.al +++ b/Apps/W1/PowerBIReports/app/src/Core/Codeunits/SetupHelper.Codeunit.al @@ -1,7 +1,7 @@ namespace Microsoft.PowerBIReports; using System.Integration.PowerBI; -using System.Reflection; +using System.Globalization; using System.Environment.Configuration; codeunit 36961 "Setup Helper" @@ -100,11 +100,11 @@ codeunit 36961 "Setup Helper" procedure InitializeEmbeddedAddin(PowerBIManagement: ControlAddIn PowerBIManagement; ReportId: Guid; ReportPageTok: Text) var PowerBIServiceMgt: Codeunit "Power BI Service Mgt."; - TypeHelper: Codeunit "Type Helper"; + Language: Codeunit Language; PowerBIEmbedReportUrlTemplateTxt: Label 'https://app.powerbi.com/reportEmbed?reportId=%1', Locked = true; begin PowerBiServiceMgt.InitializeAddinToken(PowerBIManagement); - PowerBIManagement.SetLocale(TypeHelper.GetCultureName()); + PowerBIManagement.SetLocale(Language.GetCurrentCultureName()); PowerBIManagement.SetFiltersVisible(true); PowerBIManagement.AddBottomPadding(true); PowerBIManagement.SetPageSelectionVisible(ReportPageTok = ''); diff --git a/Apps/W1/Shopify/app/src/Base/Pages/ShpfyShopCard.Page.al b/Apps/W1/Shopify/app/src/Base/Pages/ShpfyShopCard.Page.al index bb7607e4c3..88d17c6b52 100644 --- a/Apps/W1/Shopify/app/src/Base/Pages/ShpfyShopCard.Page.al +++ b/Apps/W1/Shopify/app/src/Base/Pages/ShpfyShopCard.Page.al @@ -248,6 +248,11 @@ page 30101 "Shpfy Shop Card" Importance = Additional; ToolTip = 'Specifies the weight unit of the Shopify Shop.'; } + field("Product Metafields To Shopify"; Rec."Product Metafields To Shopify") + { + ApplicationArea = All; + ToolTip = 'Specifies whether product/variant metafields are synchronized to Shopify.'; + } } group(PriceSynchronization) { @@ -395,6 +400,11 @@ page 30101 "Shpfy Shop Card" ApplicationArea = All; ToolTip = 'Specifies how to synchronize the county of the customer/company.'; } + field("Customer Metafields To Shopify"; Rec."Customer Metafields To Shopify") + { + ApplicationArea = All; + ToolTip = 'Specifies whether customer metafields are synchronized to Shopify.'; + } } group("B2B Company Synchronization") { @@ -440,6 +450,11 @@ page 30101 "Shpfy Shop Card" ApplicationArea = All; ToolTip = 'Specifies whether a catalog is automatically created for new companies.'; } + field("Company Metafields To Shopify"; Rec."Company Metafields To Shopify") + { + ApplicationArea = All; + ToolTip = 'Specifies whether company metafields are synchronized to Shopify.'; + } } group(OrderProcessing) { diff --git a/Apps/W1/Shopify/app/src/Base/Tables/ShpfyShop.Table.al b/Apps/W1/Shopify/app/src/Base/Tables/ShpfyShop.Table.al index 7bf13851d5..526d926453 100644 --- a/Apps/W1/Shopify/app/src/Base/Tables/ShpfyShop.Table.al +++ b/Apps/W1/Shopify/app/src/Base/Tables/ShpfyShop.Table.al @@ -751,6 +751,24 @@ table 30102 "Shpfy Shop" Caption = 'Weight Unit'; DataClassification = CustomerContent; } + field(130; "Product Metafields To Shopify"; Boolean) + { + Caption = 'Sync Product/Variant Metafields to Shopify'; + DataClassification = SystemMetadata; + InitValue = true; + } + field(131; "Customer Metafields To Shopify"; Boolean) + { + Caption = 'Sync Customer Metafields to Shopify'; + DataClassification = SystemMetadata; + InitValue = true; + } + field(132; "Company Metafields To Shopify"; Boolean) + { + Caption = 'Sync Company Metafields to Shopify'; + DataClassification = SystemMetadata; + InitValue = true; + } field(200; "Shop Id"; Integer) { DataClassification = SystemMetadata; diff --git a/Apps/W1/Shopify/app/src/Companies/Codeunits/ShpfyCompanyExport.Codeunit.al b/Apps/W1/Shopify/app/src/Companies/Codeunits/ShpfyCompanyExport.Codeunit.al index 1eca928a59..73df9a0403 100644 --- a/Apps/W1/Shopify/app/src/Companies/Codeunits/ShpfyCompanyExport.Codeunit.al +++ b/Apps/W1/Shopify/app/src/Companies/Codeunits/ShpfyCompanyExport.Codeunit.al @@ -203,7 +203,8 @@ codeunit 30284 "Shpfy Company Export" CompanyLocation.Modify(); end; - UpdateMetafields(ShopifyCompany.Id); + if Shop."Company Metafields To Shopify" then + UpdateMetafields(ShopifyCompany.Id); end; local procedure UpdateMetafields(ComppanyId: BigInteger) diff --git a/Apps/W1/Shopify/app/src/Customers/Codeunits/ShpfyCustomerExport.Codeunit.al b/Apps/W1/Shopify/app/src/Customers/Codeunits/ShpfyCustomerExport.Codeunit.al index 0958950e9f..554eebeb37 100644 --- a/Apps/W1/Shopify/app/src/Customers/Codeunits/ShpfyCustomerExport.Codeunit.al +++ b/Apps/W1/Shopify/app/src/Customers/Codeunits/ShpfyCustomerExport.Codeunit.al @@ -268,7 +268,8 @@ codeunit 30116 "Shpfy Customer Export" CustomerAddress.Modify(); end; - UpdateMetafields(ShopifyCustomer.Id); + if Shop."Customer Metafields To Shopify" then + UpdateMetafields(ShopifyCustomer.Id); end; internal procedure SetCreateCustomers(NewCustomers: Boolean) diff --git a/Apps/W1/Shopify/app/src/Invoicing/Codeunits/ShpfyDraftOrdersAPI.Codeunit.al b/Apps/W1/Shopify/app/src/Invoicing/Codeunits/ShpfyDraftOrdersAPI.Codeunit.al index 01698dd3ef..e1940a000d 100644 --- a/Apps/W1/Shopify/app/src/Invoicing/Codeunits/ShpfyDraftOrdersAPI.Codeunit.al +++ b/Apps/W1/Shopify/app/src/Invoicing/Codeunits/ShpfyDraftOrdersAPI.Codeunit.al @@ -293,9 +293,10 @@ codeunit 30159 "Shpfy Draft Orders API" Currency: Record Currency; begin if Currency.Get(CurrencyCode) then - exit(Currency."ISO Code") - else - exit(CopyStr(CurrencyCode, 1, 3)); // If it is not found in the currency table then it is LCY + if Currency."ISO Code" <> '' then + exit(Currency."ISO Code"); + + exit(CopyStr(CurrencyCode, 1, 3)); // If it is not found in the currency table then it is LCY end; local procedure AddFieldToGraphQuery(var GraphQuery: TextBuilder; FieldName: Text; ValueAsVariant: Variant; ValueAsString: Boolean): Boolean diff --git a/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldValueType.Enum.al b/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldValueType.Enum.al index fcf76e44aa..4308aaed0d 100644 --- a/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldValueType.Enum.al +++ b/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldValueType.Enum.al @@ -1,3 +1,4 @@ +#if not CLEANSCHEMA29 namespace Microsoft.Integration.Shopify; /// @@ -10,7 +11,9 @@ enum 30102 "Shpfy Metafield Value Type" Extensible = false; ObsoleteState = Pending; ObsoleteReason = 'Value Type is obsolete in Shopify API. Use Metafield Type instead.'; - ObsoleteTag = '25.0'; +#pragma warning disable AS0074 + ObsoleteTag = '26.0'; +#pragma warning restore AS0074 value(0; String) { @@ -26,4 +29,5 @@ enum 30102 "Shpfy Metafield Value Type" { Caption = 'Json'; } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Apps/W1/Shopify/app/src/Metafields/Tables/ShpfyMetafield.Table.al b/Apps/W1/Shopify/app/src/Metafields/Tables/ShpfyMetafield.Table.al index 4ffd3f0ccd..d69d2e1d78 100644 --- a/Apps/W1/Shopify/app/src/Metafields/Tables/ShpfyMetafield.Table.al +++ b/Apps/W1/Shopify/app/src/Metafields/Tables/ShpfyMetafield.Table.al @@ -28,14 +28,20 @@ table 30101 "Shpfy Metafield" } #pragma warning restore AS0086 +#if not CLEANSCHEMA28 field(3; "Owner Resource"; Text[50]) { Caption = 'Owner Resource'; DataClassification = SystemMetadata; - ObsoleteState = Pending; ObsoleteReason = 'Owner Resource is obsolete. Use Owner Type instead.'; +#if CLEAN25 + ObsoleteState = Removed; + ObsoleteTag = '28.0'; +#else + ObsoleteState = Pending; ObsoleteTag = '25.0'; - +#endif +#if not CLEAN25 trigger OnValidate() begin case "Owner Resource" of @@ -47,7 +53,9 @@ table 30101 "Shpfy Metafield" Validate("Owner Type", "Owner Type"::ProductVariant); end; end; +#endif } +#endif field(4; "Owner Id"; BigInteger) { @@ -63,14 +71,23 @@ table 30101 "Shpfy Metafield" } #pragma warning restore AS0086 +#if not CLEANSCHEMA28 +#pragma warning disable AS0105 field(6; "Value Type"; Enum "Shpfy Metafield Value Type") { Caption = 'Value Type'; DataClassification = CustomerContent; - ObsoleteState = Pending; ObsoleteReason = 'Value Type is obsolete in Shopify API. Use Type instead.'; +#if CLEAN25 + ObsoleteState = Removed; + ObsoleteTag = '28.0'; +#else + ObsoleteState = Pending; ObsoleteTag = '25.0'; +#endif } +#endif +#pragma warning restore AS0105 #pragma warning disable AS0086 // false positive on extending the field length on internal table field(7; Value; Text[2048]) diff --git a/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyProductExport.Codeunit.al b/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyProductExport.Codeunit.al index 7160dbfe3f..5326e819d8 100644 --- a/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyProductExport.Codeunit.al +++ b/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyProductExport.Codeunit.al @@ -724,7 +724,8 @@ codeunit 30178 "Shpfy Product Export" until ItemUnitofMeasure.Next() = 0; end; - UpdateMetafields(ShopifyProduct.Id); + if Shop."Product Metafields To Shopify" then + UpdateMetafields(ShopifyProduct.Id); UpdateProductTranslations(ShopifyProduct.Id, Item) end; end; @@ -733,6 +734,9 @@ codeunit 30178 "Shpfy Product Export" var ShpfyVariant: Record "Shpfy Variant"; begin + if OnlyUpdatePrice then + exit; + MetafieldAPI.CreateOrUpdateMetafieldsInShopify(Database::"Shpfy Product", ProductId); ShpfyVariant.SetRange("Product Id", ProductId); @@ -823,10 +827,13 @@ codeunit 30178 "Shpfy Product Export" TranslationAPI: Codeunit "Shpfy Translation API"; Digests: Dictionary of [Text, Text]; begin - Digests := TranslationAPI.RetrieveTranslatableContentDigests(TempTranslation."Resource Type", TempTranslation."Resource ID"); - ShopifyLanguage.SetRange("Shop Code", Shop.Code); ShopifyLanguage.SetRange("Sync Translations", true); + if ShopifyLanguage.IsEmpty() then + exit; + + Digests := TranslationAPI.RetrieveTranslatableContentDigests(TempTranslation."Resource Type", TempTranslation."Resource ID"); + if ShopifyLanguage.FindSet() then repeat ICreateTranslation.CreateTranslation(RecVariant, ShopifyLanguage, TempTranslation, Digests); diff --git a/Apps/W1/StatisticalAccounts/app/src/StatAccFinReportingMgt.Codeunit.al b/Apps/W1/StatisticalAccounts/app/src/StatAccFinReportingMgt.Codeunit.al index d56afe51c5..59fe6c23a7 100644 --- a/Apps/W1/StatisticalAccounts/app/src/StatAccFinReportingMgt.Codeunit.al +++ b/Apps/W1/StatisticalAccounts/app/src/StatAccFinReportingMgt.Codeunit.al @@ -155,6 +155,7 @@ codeunit 2622 "Stat. Acc. Fin Reporting Mgt" StatisticalLedgerEntry.SetCurrentKey("Statistical Account No.", "Posting Date"); StatisticalLedgerEntry.SetRange("Statistical Account No.", StatisticalAccount."No."); StatisticalAccount.CopyFilter("Date Filter", StatisticalLedgerEntry."Posting Date"); + StatisticalAccount.CopyFilter("No.", StatisticalLedgerEntry."Statistical Account No."); SourceAccScheduleLine.CopyFilter("Dimension 1 Filter", StatisticalLedgerEntry."Global Dimension 1 Code"); SourceAccScheduleLine.CopyFilter("Dimension 2 Filter", StatisticalLedgerEntry."Global Dimension 2 Code"); diff --git a/Apps/W1/StatisticalAccounts/test/src/StatisticalAccountTest.Codeunit.al b/Apps/W1/StatisticalAccounts/test/src/StatisticalAccountTest.Codeunit.al index e30470f843..b0e98f38d6 100644 --- a/Apps/W1/StatisticalAccounts/test/src/StatisticalAccountTest.Codeunit.al +++ b/Apps/W1/StatisticalAccounts/test/src/StatisticalAccountTest.Codeunit.al @@ -543,6 +543,66 @@ codeunit 139683 "Statistical Account Test" end; + [Test] + [HandlerFunctions('MessageDialogHandler,ConfirmationDialogHandler')] + procedure AccountRangeShouldRecognizedForTotalingTypeStatisticalAccountInFinancialReport() + var + ColumnLayout: Record "Column Layout"; + ColumnLayoutName: Record "Column Layout Name"; + AccScheduleName: Record "Acc. Schedule Name"; + AccScheduleLine: Record "Acc. Schedule Line"; + FinancialReport: Record "Financial Report"; + StatisticalAccount: array[3] of Record "Statistical Account"; + FinancialReports: TestPage "Financial Reports"; + AccScheduleOverview: TestPage "Acc. Schedule Overview"; + ExpectedAmount: Decimal; + DateFilter: Text; + begin + // [SCENARIO 556238] Account ranges are not getting recognized when the Totaling Type is Statistical Account in Financial reports row definition + Initialize(); + + // [GIVEN] Setup Demo Data. + SetupFinancialReport(); + + // [GIVEN] Create Statistical Account and Post Journal + CreateStatisticalAccount(StatisticalAccount, 3); + CreateStatisticalAccountsJournalAndPost(StatisticalAccount, 3, ExpectedAmount); + + // [GIVEN] Create a Column Name and Column Layout. + LibraryERM.CreateColumnLayoutName(ColumnLayoutName); + LibraryERM.CreateColumnLayout(ColumnLayout, ColumnLayoutName.Name); + ColumnLayout.Validate("Column Type", "Column Layout Type"::"Balance at Date"); + ColumnLayout.Modify(); + + // [GIVEN] Create a Account Schedule Name and Line with "Statistical Account" and Range in Totaling + LibraryERM.CreateAccScheduleName(AccScheduleName); + LibraryERM.CreateAccScheduleLine(AccScheduleLine, AccScheduleName.Name); + AccScheduleLine.Validate("Totaling Type", "Acc. Schedule Line Totaling Type"::"Statistical Account"); + AccScheduleLine.Validate(Totaling, StatisticalAccount[1]."No." + '..' + StatisticalAccount[3]."No."); + AccScheduleLine.Modify(); + + // [GIVEN] Update "Financial Report Column Group" in Financial report. + FinancialReport.Get(AccScheduleLine."Schedule Name"); + FinancialReport.Validate("Financial Report Column Group", ColumnLayout."Column Layout Name"); + FinancialReport.Modify(); + + // [WHEN] Run Account Schedule Overview with "Period Type" as year. + FinancialReports.OpenEdit(); + FinancialReports.Filter.SetFilter(Name, AccScheduleName.Name); + AccScheduleOverview.Trap(); + FinancialReports.Overview.Invoke(); + AccScheduleOverview.PeriodType.SetValue("Analysis Period Type"::Year); + + // [GIVEN] Save the Date Filter. + DateFilter := Format(AccScheduleOverview.DateFilter); + + // [VERIFY] Verify Balance should be shown correctly filtered by Range in the Financial Reports. + Assert.AreEqual( + ExpectedAmount, + AccScheduleOverview.ColumnValues1.AsDecimal(), + StrSubstNo(BalanceMustBeEqualErr, ExpectedAmount)); + end; + local procedure SetupFinancialReport() var AccScheduleLine: Record "Acc. Schedule Line"; @@ -850,6 +910,38 @@ codeunit 139683 "Statistical Account Test" until StatisticalAccountJournalLine.Next() = 0; end; + local procedure CreateStatisticalAccount(var StatisticalAccount: array[3] of Record "Statistical Account"; NoOfLines: Integer) + var + i: Integer; + begin + for i := 1 to NoOfLines do begin + StatisticalAccount[i]."No." := Format(LibraryRandom.RandIntInRange(1000, 1000) + i); + StatisticalAccount[i].Name := StatisticalAccount[i]."No."; + StatisticalAccount[i].Insert(); + end; + end; + + local procedure CreateStatisticalAccountsJournalAndPost( + var StatisticalAccount: array[3] of Record "Statistical Account"; + NoOfLines: Integer; var Amount: Decimal) + var + StatisticalAccountsJournal: TestPage "Statistical Accounts Journal"; + i: Integer; + begin + for i := 1 to NoOfLines do begin + StatisticalAccountsJournal.OpenEdit(); + StatisticalAccountsJournal.New(); + StatisticalAccountsJournal."Posting Date".SetValue(WorkDate()); + StatisticalAccountsJournal."Document No.".SetValue(LibraryRandom.RandText(10)); + StatisticalAccountsJournal.StatisticalAccountNo.SetValue(StatisticalAccount[i]."No."); + StatisticalAccountsJournal.Amount.SetValue(LibraryRandom.RandIntInRange(1000, 1000)); + Amount += StatisticalAccountsJournal.Amount.AsDecimal(); + StatisticalAccountsJournal.Close(); + end; + StatisticalAccountsJournal.OpenEdit(); + RegisterJournal(StatisticalAccountsJournal); + end; + [ModalPageHandler] procedure StatAccJnlBatcheModalPageHandler(var StatBatch: TestPage "Statistical Acc. Journal Batch") begin diff --git a/Apps/W1/SubscriptionBilling/App/Usage Based Billing/Tables/UsageDataCustomer.Table.al b/Apps/W1/SubscriptionBilling/App/Usage Based Billing/Tables/UsageDataCustomer.Table.al index 91b0838b80..955bd44bae 100644 --- a/Apps/W1/SubscriptionBilling/App/Usage Based Billing/Tables/UsageDataCustomer.Table.al +++ b/Apps/W1/SubscriptionBilling/App/Usage Based Billing/Tables/UsageDataCustomer.Table.al @@ -1,6 +1,5 @@ namespace Microsoft.SubscriptionBilling; -using System.Reflection; using System.Globalization; using Microsoft.Sales.Customer; @@ -58,7 +57,7 @@ table 8012 "Usage Data Customer" trigger OnLookup() var Language: Record Language; - TypeHelper: Codeunit "Type Helper"; + LanguageCU: Codeunit Language; DotNet_CultureInfo: Codeunit DotNet_CultureInfo; begin if Culture <> '' then begin @@ -67,7 +66,7 @@ table 8012 "Usage Data Customer" end; if Page.RunModal(0, Language) = Action::LookupOK then - Rec.Validate(Culture, TypeHelper.LanguageIDToCultureName(Language."Windows Language ID")); + Rec.Validate(Culture, LanguageCU.GetCultureName(Language."Windows Language ID")); end; } field(9; "Supplier Reference Entry No."; Integer) diff --git a/Apps/W1/Sustainability/app/src/Assembly/SustAssemblyLine.TableExt.al b/Apps/W1/Sustainability/app/src/Assembly/SustAssemblyLine.TableExt.al new file mode 100644 index 0000000000..d183993928 --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Assembly/SustAssemblyLine.TableExt.al @@ -0,0 +1,184 @@ +namespace Microsoft.Sustainability.Assembly; + +using Microsoft.Assembly.Document; +using Microsoft.Sustainability.Account; +using Microsoft.Sustainability.Setup; +using Microsoft.Inventory.Item; + +tableextension 6252 "Sust. Assembly Line" extends "Assembly Line" +{ + fields + { + field(6210; "Sust. Account No."; Code[20]) + { + Caption = 'Sustainability Account No.'; + TableRelation = "Sustainability Account" where("Account Type" = const(Posting), Blocked = const(false)); + DataClassification = CustomerContent; + + trigger OnValidate() + begin + if Rec."Sust. Account No." <> xRec."Sust. Account No." then + ClearEmissionInformation(Rec); + + if Rec."Sust. Account No." = '' then begin + Rec.Validate("Sust. Account Category", ''); + "Sust. Account Name" := ''; + end else begin + ValidateEmissionPrerequisite(Rec, Rec.FieldNo("Sust. Account No.")); + CopyFromSustainabilityAccount(Rec); + UpdateCO2eInformation(); + end; + end; + } + field(6211; "Sust. Account Name"; Text[100]) + { + Caption = 'Sustainability Account Name'; + DataClassification = CustomerContent; + } + field(6212; "Sust. Account Category"; Code[20]) + { + Caption = 'Sustainability Account Category'; + Editable = false; + TableRelation = "Sustain. Account Category"; + DataClassification = CustomerContent; + + trigger OnValidate() + begin + if Rec."Sust. Account Category" <> '' then + ValidateEmissionPrerequisite(Rec, Rec.FieldNo("Sust. Account Category")) + else + Rec.Validate("Sust. Account Subcategory", ''); + + if "Sust. Account Category" <> xRec."Sust. Account Category" then + Rec.Validate("Shortcut Dimension 1 Code", ''); + end; + } + field(6213; "Sust. Account Subcategory"; Code[20]) + { + Caption = 'Sustainability Account Subcategory'; + Editable = false; + TableRelation = "Sustain. Account Subcategory".Code where("Category Code" = field("Sust. Account Category")); + DataClassification = CustomerContent; + + trigger OnValidate() + begin + if Rec."Sust. Account Subcategory" <> '' then + ValidateEmissionPrerequisite(Rec, Rec.FieldNo("Sust. Account Subcategory")); + end; + } + field(6214; "CO2e per Unit"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + Caption = 'CO2e per Unit'; + DataClassification = CustomerContent; + + trigger OnValidate() + begin + if Rec."CO2e per Unit" <> 0 then + ValidateEmissionPrerequisite(Rec, Rec.FieldNo("CO2e per Unit")); + + UpdateSustainabilityEmission(Rec); + end; + } + field(6215; "Total CO2e"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + Caption = 'Total CO2e'; + DataClassification = CustomerContent; + + trigger OnValidate() + begin + if Rec."Total CO2e" <> 0 then + ValidateEmissionPrerequisite(Rec, Rec.FieldNo("Total CO2e")); + + UpdateEmissionPerUnit(Rec); + end; + } + field(6216; "Posted Total CO2e"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + Caption = 'Posted Total CO2e'; + Editable = false; + DataClassification = CustomerContent; + } + } + + procedure UpdateSustainabilityEmission(var AssemblyLine: Record "Assembly Line") + begin + AssemblyLine."Total CO2e" := AssemblyLine."CO2e per Unit" * AssemblyLine."Qty. per Unit of Measure" * AssemblyLine.Quantity; + end; + + procedure UpdateEmissionPerUnit(var AssemblyLine: Record "Assembly Line") + var + Denominator: Decimal; + begin + AssemblyLine."CO2e Per Unit" := 0; + + if (AssemblyLine."Qty. per Unit of Measure" = 0) or (AssemblyLine.Quantity = 0) then + exit; + + Denominator := AssemblyLine."Qty. per Unit of Measure" * AssemblyLine.Quantity; + if AssemblyLine."Total CO2e" <> 0 then + AssemblyLine."CO2e per Unit" := AssemblyLine."Total CO2e" / Denominator; + end; + + local procedure ClearEmissionInformation(var AssemblyLine: Record "Assembly Line") + begin + AssemblyLine.Validate("CO2e per Unit", 0); + end; + + local procedure ValidateEmissionPrerequisite(AssemblyLine: Record "Assembly Line"; CurrentFieldNo: Integer) + var + SustAccountCategory: Record "Sustain. Account Category"; + begin + case CurrentFieldNo of + AssemblyLine.FieldNo("CO2e per Unit"), + AssemblyLine.FieldNo("Total CO2e"): + AssemblyLine.TestField("Sust. Account No."); + AssemblyLine.FieldNo("Sust. Account No."), + AssemblyLine.FieldNo("Sust. Account Category"), + AssemblyLine.FieldNo("Sust. Account Subcategory"), + AssemblyLine.FieldNo("Sust. Account Name"): + begin + AssemblyLine.TestField("No."); + + if SustAccountCategory.Get(AssemblyLine."Sust. Account Category") then + if SustAccountCategory."Water Intensity" or SustAccountCategory."Waste Intensity" or SustAccountCategory."Discharged Into Water" then + Error(NotAllowedToUseSustAccountForWaterOrWasteErr, AssemblyLine."Sust. Account No."); + end; + end; + end; + + local procedure UpdateCO2eInformation() + var + Item: Record Item; + begin + if Rec.Type <> Rec.Type::Item then + exit; + + if not Item.Get(Rec."No.") then + exit; + + Rec.Validate("CO2e per Unit", Item."CO2e per Unit"); + end; + + local procedure CopyFromSustainabilityAccount(var AssemblyLine: Record "Assembly Line") + var + SustainabilityAccount: Record "Sustainability Account"; + begin + SustainabilityAccount.Get(AssemblyLine."Sust. Account No."); + SustainabilityAccount.CheckAccountReadyForPosting(); + SustainabilityAccount.TestField("Direct Posting", true); + + AssemblyLine.Validate("Sust. Account Name", SustainabilityAccount.Name); + AssemblyLine.Validate("Sust. Account Category", SustainabilityAccount.Category); + AssemblyLine.Validate("Sust. Account Subcategory", SustainabilityAccount.Subcategory); + end; + + var + SustainabilitySetup: Record "Sustainability Setup"; + NotAllowedToUseSustAccountForWaterOrWasteErr: Label 'It is not allowed to use Sustainability Account %1 for water or waste in Assembly document.', Comment = '%1 = Sust. Account No.'; +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Assembly/SustAssemblyOrder.PageExt.al b/Apps/W1/Sustainability/app/src/Assembly/SustAssemblyOrder.PageExt.al new file mode 100644 index 0000000000..1dee4c15f0 --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Assembly/SustAssemblyOrder.PageExt.al @@ -0,0 +1,28 @@ +namespace Microsoft.Sustainability.Assembly; + +using Microsoft.Assembly.Document; + +pageextension 6258 "Sust. Assembly Order" extends "Assembly Order" +{ + layout + { + addafter("Cost Amount") + { + field("Sust. Account No."; Rec."Sust. Account No.") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the value of the Sustainability Account No. field.'; + } + field("CO2e per Unit"; Rec."CO2e per Unit") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the value of the CO2e per Unit field.'; + } + field("Total CO2e"; Rec."Total CO2e") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the value of the Total CO2e field.'; + } + } + } +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Assembly/SustAssemblyOrderSubform.PageExt.al b/Apps/W1/Sustainability/app/src/Assembly/SustAssemblyOrderSubform.PageExt.al new file mode 100644 index 0000000000..38742f279c --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Assembly/SustAssemblyOrderSubform.PageExt.al @@ -0,0 +1,26 @@ +namespace Microsoft.Sustainability.Assembly; + +using Microsoft.Assembly.Document; + +pageextension 6253 "Sust. Assembly Order Subform" extends "Assembly Order Subform" +{ + layout + { + addafter("Location Code") + { + field("Sust. Account No."; Rec."Sust. Account No.") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the value of the Sustainability Account No. field.'; + } + } + addafter("Cost Amount") + { + field("Total CO2e"; Rec."Total CO2e") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the value of the Total CO2e field.'; + } + } + } +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Assembly/SustAssemblySubscriber.Codeunit.al b/Apps/W1/Sustainability/app/src/Assembly/SustAssemblySubscriber.Codeunit.al new file mode 100644 index 0000000000..4ae1ac390c --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Assembly/SustAssemblySubscriber.Codeunit.al @@ -0,0 +1,311 @@ +namespace Microsoft.Sustainability.Assembly; + +using Microsoft.Assembly.Document; +using Microsoft.Inventory.Item; +using Microsoft.Projects.Resources.Resource; +using Microsoft.Inventory.Journal; +using Microsoft.Sustainability.Journal; +using Microsoft.Sustainability.Posting; +using Microsoft.Sustainability.Account; +using Microsoft.Assembly.Posting; +using Microsoft.Assembly.History; + +codeunit 6255 "Sust. Assembly Subscriber" +{ + [EventSubscriber(ObjectType::Table, Database::"Assembly Line", 'OnAfterCopyFromItem', '', false, false)] + local procedure OnAfterCopyFromItem(var AssemblyLine: Record "Assembly Line"; Item: Record Item) + begin + AssemblyLine.Validate("Sust. Account No.", Item."Default Sust. Account"); + end; + + [EventSubscriber(ObjectType::Table, Database::"Assembly Header", 'OnAfterValidateEvent', "Item No.", false, false)] + local procedure OnAfterValidateNoEventOnAssemblyHeader(var Rec: Record "Assembly Header") + begin + UpdateDefaultSustAccOnAssemblyHeader(Rec); + end; + + [EventSubscriber(ObjectType::Table, Database::"Assembly Line", 'OnAfterCopyFromResource', '', false, false)] + local procedure OnAfterCopyFromResource(var AssemblyLine: Record "Assembly Line"; Resource: Record Resource) + begin + AssemblyLine.Validate("Sust. Account No.", Resource."Default Sust. Account"); + end; + + [EventSubscriber(ObjectType::Table, Database::"Assembly Line", 'OnAfterValidateEvent', "No.", false, false)] + local procedure OnAfterValidateNoEvent(var Rec: Record "Assembly Line") + begin + if Rec."No." = '' then + if Rec."Sust. Account No." <> '' then + Rec.Validate("Sust. Account No.", ''); + end; + + [EventSubscriber(ObjectType::Table, Database::"Assembly Header", 'OnValiateQuantityOnAfterCalcBaseQty', '', false, false)] + local procedure OnValiateQuantityOnAfterCalcBaseQty(var AssemblyHeader: Record "Assembly Header") + begin + AssemblyHeader.UpdateSustainabilityEmission(AssemblyHeader); + end; + + [EventSubscriber(ObjectType::Table, Database::"Assembly Line", 'OnAfterInitQtyToConsume', '', false, false)] + local procedure OnAfterInitQtyToConsume(var AssemblyLine: Record "Assembly Line") + begin + AssemblyLine.UpdateSustainabilityEmission(AssemblyLine); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Assembly-Post", 'OnAfterCreateItemJnlLineFromAssemblyHeader', '', false, false)] + local procedure OnAfterCreateItemJnlLineFromAssemblyHeader(var ItemJournalLine: Record "Item Journal Line"; AssemblyHeader: Record "Assembly Header") + begin + if (ItemJournalLine.Quantity <> 0) then + UpdateSustainabilityItemJournalLineForOutput(ItemJournalLine, AssemblyHeader); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Assembly-Post", 'OnBeforePostItemConsumption', '', false, false)] + local procedure OnBeforePostItemConsumption(var ItemJournalLine: Record "Item Journal Line"; var AssemblyHeader: Record "Assembly Header"; var AssemblyLine: Record "Assembly Line") + begin + if (ItemJournalLine.Quantity <> 0) then + UpdateSustainabilityItemJournalLineForConsumption(ItemJournalLine, AssemblyLine); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Assembly-Post", 'OnPostHeaderOnAfterPostItemOutput', '', false, false)] + local procedure OnPostHeaderOnAfterPostItemOutput(var AssemblyHeader: Record "Assembly Header"; var HeaderQtyBase: Decimal) + var + PostedAssemblyHeader: Record "Posted Assembly Header"; + begin + if PostedAssemblyHeader.Get(AssemblyHeader."Posting No.") then + PostSustainabilityLineForOutput(AssemblyHeader, PostedAssemblyHeader."Posting Date", HeaderQtyBase, PostedAssemblyHeader."Source Code", AssemblyHeader."Posting No."); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Assembly-Post", 'OnBeforeAssemblyLineModify', '', false, false)] + local procedure OnBeforeAssemblyLineModify(var AssemblyLine: Record "Assembly Line"; QtyToConsumeBase: Decimal) + var + AssemblyHeader: Record "Assembly Header"; + PostedAssemblyHeader: Record "Posted Assembly Header"; + begin + if AssemblyHeader.Get(AssemblyLine."Document Type", AssemblyLine."Document No.") and PostedAssemblyHeader.Get(AssemblyHeader."Posting No.") then + PostSustainabilityLineForConsumption(AssemblyLine, PostedAssemblyHeader."Posting Date", QtyToConsumeBase, PostedAssemblyHeader."Source Code", AssemblyHeader."Posting No."); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Assembly-Post", 'OnUndoPostHeaderOnAfterTransferFields', '', false, false)] + local procedure OnUndoPostHeaderOnAfterTransferFields(PostedAssemblyHeader: Record "Posted Assembly Header"; var AssemblyHeader: Record "Assembly Header") + begin + if AssemblyHeader."Quantity (Base)" <> 0 then + PostSustainabilityLineForOutput(AssemblyHeader, PostedAssemblyHeader."Posting Date", -AssemblyHeader."Quantity (Base)", PostedAssemblyHeader."Source Code", PostedAssemblyHeader."No."); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Assembly-Post", 'OnUndoPostLinesOnAfterTransferFields', '', false, false)] + local procedure OnUndoPostLinesOnAfterTransferFields(var AssemblyLine: Record "Assembly Line"; PostedAssemblyHeader: Record "Posted Assembly Header") + begin + if AssemblyLine."Quantity (Base)" <> 0 then + PostSustainabilityLineForConsumption(AssemblyLine, PostedAssemblyHeader."Posting Date", -AssemblyLine."Quantity (Base)", PostedAssemblyHeader."Source Code", PostedAssemblyHeader."No."); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Assembly-Post", 'OnAfterPostedAssemblyHeaderModify', '', false, false)] + local procedure OnAfterPostedAssemblyHeaderModify(var PostedAssemblyHeader: Record "Posted Assembly Header"; AssemblyHeader: Record "Assembly Header") + begin + UpdatePostedSustainabilityEmission(AssemblyHeader."CO2e per Unit", AssemblyHeader."Qty. per Unit of Measure", PostedAssemblyHeader.Quantity, 1, PostedAssemblyHeader."Total CO2e"); + PostedAssemblyHeader.Modify(); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Assembly-Post", 'OnBeforePostedAssemblyLineInsert', '', false, false)] + local procedure OnAfterPostedAssemblyLineInsert(AssemblyLine: Record "Assembly Line"; var PostedAssemblyLine: Record "Posted Assembly Line") + begin + UpdatePostedSustainabilityEmission(AssemblyLine."CO2e per Unit", AssemblyLine."Qty. per Unit of Measure", PostedAssemblyLine.Quantity, 1, PostedAssemblyLine."Total CO2e"); + end; + + local procedure UpdateDefaultSustAccOnAssemblyHeader(var AssemblyHeader: Record "Assembly Header") + var + Item: Record Item; + begin + if AssemblyHeader."Item No." = '' then + AssemblyHeader.Validate("Sust. Account No.", '') + else begin + Item.Get(AssemblyHeader."Item No."); + + AssemblyHeader.Validate("Sust. Account No.", Item."Default Sust. Account"); + end; + end; + + local procedure UpdateSustainabilityItemJournalLineForOutput(var ItemJournalLine: Record "Item Journal Line"; var AssemblyHeader: Record "Assembly Header") + var + GHGCredit: Boolean; + Sign: Integer; + CO2eToPost: Decimal; + begin + GHGCredit := IsGHGCreditLine(AssemblyHeader."Item No."); + Sign := GetPostingSign(ItemJournalLine.Quantity, 0, GHGCredit); + + CO2eToPost := AssemblyHeader."CO2e per Unit" * ItemJournalLine.Quantity * AssemblyHeader."Qty. per Unit of Measure" * Sign; + if not CanPostSustainabilityJnlLine(AssemblyHeader."Sust. Account No.", AssemblyHeader."Sust. Account Category", AssemblyHeader."Sust. Account Subcategory", CO2eToPost) then + exit; + + ItemJournalLine."Sust. Account No." := AssemblyHeader."Sust. Account No."; + ItemJournalLine."Sust. Account Name" := AssemblyHeader."Sust. Account Name"; + ItemJournalLine."Sust. Account Category" := AssemblyHeader."Sust. Account Category"; + ItemJournalLine."Sust. Account Subcategory" := AssemblyHeader."Sust. Account Subcategory"; + ItemJournalLine."CO2e per Unit" := AssemblyHeader."CO2e per Unit"; + ItemJournalLine."Total CO2e" := CO2eToPost; + end; + + local procedure UpdateSustainabilityItemJournalLineForConsumption(var ItemJournalLine: Record "Item Journal Line"; var AssemblyLine: Record "Assembly Line") + var + GHGCredit: Boolean; + Sign: Integer; + CO2eToPost: Decimal; + begin + if AssemblyLine.Type = AssemblyLine.Type::Item then + GHGCredit := IsGHGCreditLine(AssemblyLine."No."); + + Sign := GetPostingSign(0, ItemJournalLine.Quantity, GHGCredit); + + CO2eToPost := AssemblyLine."CO2e per Unit" * ItemJournalLine.Quantity * AssemblyLine."Qty. per Unit of Measure" * Sign; + if not CanPostSustainabilityJnlLine(AssemblyLine."Sust. Account No.", AssemblyLine."Sust. Account Category", AssemblyLine."Sust. Account Subcategory", CO2eToPost) then + exit; + + ItemJournalLine."Sust. Account No." := AssemblyLine."Sust. Account No."; + ItemJournalLine."Sust. Account Name" := AssemblyLine."Sust. Account Name"; + ItemJournalLine."Sust. Account Category" := AssemblyLine."Sust. Account Category"; + ItemJournalLine."Sust. Account Subcategory" := AssemblyLine."Sust. Account Subcategory"; + ItemJournalLine."CO2e per Unit" := AssemblyLine."CO2e per Unit"; + ItemJournalLine."Total CO2e" := CO2eToPost; + end; + + local procedure UpdatePostedSustainabilityEmission(CO2ePerUnit: Decimal; QtyperUnitofMeasure: Decimal; Quantity: Decimal; Sign: Integer; var PostedEmissionCO2e: Decimal) + begin + PostedEmissionCO2e := (CO2ePerUnit * Abs(Quantity) * QtyperUnitofMeasure) * Sign; + end; + + local procedure PostSustainabilityLineForOutput(var AssemblyHeader: Record "Assembly Header"; PostingDate: Date; QtyToOutputBase: Decimal; SrcCode: Code[10]; GenJnlLineDocNo: Code[20]) + var + SustainabilityJnlLine: Record "Sustainability Jnl. Line"; + SustainabilityPostMgt: Codeunit "Sustainability Post Mgt"; + GHGCredit: Boolean; + CO2eToPost: Decimal; + Sign: Integer; + begin + if (QtyToOutputBase = 0) then + exit; + + GHGCredit := IsGHGCreditLine(AssemblyHeader."Item No."); + + Sign := GetPostingSign(QtyToOutputBase, 0, GHGCredit); + CO2eToPost := AssemblyHeader."CO2e per Unit" * QtyToOutputBase * AssemblyHeader."Qty. per Unit of Measure" * Sign; + if not CanPostSustainabilityJnlLine(AssemblyHeader."Sust. Account No.", AssemblyHeader."Sust. Account Category", AssemblyHeader."Sust. Account Subcategory", CO2eToPost) then + exit; + + SustainabilityJnlLine.Init(); + SustainabilityJnlLine."Source Code" := SrcCode; + SustainabilityJnlLine.Validate("Posting Date", PostingDate); + if GHGCredit then + SustainabilityJnlLine.Validate("Document Type", SustainabilityJnlLine."Document Type"::"GHG Credit") + else + SustainabilityJnlLine.Validate("Document Type", SustainabilityJnlLine."Document Type"::Invoice); + + SustainabilityJnlLine.Validate("Document No.", GenJnlLineDocNo); + SustainabilityJnlLine.Validate("Account No.", AssemblyHeader."Sust. Account No."); + SustainabilityJnlLine.Validate("Account Category", AssemblyHeader."Sust. Account Category"); + SustainabilityJnlLine.Validate("Account Subcategory", AssemblyHeader."Sust. Account Subcategory"); + SustainabilityJnlLine.Validate("Unit of Measure", AssemblyHeader."Unit of Measure Code"); + SustainabilityJnlLine."Dimension Set ID" := AssemblyHeader."Dimension Set ID"; + SustainabilityJnlLine."Shortcut Dimension 1 Code" := AssemblyHeader."Shortcut Dimension 1 Code"; + SustainabilityJnlLine."Shortcut Dimension 2 Code" := AssemblyHeader."Shortcut Dimension 2 Code"; + SustainabilityJnlLine.Validate("CO2e Emission", CO2eToPost); + SustainabilityPostMgt.InsertLedgerEntry(SustainabilityJnlLine, AssemblyHeader); + + AssemblyHeader."Posted Total CO2e" += CO2eToPost; + end; + + local procedure PostSustainabilityLineForConsumption(var AssemblyLine: Record "Assembly Line"; PostingDate: Date; QtyToConsumeBase: Decimal; SrcCode: Code[10]; GenJnlLineDocNo: Code[20]) + var + SustainabilityJnlLine: Record "Sustainability Jnl. Line"; + SustainabilityPostMgt: Codeunit "Sustainability Post Mgt"; + GHGCredit: Boolean; + CO2eToPost: Decimal; + Sign: Integer; + begin + if (QtyToConsumeBase = 0) then + exit; + + if AssemblyLine.Type = AssemblyLine.Type::Item then + GHGCredit := IsGHGCreditLine(AssemblyLine."No."); + + Sign := GetPostingSign(0, QtyToConsumeBase, GHGCredit); + CO2eToPost := AssemblyLine."CO2e per Unit" * QtyToConsumeBase * AssemblyLine."Qty. per Unit of Measure" * Sign; + if not CanPostSustainabilityJnlLine(AssemblyLine."Sust. Account No.", AssemblyLine."Sust. Account Category", AssemblyLine."Sust. Account Subcategory", CO2eToPost) then + exit; + + SustainabilityJnlLine.Init(); + SustainabilityJnlLine."Journal Template Name" := ''; + SustainabilityJnlLine."Journal Batch Name" := ''; + SustainabilityJnlLine."Source Code" := SrcCode; + SustainabilityJnlLine.Validate("Posting Date", PostingDate); + if GHGCredit then + SustainabilityJnlLine.Validate("Document Type", SustainabilityJnlLine."Document Type"::"GHG Credit") + else + SustainabilityJnlLine.Validate("Document Type", SustainabilityJnlLine."Document Type"::Invoice); + + SustainabilityJnlLine.Validate("Document No.", GenJnlLineDocNo); + SustainabilityJnlLine.Validate("Account No.", AssemblyLine."Sust. Account No."); + SustainabilityJnlLine.Validate("Account Category", AssemblyLine."Sust. Account Category"); + SustainabilityJnlLine.Validate("Account Subcategory", AssemblyLine."Sust. Account Subcategory"); + SustainabilityJnlLine.Validate("Unit of Measure", AssemblyLine."Unit of Measure Code"); + SustainabilityJnlLine."Dimension Set ID" := AssemblyLine."Dimension Set ID"; + SustainabilityJnlLine."Shortcut Dimension 1 Code" := AssemblyLine."Shortcut Dimension 1 Code"; + SustainabilityJnlLine."Shortcut Dimension 2 Code" := AssemblyLine."Shortcut Dimension 2 Code"; + SustainabilityJnlLine.Validate("CO2e Emission", CO2eToPost); + SustainabilityPostMgt.InsertLedgerEntry(SustainabilityJnlLine, AssemblyLine); + + AssemblyLine."Posted Total CO2e" += CO2eToPost; + end; + + local procedure GetPostingSign(QtyToOutPut: Decimal; QtyToConsume: Decimal; GHGCredit: Boolean): Integer + var + Sign: Integer; + begin + Sign := 1; + + if QtyToConsume <> 0 then + if not GHGCredit then + Sign := -1; + + if QtyToOutPut <> 0 then + if GHGCredit then + Sign := -1; + + exit(Sign); + end; + + local procedure IsGHGCreditLine(ItemNo: Code[20]): Boolean + var + Item: Record Item; + begin + if ItemNo = '' then + exit(false); + + Item.Get(ItemNo); + + exit(Item."GHG Credit"); + end; + + local procedure CanPostSustainabilityJnlLine(AccountNo: Code[20]; AccountCategory: Code[20]; AccountSubCategory: Code[20]; CO2eToPost: Decimal): Boolean + var + SustAccountCategory: Record "Sustain. Account Category"; + SustainAccountSubcategory: Record "Sustain. Account Subcategory"; + begin + if AccountNo = '' then + exit(false); + + if SustAccountCategory.Get(AccountCategory) then + if SustAccountCategory."Water Intensity" or SustAccountCategory."Waste Intensity" or SustAccountCategory."Discharged Into Water" then + Error(NotAllowedToPostSustLedEntryForWaterOrWasteErr, AccountNo); + + if SustainAccountSubcategory.Get(AccountCategory, AccountSubCategory) then + if not SustainAccountSubcategory."Renewable Energy" then + if (CO2eToPost = 0) then + Error(EmissionMustNotBeZeroErr); + + if (CO2eToPost <> 0) then + exit(true); + end; + + var + EmissionMustNotBeZeroErr: Label 'The Emission fields must have a value that is not 0.'; + NotAllowedToPostSustLedEntryForWaterOrWasteErr: Label 'It is not allowed to post Sustainability Ledger Entry for water or waste in Assembly document for Account No. %1', Comment = '%1 = Sustainability Account No.'; +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Assembly/SustPostedAssemblyHeader.TableExt.al b/Apps/W1/Sustainability/app/src/Assembly/SustPostedAssemblyHeader.TableExt.al new file mode 100644 index 0000000000..879bf839d2 --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Assembly/SustPostedAssemblyHeader.TableExt.al @@ -0,0 +1,54 @@ +namespace Microsoft.Sustainability.Assembly; + +using Microsoft.Assembly.History; +using Microsoft.Sustainability.Account; +using Microsoft.Sustainability.Setup; + +tableextension 6253 "Sust. Posted Assembly Header" extends "Posted Assembly Header" +{ + fields + { + field(6210; "Sust. Account No."; Code[20]) + { + Caption = 'Sustainability Account No.'; + TableRelation = "Sustainability Account" where("Account Type" = const(Posting), Blocked = const(false)); + DataClassification = CustomerContent; + } + field(6211; "Sust. Account Name"; Text[100]) + { + Caption = 'Sustainability Account Name'; + DataClassification = CustomerContent; + } + field(6212; "Sust. Account Category"; Code[20]) + { + Caption = 'Sustainability Account Category'; + Editable = false; + TableRelation = "Sustain. Account Category"; + DataClassification = CustomerContent; + } + field(6213; "Sust. Account Subcategory"; Code[20]) + { + Caption = 'Sustainability Account Subcategory'; + Editable = false; + TableRelation = "Sustain. Account Subcategory".Code where("Category Code" = field("Sust. Account Category")); + DataClassification = CustomerContent; + } + field(6214; "CO2e per Unit"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + Caption = 'CO2e per Unit'; + DataClassification = CustomerContent; + } + field(6215; "Total CO2e"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + Caption = 'Total CO2e'; + DataClassification = CustomerContent; + } + } + + var + SustainabilitySetup: Record "Sustainability Setup"; +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Assembly/SustPostedAssemblyLine.TableExt.al b/Apps/W1/Sustainability/app/src/Assembly/SustPostedAssemblyLine.TableExt.al new file mode 100644 index 0000000000..1e377ecccb --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Assembly/SustPostedAssemblyLine.TableExt.al @@ -0,0 +1,54 @@ +namespace Microsoft.Sustainability.Assembly; + +using Microsoft.Assembly.History; +using Microsoft.Sustainability.Account; +using Microsoft.Sustainability.Setup; + +tableextension 6254 "Sust. Posted Assembly Line" extends "Posted Assembly Line" +{ + fields + { + field(6210; "Sust. Account No."; Code[20]) + { + Caption = 'Sustainability Account No.'; + TableRelation = "Sustainability Account" where("Account Type" = const(Posting), Blocked = const(false)); + DataClassification = CustomerContent; + } + field(6211; "Sust. Account Name"; Text[100]) + { + Caption = 'Sustainability Account Name'; + DataClassification = CustomerContent; + } + field(6212; "Sust. Account Category"; Code[20]) + { + Caption = 'Sustainability Account Category'; + Editable = false; + TableRelation = "Sustain. Account Category"; + DataClassification = CustomerContent; + } + field(6213; "Sust. Account Subcategory"; Code[20]) + { + Caption = 'Sustainability Account Subcategory'; + Editable = false; + TableRelation = "Sustain. Account Subcategory".Code where("Category Code" = field("Sust. Account Category")); + DataClassification = CustomerContent; + } + field(6214; "CO2e per Unit"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + Caption = 'CO2e per Unit'; + DataClassification = CustomerContent; + } + field(6215; "Total CO2e"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + Caption = 'Total CO2e'; + DataClassification = CustomerContent; + } + } + + var + SustainabilitySetup: Record "Sustainability Setup"; +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Assembly/SustPostedAssemblyOrder.PageExt.al b/Apps/W1/Sustainability/app/src/Assembly/SustPostedAssemblyOrder.PageExt.al new file mode 100644 index 0000000000..0cdc975bdd --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Assembly/SustPostedAssemblyOrder.PageExt.al @@ -0,0 +1,28 @@ +namespace Microsoft.Sustainability.Assembly; + +using Microsoft.Assembly.History; + +pageextension 6255 "Sust. Posted Assembly Order" extends "Posted Assembly Order" +{ + layout + { + addafter("Cost Amount") + { + field("Sust. Account No."; Rec."Sust. Account No.") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the value of the Sustainability Account No. field.'; + } + field("CO2e per Unit"; Rec."CO2e per Unit") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the value of the CO2e per Unit field.'; + } + field("Total CO2e"; Rec."Total CO2e") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the value of the Total CO2e field.'; + } + } + } +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Assembly/SustPstdAssemblyOrderSub.PageExt.al b/Apps/W1/Sustainability/app/src/Assembly/SustPstdAssemblyOrderSub.PageExt.al new file mode 100644 index 0000000000..5e37bb5f4a --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Assembly/SustPstdAssemblyOrderSub.PageExt.al @@ -0,0 +1,26 @@ +namespace Microsoft.Sustainability.Assembly; + +using Microsoft.Assembly.History; + +pageextension 6254 "Sust. Pstd Assembly Order Sub." extends "Posted Assembly Order Subform" +{ + layout + { + addafter("Location Code") + { + field("Sust. Account No."; Rec."Sust. Account No.") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the value of the Sustainability Account No. field.'; + } + } + addafter("Cost Amount") + { + field("Total CO2e"; Rec."Total CO2e") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the value of the Total CO2e field.'; + } + } + } +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Assembly/SustainabilityAssemblyHeader.TableExt.al b/Apps/W1/Sustainability/app/src/Assembly/SustainabilityAssemblyHeader.TableExt.al new file mode 100644 index 0000000000..6ff76c53a3 --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Assembly/SustainabilityAssemblyHeader.TableExt.al @@ -0,0 +1,210 @@ +namespace Microsoft.Sustainability.Assembly; + +using Microsoft.Assembly.Document; +using Microsoft.Sustainability.Setup; +using Microsoft.Inventory.Item; +using Microsoft.Sustainability.Account; + +tableextension 6251 "Sustainability Assembly Header" extends "Assembly Header" +{ + fields + { + field(6210; "Sust. Account No."; Code[20]) + { + Caption = 'Sustainability Account No.'; + TableRelation = "Sustainability Account" where("Account Type" = const(Posting), Blocked = const(false)); + DataClassification = CustomerContent; + + trigger OnValidate() + begin + if Rec."Sust. Account No." <> xRec."Sust. Account No." then + ClearEmissionInformation(Rec); + + if Rec."Sust. Account No." = '' then begin + Rec.Validate("Sust. Account Category", ''); + "Sust. Account Name" := ''; + end else begin + ValidateEmissionPrerequisite(Rec, Rec.FieldNo("Sust. Account No.")); + CopyFromSustainabilityAccount(Rec); + UpdateCO2eInformation(); + end; + end; + } + field(6211; "Sust. Account Name"; Text[100]) + { + Caption = 'Sustainability Account Name'; + DataClassification = CustomerContent; + } + field(6212; "Sust. Account Category"; Code[20]) + { + Caption = 'Sustainability Account Category'; + Editable = false; + TableRelation = "Sustain. Account Category"; + DataClassification = CustomerContent; + + trigger OnValidate() + begin + if Rec."Sust. Account Category" <> '' then + ValidateEmissionPrerequisite(Rec, Rec.FieldNo("Sust. Account Category")) + else + Rec.Validate("Sust. Account Subcategory", ''); + + if "Sust. Account Category" <> xRec."Sust. Account Category" then + Rec.Validate("Shortcut Dimension 1 Code", ''); + end; + } + field(6213; "Sust. Account Subcategory"; Code[20]) + { + Caption = 'Sustainability Account Subcategory'; + Editable = false; + TableRelation = "Sustain. Account Subcategory".Code where("Category Code" = field("Sust. Account Category")); + DataClassification = CustomerContent; + + trigger OnValidate() + begin + if Rec."Sust. Account Subcategory" <> '' then + ValidateEmissionPrerequisite(Rec, Rec.FieldNo("Sust. Account Subcategory")); + end; + } + field(6214; "CO2e per Unit"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + Caption = 'CO2e per Unit'; + DataClassification = CustomerContent; + + trigger OnValidate() + begin + if Rec."CO2e per Unit" <> 0 then + ValidateEmissionPrerequisite(Rec, Rec.FieldNo("CO2e per Unit")); + + UpdateSustainabilityEmission(Rec); + end; + } + field(6215; "Total CO2e"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + Caption = 'Total CO2e'; + DataClassification = CustomerContent; + + trigger OnValidate() + begin + if Rec."Total CO2e" <> 0 then + ValidateEmissionPrerequisite(Rec, Rec.FieldNo("Total CO2e")); + + UpdateEmissionPerUnit(Rec); + end; + } + field(6216; "Posted Total CO2e"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + Caption = 'Posted Total CO2e'; + Editable = false; + DataClassification = CustomerContent; + } + + field(6217; "Expected Assembly Line Total CO2e"; Decimal) + { + AutoFormatType = 11; + CalcFormula = sum("Assembly Line"."Total CO2e" where("Document Type" = field("Document Type"), + "Document No." = field("No."))); + Caption = 'Expected Assembly Line Total CO2e'; + Editable = false; + FieldClass = FlowField; + } + } + + procedure UpdateSustainabilityEmission(var AssemblyHeader: Record "Assembly Header") + begin + AssemblyHeader."Total CO2e" := AssemblyHeader."CO2e per Unit" * AssemblyHeader."Qty. per Unit of Measure" * AssemblyHeader.Quantity; + end; + + procedure UpdateEmissionPerUnit(var AssemblyHeader: Record "Assembly Header") + var + Denominator: Decimal; + begin + AssemblyHeader."CO2e Per Unit" := 0; + + if (AssemblyHeader."Qty. per Unit of Measure" = 0) or (AssemblyHeader.Quantity = 0) then + exit; + + Denominator := AssemblyHeader."Qty. per Unit of Measure" * AssemblyHeader.Quantity; + if AssemblyHeader."Total CO2e" <> 0 then + AssemblyHeader."CO2e per Unit" := AssemblyHeader."Total CO2e" / Denominator; + end; + + local procedure ClearEmissionInformation(var AssemblyHeader: Record "Assembly Header") + begin + AssemblyHeader.Validate("CO2e per Unit", 0); + end; + + local procedure ValidateEmissionPrerequisite(AssemblyHeader: Record "Assembly Header"; CurrentFieldNo: Integer) + var + SustAccountCategory: Record "Sustain. Account Category"; + begin + case CurrentFieldNo of + AssemblyHeader.FieldNo("CO2e per Unit"), + AssemblyHeader.FieldNo("Total CO2e"): + AssemblyHeader.TestField("Sust. Account No."); + AssemblyHeader.FieldNo("Sust. Account No."), + AssemblyHeader.FieldNo("Sust. Account Category"), + AssemblyHeader.FieldNo("Sust. Account Subcategory"), + AssemblyHeader.FieldNo("Sust. Account Name"): + begin + AssemblyHeader.TestField("No."); + + if SustAccountCategory.Get(AssemblyHeader."Sust. Account Category") then + if SustAccountCategory."Water Intensity" or SustAccountCategory."Waste Intensity" or SustAccountCategory."Discharged Into Water" then + Error(NotAllowedToUseSustAccountForWaterOrWasteErr, AssemblyHeader."Sust. Account No."); + end; + end; + end; + + local procedure UpdateCO2eInformation() + var + Item: Record Item; + CalcCO2ePerUnit: Decimal; + begin + if not Item.Get(Rec."Item No.") then + exit; + + if ExistSustAssemblyLine(Rec) then begin + Rec.CalcFields("Expected Assembly Line Total CO2e"); + CalcCO2ePerUnit := (Rec."Expected Assembly Line Total CO2e") / Rec.Quantity; + + Rec.Validate("CO2e per Unit", CalcCO2ePerUnit); + end else + Rec.Validate("CO2e per Unit", Item."CO2e per Unit"); + end; + + local procedure CopyFromSustainabilityAccount(var AssemblyHeader: Record "Assembly Header") + var + SustainabilityAccount: Record "Sustainability Account"; + begin + SustainabilityAccount.Get(AssemblyHeader."Sust. Account No."); + SustainabilityAccount.CheckAccountReadyForPosting(); + SustainabilityAccount.TestField("Direct Posting", true); + + AssemblyHeader.Validate("Sust. Account Name", SustainabilityAccount.Name); + AssemblyHeader.Validate("Sust. Account Category", SustainabilityAccount.Category); + AssemblyHeader.Validate("Sust. Account Subcategory", SustainabilityAccount.Subcategory); + end; + + local procedure ExistSustAssemblyLine(AssemblyHeader: Record "Assembly Header"): Boolean + var + AssemblyLine: Record "Assembly Line"; + begin + AssemblyLine.SetLoadFields("Document Type", "Document No.", "Sust. Account No."); + AssemblyLine.SetRange("Document Type", AssemblyHeader."Document Type"); + AssemblyLine.SetRange("Document No.", AssemblyHeader."No."); + AssemblyLine.SetFilter("Sust. Account No.", '<>%1', ''); + + exit(not AssemblyLine.IsEmpty()); + end; + + var + SustainabilitySetup: Record "Sustainability Setup"; + NotAllowedToUseSustAccountForWaterOrWasteErr: Label 'It is not allowed to use Sustainability Account %1 for water or waste in Assembly document.', Comment = '%1 = Sust. Account No.'; +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Certificate/SustCertificateSubscribers.Codeunit.al b/Apps/W1/Sustainability/app/src/Certificate/SustCertificateSubscribers.Codeunit.al index e4dae68fd5..2396fe6121 100644 --- a/Apps/W1/Sustainability/app/src/Certificate/SustCertificateSubscribers.Codeunit.al +++ b/Apps/W1/Sustainability/app/src/Certificate/SustCertificateSubscribers.Codeunit.al @@ -25,7 +25,7 @@ codeunit 6250 "Sust. Certificate Subscribers" [EventSubscriber(ObjectType::Table, Database::Item, 'OnBeforeValidateEvent', "Replenishment System", false, false)] local procedure OnAfterValidateItemReplenishmentSystemEvent(var Rec: Record Item; var xRec: Record Item) begin - ClearDefaultSustAccountForNonPurchaseItem(Rec); + ClearDefaultSustEmissionForNonPurchaseItem(Rec); end; [EventSubscriber(ObjectType::Table, Database::"Purchase Line", 'OnBeforeValidateEvent', 'No.', false, false)] @@ -54,7 +54,7 @@ codeunit 6250 "Sust. Certificate Subscribers" Item.Validate("Default Sust. Account", ItemCategory."Default Sust. Account"); end; - local procedure ClearDefaultSustAccountForNonPurchaseItem(var Item: Record Item) + local procedure ClearDefaultSustEmissionForNonPurchaseItem(var Item: Record Item) begin if (Item."Replenishment System" = Item."Replenishment System"::Purchase) then exit; @@ -62,9 +62,11 @@ codeunit 6250 "Sust. Certificate Subscribers" if (Item."Default Sust. Account" = '') then exit; - if Confirm(StrSubstNo(ConfirmationForClearEmissionInfoQst, Item."Replenishment System"), false) then - Item.Validate("Default Sust. Account", '') - else + if Confirm(StrSubstNo(ConfirmationForClearEmissionInfoQst, Item."Replenishment System"), false) then begin + Item.Validate("Default CH4 Emission", 0); + Item.Validate("Default CO2 Emission", 0); + Item.Validate("Default N2O Emission", 0); + end else Error(''); end; } \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Certificate/SustItemCard.PageExt.al b/Apps/W1/Sustainability/app/src/Certificate/SustItemCard.PageExt.al index 965fa5d935..e1995d9bea 100644 --- a/Apps/W1/Sustainability/app/src/Certificate/SustItemCard.PageExt.al +++ b/Apps/W1/Sustainability/app/src/Certificate/SustItemCard.PageExt.al @@ -38,7 +38,6 @@ pageextension 6222 "Sust. Item Card" extends "Item Card" field("Default Sust. Account"; Rec."Default Sust. Account") { ApplicationArea = Basic, Suite; - Editable = Rec."Replenishment System" = Rec."Replenishment System"::Purchase; ToolTip = 'Specifies the value of the Default Sust. Account field.'; } field("Default CO2 Emission"; Rec."Default CO2 Emission") diff --git a/Apps/W1/Sustainability/app/src/Manufacturing/SustCalculateCO2e.Report.al b/Apps/W1/Sustainability/app/src/Manufacturing/SustCalculateCO2e.Report.al index d4ae850190..bdf66ca3a3 100644 --- a/Apps/W1/Sustainability/app/src/Manufacturing/SustCalculateCO2e.Report.al +++ b/Apps/W1/Sustainability/app/src/Manufacturing/SustCalculateCO2e.Report.al @@ -94,6 +94,7 @@ report 6214 "Sust. Calculate CO2e" field(CapacityType; Type) { ApplicationArea = Manufacturing; + Visible = not TypeVisible; Caption = 'Type'; OptionCaption = 'Work Center,Machine Center,Both'; ToolTip = 'Specifies the value of the Type field.'; @@ -102,9 +103,10 @@ report 6214 "Sust. Calculate CO2e" } } - procedure Initialize(CapacityType: Integer) + procedure Initialize(CapacityType: Integer; HideCapacityType: Boolean) begin Type := CapacityType; + TypeVisible := HideCapacityType; end; local procedure CommitRecord(var CommitCounter: Integer) @@ -208,4 +210,5 @@ report 6214 "Sust. Calculate CO2e" ProcessBarMsg: Label 'Processing: @1@@@@@@@', Comment = '1 - overall progress'; UpdateCompleteMsg: Label 'CO2e per unit is updated on %1 out of %2 entries in %3.', Comment = '%1 - Records Updated, %2 - Total Record Count , %3 = Table Caption'; RecordCount: Integer; + TypeVisible: Boolean; } \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Manufacturing/SustMachineCenterCard.PageExt.al b/Apps/W1/Sustainability/app/src/Manufacturing/SustMachineCenterCard.PageExt.al index 2655e890dd..758cfe5411 100644 --- a/Apps/W1/Sustainability/app/src/Manufacturing/SustMachineCenterCard.PageExt.al +++ b/Apps/W1/Sustainability/app/src/Manufacturing/SustMachineCenterCard.PageExt.al @@ -84,7 +84,7 @@ pageextension 6232 "Sust. Machine Center Card" extends "Machine Center Card" CalculateCO2e: Report "Sust. Calculate CO2e"; begin MachineCenter.SetFilter("No.", Rec."No."); - CalculateCO2e.Initialize(1); + CalculateCO2e.Initialize(1, true); CalculateCO2e.SetTableView(MachineCenter); CalculateCO2e.Run(); end; diff --git a/Apps/W1/Sustainability/app/src/Manufacturing/SustMachineCenterList.PageExt.al b/Apps/W1/Sustainability/app/src/Manufacturing/SustMachineCenterList.PageExt.al index 5102d6ea15..f04695b4a8 100644 --- a/Apps/W1/Sustainability/app/src/Manufacturing/SustMachineCenterList.PageExt.al +++ b/Apps/W1/Sustainability/app/src/Manufacturing/SustMachineCenterList.PageExt.al @@ -21,7 +21,7 @@ pageextension 6257 "Sust. Machine Center List" extends "Machine Center List" var CalculateCO2e: Report "Sust. Calculate CO2e"; begin - CalculateCO2e.Initialize(1); + CalculateCO2e.Initialize(1, true); CalculateCO2e.Run(); end; } diff --git a/Apps/W1/Sustainability/app/src/Manufacturing/SustProdOrderLine.TableExt.al b/Apps/W1/Sustainability/app/src/Manufacturing/SustProdOrderLine.TableExt.al index 250dfe9d1d..bc0ee2b3af 100644 --- a/Apps/W1/Sustainability/app/src/Manufacturing/SustProdOrderLine.TableExt.al +++ b/Apps/W1/Sustainability/app/src/Manufacturing/SustProdOrderLine.TableExt.al @@ -179,11 +179,18 @@ tableextension 6248 "Sust. Prod. Order Line" extends "Prod. Order Line" local procedure UpdateCO2eInformation() var Item: Record Item; + CalcCO2ePerUnit: Decimal; begin if not Item.Get(Rec."Item No.") then exit; - Rec.Validate("CO2e per Unit", Item."CO2e per Unit"); + if (ExistSustProdOrderRoutingLine(Rec)) or (ExistSustProdOrderComponent(Rec)) then begin + Rec.CalcFields("Expected Operation Total CO2e", "Expected Component Total CO2e"); + CalcCO2ePerUnit := (Rec."Expected Operation Total CO2e" + Rec."Expected Component Total CO2e") / Rec.Quantity; + + Rec.Validate("CO2e per Unit", CalcCO2ePerUnit); + end else + Rec.Validate("CO2e per Unit", Item."CO2e per Unit"); end; local procedure CopyFromSustainabilityAccount(var ProdOrderLine: Record "Prod. Order Line") @@ -199,6 +206,33 @@ tableextension 6248 "Sust. Prod. Order Line" extends "Prod. Order Line" ProdOrderLine.Validate("Sust. Account Subcategory", SustainabilityAccount.Subcategory); end; + local procedure ExistSustProdOrderRoutingLine(ProdOrderLine: Record "Prod. Order Line"): Boolean + var + ProdOrderRoutingLine: Record "Prod. Order Routing Line"; + begin + ProdOrderRoutingLine.SetLoadFields(Status, "Prod. Order No.", "Routing No.", "Routing Reference No.", "Sust. Account No."); + ProdOrderRoutingLine.SetRange(Status, ProdOrderLine.Status); + ProdOrderRoutingLine.SetRange("Prod. Order No.", ProdOrderLine."Prod. Order No."); + ProdOrderRoutingLine.SetRange("Routing No.", ProdOrderLine."Routing No."); + ProdOrderRoutingLine.SetRange("Routing Reference No.", ProdOrderLine."Line No."); + ProdOrderRoutingLine.SetFilter("Sust. Account No.", '<>%1', ''); + + exit(not ProdOrderRoutingLine.IsEmpty()); + end; + + local procedure ExistSustProdOrderComponent(ProdOrderLine: Record "Prod. Order Line"): Boolean + var + ProdOrderComponent: Record "Prod. Order Component"; + begin + ProdOrderComponent.SetLoadFields(Status, "Prod. Order No.", "Prod. Order Line No.", "Sust. Account No."); + ProdOrderComponent.SetRange(Status, ProdOrderLine.Status); + ProdOrderComponent.SetRange("Prod. Order No.", ProdOrderLine."Prod. Order No."); + ProdOrderComponent.SetRange("Prod. Order Line No.", ProdOrderLine."Line No."); + ProdOrderComponent.SetFilter("Sust. Account No.", '<>%1', ''); + + exit(not ProdOrderComponent.IsEmpty()); + end; + var SustainabilitySetup: Record "Sustainability Setup"; NotAllowedToUseSustAccountForWaterOrWasteErr: Label 'It is not allowed to use Sustainability Account %1 for water or waste in Production document.', Comment = '%1 = Sust. Account No.'; diff --git a/Apps/W1/Sustainability/app/src/Manufacturing/SustWorkCenterCard.PageExt.al b/Apps/W1/Sustainability/app/src/Manufacturing/SustWorkCenterCard.PageExt.al index 1bff9ce94f..7f601c03ec 100644 --- a/Apps/W1/Sustainability/app/src/Manufacturing/SustWorkCenterCard.PageExt.al +++ b/Apps/W1/Sustainability/app/src/Manufacturing/SustWorkCenterCard.PageExt.al @@ -84,6 +84,7 @@ pageextension 6231 "Sust. Work Center Card" extends "Work Center Card" CalculateCO2e: Report "Sust. Calculate CO2e"; begin WorkCenter.SetFilter("No.", Rec."No."); + CalculateCO2e.Initialize(0, true); CalculateCO2e.SetTableView(WorkCenter); CalculateCO2e.Run(); end; diff --git a/Apps/W1/Sustainability/app/src/Manufacturing/SustWorkCenterList.PageExt.al b/Apps/W1/Sustainability/app/src/Manufacturing/SustWorkCenterList.PageExt.al index 05744dcbe3..5a49ebe70c 100644 --- a/Apps/W1/Sustainability/app/src/Manufacturing/SustWorkCenterList.PageExt.al +++ b/Apps/W1/Sustainability/app/src/Manufacturing/SustWorkCenterList.PageExt.al @@ -21,6 +21,7 @@ pageextension 6256 "Sust. Work Center List" extends "Work Center List" var CalculateCO2e: Report "Sust. Calculate CO2e"; begin + CalculateCO2e.Initialize(0, true); CalculateCO2e.Run(); end; } diff --git a/Apps/W1/Sustainability/app/src/Posting/SustItemPostSubscriber.Codeunit.al b/Apps/W1/Sustainability/app/src/Posting/SustItemPostSubscriber.Codeunit.al index 159b9e2fc6..88971e1a31 100644 --- a/Apps/W1/Sustainability/app/src/Posting/SustItemPostSubscriber.Codeunit.al +++ b/Apps/W1/Sustainability/app/src/Posting/SustItemPostSubscriber.Codeunit.al @@ -16,6 +16,13 @@ codeunit 6256 "Sust. Item Post Subscriber" local procedure CanCreateSustValueEntry(ItemJournalLine: Record "Item Journal Line"; var ValueEntry: Record "Value Entry"): Boolean begin + if ValueEntry."Order Type" = ValueEntry."Order Type"::Transfer then + if (ValueEntry."Document Type" = ValueEntry."Document Type"::"Transfer Shipment") and (ValueEntry."Valued Quantity" > 0) then + exit(false) + else + if (ValueEntry."Document Type" = ValueEntry."Document Type"::"Transfer Receipt") and (ValueEntry."Valued Quantity" < 0) then + exit(false); + exit((ItemJournalLine."Sust. Account No." <> '') and (ValueEntry."Entry Type" = ValueEntry."Entry Type"::"Direct Cost")); end; diff --git a/Apps/W1/Sustainability/app/src/Posting/SustainabilityPostMgt.Codeunit.al b/Apps/W1/Sustainability/app/src/Posting/SustainabilityPostMgt.Codeunit.al index 7f31b47fb2..08f5b94f52 100644 --- a/Apps/W1/Sustainability/app/src/Posting/SustainabilityPostMgt.Codeunit.al +++ b/Apps/W1/Sustainability/app/src/Posting/SustainabilityPostMgt.Codeunit.al @@ -1,12 +1,14 @@ namespace Microsoft.Sustainability.Posting; +using Microsoft.Assembly.Document; using Microsoft.Inventory.Item; +using Microsoft.Inventory.Journal; using Microsoft.Inventory.Ledger; +using Microsoft.Inventory.Transfer; using Microsoft.Sales.Document; using Microsoft.Sustainability.Account; using Microsoft.Sustainability.Emission; using Microsoft.Sustainability.Journal; -using Microsoft.Inventory.Journal; using Microsoft.Sustainability.Ledger; codeunit 6212 "Sustainability Post Mgt" @@ -19,7 +21,25 @@ codeunit 6212 "Sustainability Post Mgt" InsertLedgerEntry(SustainabilityJnlLine); end; - procedure InsertLedgerEntry(SustainabilityJnlLine: Record "Sustainability Jnl. Line"; SalesLie: Record "Sales Line") + procedure InsertLedgerEntry(SustainabilityJnlLine: Record "Sustainability Jnl. Line"; SalesLine: Record "Sales Line") + begin + SkipUpdateCarbonEmissionValue := true; + InsertLedgerEntry(SustainabilityJnlLine); + end; + + procedure InsertLedgerEntry(SustainabilityJnlLine: Record "Sustainability Jnl. Line"; AssemblyHeader: Record "Assembly Header") + begin + SkipUpdateCarbonEmissionValue := true; + InsertLedgerEntry(SustainabilityJnlLine); + end; + + procedure InsertLedgerEntry(SustainabilityJnlLine: Record "Sustainability Jnl. Line"; AssemblyLine: Record "Assembly Line") + begin + SkipUpdateCarbonEmissionValue := true; + InsertLedgerEntry(SustainabilityJnlLine); + end; + + procedure InsertLedgerEntry(SustainabilityJnlLine: Record "Sustainability Jnl. Line"; TransferLine: Record "Transfer Line") begin SkipUpdateCarbonEmissionValue := true; InsertLedgerEntry(SustainabilityJnlLine); @@ -85,13 +105,13 @@ codeunit 6212 "Sustainability Post Mgt" procedure UpdateCO2ePerUnit(SustValueEntry: Record "Sustainability Value Entry") var Item: Record Item; - ItemCostMgt: Codeunit SustCostManagement; + SustCostMgt: Codeunit SustCostManagement; begin if (SustValueEntry."Valued Quantity" > 0) and not (SustValueEntry."Expected Emission") then begin if not Item.Get(SustValueEntry."Item No.") then exit; - ItemCostMgt.UpdateCO2ePerUnit(Item, 0); + SustCostMgt.UpdateCO2ePerUnit(Item, 0); end; end; diff --git a/Apps/W1/Sustainability/app/src/Transfer/SustTransferLine.TableExt.al b/Apps/W1/Sustainability/app/src/Transfer/SustTransferLine.TableExt.al new file mode 100644 index 0000000000..3316eff2e9 --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Transfer/SustTransferLine.TableExt.al @@ -0,0 +1,191 @@ +namespace Microsoft.Sustainability.Transfer; + +using Microsoft.Inventory.Item; +using Microsoft.Inventory.Transfer; +using Microsoft.Sustainability.Account; +using Microsoft.Sustainability.Setup; + +tableextension 6250 "Sust. Transfer Line" extends "Transfer Line" +{ + fields + { + field(6210; "Sust. Account No."; Code[20]) + { + Caption = 'Sustainability Account No.'; + TableRelation = "Sustainability Account" where("Account Type" = const(Posting), Blocked = const(false)); + DataClassification = CustomerContent; + + trigger OnValidate() + begin + if Rec."Sust. Account No." <> xRec."Sust. Account No." then + ClearEmissionInformation(Rec); + + if Rec."Sust. Account No." = '' then begin + Rec.Validate("Sust. Account Category", ''); + "Sust. Account Name" := ''; + end else begin + ValidateEmissionPrerequisite(Rec, Rec.FieldNo("Sust. Account No.")); + CopyFromSustainabilityAccount(Rec); + UpdateCO2eInformation(); + end; + + CreateDimFromDefaultDim(); + end; + } + field(6211; "Sust. Account Name"; Text[100]) + { + Caption = 'Sustainability Account Name'; + DataClassification = CustomerContent; + } + field(6212; "Sust. Account Category"; Code[20]) + { + Caption = 'Sustainability Account Category'; + Editable = false; + TableRelation = "Sustain. Account Category"; + DataClassification = CustomerContent; + + trigger OnValidate() + begin + if Rec."Sust. Account Category" <> '' then + ValidateEmissionPrerequisite(Rec, Rec.FieldNo("Sust. Account Category")) + else + Rec.Validate("Sust. Account Subcategory", ''); + + if "Sust. Account Category" <> xRec."Sust. Account Category" then + Rec.Validate("Shortcut Dimension 1 Code", ''); + end; + } + field(6213; "Sust. Account Subcategory"; Code[20]) + { + Caption = 'Sustainability Account Subcategory'; + Editable = false; + TableRelation = "Sustain. Account Subcategory".Code where("Category Code" = field("Sust. Account Category")); + DataClassification = CustomerContent; + + trigger OnValidate() + begin + if Rec."Sust. Account Subcategory" <> '' then + ValidateEmissionPrerequisite(Rec, Rec.FieldNo("Sust. Account Subcategory")); + end; + } + field(6214; "CO2e per Unit"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + Caption = 'CO2e per Unit'; + DataClassification = CustomerContent; + + trigger OnValidate() + begin + if Rec."CO2e per Unit" <> 0 then + ValidateEmissionPrerequisite(Rec, Rec.FieldNo("CO2e per Unit")); + + UpdateSustainabilityEmission(Rec); + end; + } + field(6215; "Total CO2e"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + Caption = 'Total CO2e'; + DataClassification = CustomerContent; + + trigger OnValidate() + begin + if Rec."Total CO2e" <> 0 then + ValidateEmissionPrerequisite(Rec, Rec.FieldNo("Total CO2e")); + + UpdateEmissionPerUnit(Rec); + end; + } + field(6216; "Posted Shipped Total CO2e"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + Caption = 'Posted Shipped Total CO2e'; + Editable = false; + DataClassification = CustomerContent; + } + field(6217; "Posted Received Total CO2e"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + Caption = 'Posted Received Total CO2e'; + Editable = false; + DataClassification = CustomerContent; + } + } + + procedure UpdateSustainabilityEmission(var TransferLine: Record "Transfer Line") + begin + TransferLine."Total CO2e" := TransferLine."CO2e per Unit" * TransferLine."Qty. per Unit of Measure" * TransferLine.Quantity; + end; + + procedure UpdateEmissionPerUnit(var TransferLine: Record "Transfer Line") + var + Denominator: Decimal; + begin + TransferLine."CO2e Per Unit" := 0; + + if (TransferLine."Qty. per Unit of Measure" = 0) or (TransferLine.Quantity = 0) then + exit; + + Denominator := TransferLine."Qty. per Unit of Measure" * TransferLine.Quantity; + if TransferLine."Total CO2e" <> 0 then + TransferLine."CO2e per Unit" := TransferLine."Total CO2e" / Denominator; + end; + + local procedure ClearEmissionInformation(var TransferLine: Record "Transfer Line") + begin + TransferLine.Validate("CO2e per Unit", 0); + end; + + local procedure ValidateEmissionPrerequisite(TransferLine: Record "Transfer Line"; CurrentFieldNo: Integer) + var + SustAccountCategory: Record "Sustain. Account Category"; + begin + case CurrentFieldNo of + TransferLine.FieldNo("CO2e per Unit"), + TransferLine.FieldNo("Total CO2e"): + TransferLine.TestField("Sust. Account No."); + TransferLine.FieldNo("Sust. Account No."), + TransferLine.FieldNo("Sust. Account Category"), + TransferLine.FieldNo("Sust. Account Subcategory"), + TransferLine.FieldNo("Sust. Account Name"): + begin + TransferLine.TestField("Item No."); + + if SustAccountCategory.Get(TransferLine."Sust. Account Category") then + if SustAccountCategory."Water Intensity" or SustAccountCategory."Waste Intensity" or SustAccountCategory."Discharged Into Water" then + Error(NotAllowedToUseSustAccountForWaterOrWasteErr, TransferLine."Sust. Account No."); + end; + end; + end; + + local procedure UpdateCO2eInformation() + var + Item: Record Item; + begin + if not Item.Get(Rec."Item No.") then + exit; + + Rec.Validate("CO2e per Unit", Item."CO2e per Unit"); + end; + + local procedure CopyFromSustainabilityAccount(var TransferLine: Record "Transfer Line") + var + SustainabilityAccount: Record "Sustainability Account"; + begin + SustainabilityAccount.Get(TransferLine."Sust. Account No."); + SustainabilityAccount.CheckAccountReadyForPosting(); + SustainabilityAccount.TestField("Direct Posting", true); + + TransferLine.Validate("Sust. Account Name", SustainabilityAccount.Name); + TransferLine.Validate("Sust. Account Category", SustainabilityAccount.Category); + TransferLine.Validate("Sust. Account Subcategory", SustainabilityAccount.Subcategory); + end; + + var + SustainabilitySetup: Record "Sustainability Setup"; + NotAllowedToUseSustAccountForWaterOrWasteErr: Label 'It is not allowed to use Sustainability Account %1 for water or waste in Transfer document.', Comment = '%1 = Sust. Account No.'; +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Transfer/SustTransferOrderSubform.PageExt.al b/Apps/W1/Sustainability/app/src/Transfer/SustTransferOrderSubform.PageExt.al new file mode 100644 index 0000000000..04899ef958 --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Transfer/SustTransferOrderSubform.PageExt.al @@ -0,0 +1,26 @@ +namespace Microsoft.Sustainability.Transfer; + +using Microsoft.Inventory.Transfer; + +pageextension 6259 "Sust. Transfer Order Subform" extends "Transfer Order Subform" +{ + layout + { + addafter(Description) + { + field("Sust. Account No."; Rec."Sust. Account No.") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the value of the Sustainability Account No. field.'; + } + } + addafter("Receipt Date") + { + field("Total CO2e"; Rec."Total CO2e") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the value of the Total CO2e field.'; + } + } + } +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Transfer/SustTransferReceiptHeader.TableExt.al b/Apps/W1/Sustainability/app/src/Transfer/SustTransferReceiptHeader.TableExt.al new file mode 100644 index 0000000000..e7c3f5f769 --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Transfer/SustTransferReceiptHeader.TableExt.al @@ -0,0 +1,31 @@ +namespace Microsoft.Sustainability.Transfer; + +using Microsoft.Inventory.Transfer; +using Microsoft.Sustainability.Setup; +using Microsoft.Sustainability.Ledger; + +tableextension 6257 "Sust. Transfer Receipt Header" extends "Transfer Receipt Header" +{ + fields + { + field(6210; "Sustainability Lines Exist"; Boolean) + { + Caption = 'Sustainability Lines Exist'; + Editable = false; + FieldClass = FlowField; + CalcFormula = exist("Transfer Receipt Line" where("Sust. Account No." = filter('<>'''''), "Document No." = field("No."))); + } + field(6211; "Total CO2e"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + CalcFormula = sum("Sustainability Ledger Entry"."CO2e Emission" where("Document No." = field("No."), "Document Type" = filter(Invoice | "GHG Credit"))); + Caption = 'Total CO2e'; + Editable = false; + FieldClass = FlowField; + } + } + + var + SustainabilitySetup: Record "Sustainability Setup"; +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Transfer/SustTransferReceiptLine.TableExt.al b/Apps/W1/Sustainability/app/src/Transfer/SustTransferReceiptLine.TableExt.al new file mode 100644 index 0000000000..90223856e8 --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Transfer/SustTransferReceiptLine.TableExt.al @@ -0,0 +1,54 @@ +namespace Microsoft.Sustainability.Transfer; + +using Microsoft.Inventory.Transfer; +using Microsoft.Sustainability.Account; +using Microsoft.Sustainability.Setup; + +tableextension 6258 "Sust. Transfer Receipt Line" extends "Transfer Receipt Line" +{ + fields + { + field(6210; "Sust. Account No."; Code[20]) + { + Caption = 'Sustainability Account No.'; + TableRelation = "Sustainability Account" where("Account Type" = const(Posting), Blocked = const(false)); + DataClassification = CustomerContent; + } + field(6211; "Sust. Account Name"; Text[100]) + { + Caption = 'Sustainability Account Name'; + DataClassification = CustomerContent; + } + field(6212; "Sust. Account Category"; Code[20]) + { + Caption = 'Sustainability Account Category'; + Editable = false; + TableRelation = "Sustain. Account Category"; + DataClassification = CustomerContent; + } + field(6213; "Sust. Account Subcategory"; Code[20]) + { + Caption = 'Sustainability Account Subcategory'; + Editable = false; + TableRelation = "Sustain. Account Subcategory".Code where("Category Code" = field("Sust. Account Category")); + DataClassification = CustomerContent; + } + field(6214; "CO2e per Unit"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + Caption = 'CO2e per Unit'; + DataClassification = CustomerContent; + } + field(6215; "Total CO2e"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + Caption = 'Total CO2e'; + DataClassification = CustomerContent; + } + } + + var + SustainabilitySetup: Record "Sustainability Setup"; +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Transfer/SustTransferReceiptStats.PageExt.al b/Apps/W1/Sustainability/app/src/Transfer/SustTransferReceiptStats.PageExt.al new file mode 100644 index 0000000000..6c99372f79 --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Transfer/SustTransferReceiptStats.PageExt.al @@ -0,0 +1,49 @@ +namespace Microsoft.Sustainability.Transfer; + +using Microsoft.Inventory.Transfer; + +pageextension 6264 "Sust. Transfer Receipt Stats." extends "Transfer Receipt Statistics" +{ + layout + { + addafter(General) + { + group(Sustainability) + { + Visible = EnableSustainability; + Caption = 'Sustainability'; + + field("Total CO2e"; Rec."Total CO2e") + { + ApplicationArea = Basic, Suite; + Caption = 'Total CO2e'; + ToolTip = 'Specifies the value of the Total CO2e field.'; + } + } + } + } + + trigger OnOpenPage() + begin + EnableSustainabilityControl(); + end; + + trigger OnAfterGetCurrRecord() + begin + EnableSustainabilityControl(); + end; + + trigger OnAfterGetRecord() + begin + EnableSustainabilityControl(); + end; + + local procedure EnableSustainabilityControl() + begin + Rec.CalcFields("Sustainability Lines Exist"); + EnableSustainability := Rec."Sustainability Lines Exist"; + end; + + var + EnableSustainability: Boolean; +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Transfer/SustTransferReceiptSubform.PageExt.al b/Apps/W1/Sustainability/app/src/Transfer/SustTransferReceiptSubform.PageExt.al new file mode 100644 index 0000000000..94d5854b97 --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Transfer/SustTransferReceiptSubform.PageExt.al @@ -0,0 +1,26 @@ +namespace Microsoft.Sustainability.Transfer; + +using Microsoft.Inventory.Transfer; + +pageextension 6261 "Sust. Transfer Receipt Subform" extends "Posted Transfer Rcpt. Subform" +{ + layout + { + addafter(Description) + { + field("Sust. Account No."; Rec."Sust. Account No.") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the value of the Sustainability Account No. field.'; + } + } + addafter("Shipping Time") + { + field("Total CO2e"; Rec."Total CO2e") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the value of the Total CO2e field.'; + } + } + } +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Transfer/SustTransferShipmentHeader.TableExt.al b/Apps/W1/Sustainability/app/src/Transfer/SustTransferShipmentHeader.TableExt.al new file mode 100644 index 0000000000..03598f2bff --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Transfer/SustTransferShipmentHeader.TableExt.al @@ -0,0 +1,31 @@ +namespace Microsoft.Sustainability.Transfer; + +using Microsoft.Inventory.Transfer; +using Microsoft.Sustainability.Setup; +using Microsoft.Sustainability.Ledger; + +tableextension 6255 "Sust. Transfer Shipment Header" extends "Transfer Shipment Header" +{ + fields + { + field(6210; "Sustainability Lines Exist"; Boolean) + { + Caption = 'Sustainability Lines Exist'; + Editable = false; + FieldClass = FlowField; + CalcFormula = exist("Transfer Shipment Line" where("Sust. Account No." = filter('<>'''''), "Document No." = field("No."))); + } + field(6211; "Total CO2e"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + CalcFormula = sum("Sustainability Ledger Entry"."CO2e Emission" where("Document No." = field("No."), "Document Type" = filter(Invoice | "GHG Credit"))); + Caption = 'Total CO2e'; + Editable = false; + FieldClass = FlowField; + } + } + + var + SustainabilitySetup: Record "Sustainability Setup"; +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Transfer/SustTransferShipmentLine.TableExt.al b/Apps/W1/Sustainability/app/src/Transfer/SustTransferShipmentLine.TableExt.al new file mode 100644 index 0000000000..6f0a00a8b0 --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Transfer/SustTransferShipmentLine.TableExt.al @@ -0,0 +1,54 @@ +namespace Microsoft.Sustainability.Transfer; + +using Microsoft.Inventory.Transfer; +using Microsoft.Sustainability.Account; +using Microsoft.Sustainability.Setup; + +tableextension 6256 "Sust. Transfer Shipment Line" extends "Transfer Shipment Line" +{ + fields + { + field(6210; "Sust. Account No."; Code[20]) + { + Caption = 'Sustainability Account No.'; + TableRelation = "Sustainability Account" where("Account Type" = const(Posting), Blocked = const(false)); + DataClassification = CustomerContent; + } + field(6211; "Sust. Account Name"; Text[100]) + { + Caption = 'Sustainability Account Name'; + DataClassification = CustomerContent; + } + field(6212; "Sust. Account Category"; Code[20]) + { + Caption = 'Sustainability Account Category'; + Editable = false; + TableRelation = "Sustain. Account Category"; + DataClassification = CustomerContent; + } + field(6213; "Sust. Account Subcategory"; Code[20]) + { + Caption = 'Sustainability Account Subcategory'; + Editable = false; + TableRelation = "Sustain. Account Subcategory".Code where("Category Code" = field("Sust. Account Category")); + DataClassification = CustomerContent; + } + field(6214; "CO2e per Unit"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + Caption = 'CO2e per Unit'; + DataClassification = CustomerContent; + } + field(6215; "Total CO2e"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + Caption = 'Total CO2e'; + DataClassification = CustomerContent; + } + } + + var + SustainabilitySetup: Record "Sustainability Setup"; +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Transfer/SustTransferShptStats.PageExt.al b/Apps/W1/Sustainability/app/src/Transfer/SustTransferShptStats.PageExt.al new file mode 100644 index 0000000000..795846349b --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Transfer/SustTransferShptStats.PageExt.al @@ -0,0 +1,49 @@ +namespace Microsoft.Sustainability.Transfer; + +using Microsoft.Inventory.Transfer; + +pageextension 6263 "Sust. Transfer Shpt. Stats." extends "Transfer Shipment Statistics" +{ + layout + { + addafter(General) + { + group(Sustainability) + { + Visible = EnableSustainability; + Caption = 'Sustainability'; + + field("Total CO2e"; Rec."Total CO2e") + { + ApplicationArea = Basic, Suite; + Caption = 'Total CO2e'; + ToolTip = 'Specifies the value of the Total CO2e field.'; + } + } + } + } + + trigger OnOpenPage() + begin + EnableSustainabilityControl(); + end; + + trigger OnAfterGetCurrRecord() + begin + EnableSustainabilityControl(); + end; + + trigger OnAfterGetRecord() + begin + EnableSustainabilityControl(); + end; + + local procedure EnableSustainabilityControl() + begin + Rec.CalcFields("Sustainability Lines Exist"); + EnableSustainability := Rec."Sustainability Lines Exist"; + end; + + var + EnableSustainability: Boolean; +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Transfer/SustTransferShptSubform.PageExt.al b/Apps/W1/Sustainability/app/src/Transfer/SustTransferShptSubform.PageExt.al new file mode 100644 index 0000000000..474f2b748f --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Transfer/SustTransferShptSubform.PageExt.al @@ -0,0 +1,26 @@ +namespace Microsoft.Sustainability.Transfer; + +using Microsoft.Inventory.Transfer; + +pageextension 6260 "Sust. Transfer Shpt Subform" extends "Posted Transfer Shpt. Subform" +{ + layout + { + addafter(Description) + { + field("Sust. Account No."; Rec."Sust. Account No.") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the value of the Sustainability Account No. field.'; + } + } + addafter("Shipping Time") + { + field("Total CO2e"; Rec."Total CO2e") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the value of the Total CO2e field.'; + } + } + } +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Transfer/SustTransferStatistics.PageExt.al b/Apps/W1/Sustainability/app/src/Transfer/SustTransferStatistics.PageExt.al new file mode 100644 index 0000000000..43d6228004 --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Transfer/SustTransferStatistics.PageExt.al @@ -0,0 +1,49 @@ +namespace Microsoft.Sustainability.Transfer; + +using Microsoft.Inventory.Transfer; + +pageextension 6262 "Sust. Transfer Statistics" extends "Transfer Statistics" +{ + layout + { + addafter(General) + { + group(Sustainability) + { + Visible = EnableSustainability; + Caption = 'Sustainability'; + + field("Total CO2e"; Rec."Total CO2e") + { + ApplicationArea = Basic, Suite; + Caption = 'Total CO2e'; + ToolTip = 'Specifies the value of the Total CO2e field.'; + } + } + } + } + + trigger OnOpenPage() + begin + EnableSustainabilityControl(); + end; + + trigger OnAfterGetCurrRecord() + begin + EnableSustainabilityControl(); + end; + + trigger OnAfterGetRecord() + begin + EnableSustainabilityControl(); + end; + + local procedure EnableSustainabilityControl() + begin + Rec.CalcFields("Sustainability Lines Exist"); + EnableSustainability := Rec."Sustainability Lines Exist"; + end; + + var + EnableSustainability: Boolean; +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Transfer/SustTransferSubscriber.Codeunit.al b/Apps/W1/Sustainability/app/src/Transfer/SustTransferSubscriber.Codeunit.al new file mode 100644 index 0000000000..f6ececfd61 --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Transfer/SustTransferSubscriber.Codeunit.al @@ -0,0 +1,228 @@ +namespace Microsoft.Sustainability.Transfer; + +using Microsoft.Inventory.Item; +using Microsoft.Inventory.Journal; +using Microsoft.Inventory.Transfer; +using Microsoft.Sustainability.Account; +using Microsoft.Sustainability.Journal; +using Microsoft.Sustainability.Posting; + +codeunit 6258 "Sust. Transfer Subscriber" +{ + [EventSubscriber(ObjectType::Table, Database::"Transfer Line", 'OnValidateItemNoOnCopyFromTempTransLine', '', false, false)] + local procedure OnValidateItemNoOnCopyFromTempTransLine(var TransferLine: Record "Transfer Line") + begin + if TransferLine."Item No." = '' then + TransferLine.Validate("Sust. Account No.", ''); + end; + + [EventSubscriber(ObjectType::Table, Database::"Transfer Line", 'OnAfterAssignItemValues', '', false, false)] + local procedure OnAfterAssignItemValues(var TransferLine: Record "Transfer Line"; Item: Record Item) + begin + TransferLine.Validate("Sust. Account No.", Item."Default Sust. Account"); + end; + + [EventSubscriber(ObjectType::Table, Database::"Transfer Line", 'OnValidateQuantityOnAfterCalcQuantityBase', '', false, false)] + local procedure OnValidateQuantityOnAfterCalcQuantityBase(var TransferLine: Record "Transfer Line") + begin + TransferLine.UpdateSustainabilityEmission(TransferLine); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"TransferOrder-Post Shipment", 'OnBeforePostItemJournalLine', '', false, false)] + local procedure OnPostItemJnlLineOnAfterPrepareShipmentItemJnlLine(var ItemJournalLine: Record "Item Journal Line"; TransferLine: Record "Transfer Line") + begin + if (ItemJournalLine.Quantity <> 0) then begin + UpdateSustainabilityItemJournalLine(ItemJournalLine, TransferLine); + PostSustainabilityLine(TransferLine, ItemJournalLine.Quantity, true, false, ItemJournalLine."Source Code", ItemJournalLine."Document No."); + UpdatePostedSustainabilityEmissionOrderLine(TransferLine, ItemJournalLine.Quantity, true, false); + end; + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"TransferOrder-Post Receipt", 'OnBeforePostItemJournalLine', '', false, false)] + local procedure OnPostItemJnlLineOnAfterPrepareReceiptItemJnlLine(var ItemJournalLine: Record "Item Journal Line"; TransferLine: Record "Transfer Line") + begin + if (ItemJournalLine.Quantity <> 0) then begin + UpdateSustainabilityItemJournalLine(ItemJournalLine, TransferLine); + PostSustainabilityLine(TransferLine, ItemJournalLine.Quantity, false, true, ItemJournalLine."Source Code", ItemJournalLine."Document No."); + UpdatePostedSustainabilityEmissionOrderLine(TransferLine, ItemJournalLine.Quantity, false, true); + end; + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"TransferOrder-Post Shipment", 'OnBeforeInsertTransShptLine', '', false, false)] + local procedure OnBeforeInsertTransShptLine(TransShptHeader: Record "Transfer Shipment Header"; TransLine: Record "Transfer Line"; var TransShptLine: Record "Transfer Shipment Line") + begin + CopyTransShiplineFromTransLine(TransLine, TransShptLine); + UpdatePostedSustainabilityEmission(TransLine, TransShptLine.Quantity, 1, TransShptLine."Total CO2e"); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"TransferOrder-Post Receipt", 'OnBeforeInsertTransRcptLine', '', false, false)] + local procedure OnBeforeInsertTransRcptLine(TransLine: Record "Transfer Line"; var TransRcptLine: Record "Transfer Receipt Line") + begin + CopyTransReceiptlineFromTransLine(TransLine, TransRcptLine); + UpdatePostedSustainabilityEmission(TransLine, TransRcptLine.Quantity, 1, TransRcptLine."Total CO2e"); + end; + + local procedure CopyTransShiplineFromTransLine(TransLine: Record "Transfer Line"; var TransShptLine: Record "Transfer Shipment Line") + begin + TransShptLine."Sust. Account No." := TransLine."Sust. Account No."; + TransShptLine."Sust. Account Name" := TransLine."Sust. Account Name"; + TransShptLine."Sust. Account Category" := TransLine."Sust. Account Category"; + TransShptLine."Sust. Account Subcategory" := TransLine."Sust. Account Subcategory"; + TransShptLine."CO2e per Unit" := TransLine."CO2e per Unit"; + end; + + local procedure CopyTransReceiptlineFromTransLine(TransLine: Record "Transfer Line"; var TransReceiptLine: Record "Transfer Receipt Line") + begin + TransReceiptLine."Sust. Account No." := TransLine."Sust. Account No."; + TransReceiptLine."Sust. Account Name" := TransLine."Sust. Account Name"; + TransReceiptLine."Sust. Account Category" := TransLine."Sust. Account Category"; + TransReceiptLine."Sust. Account Subcategory" := TransLine."Sust. Account Subcategory"; + TransReceiptLine."CO2e per Unit" := TransLine."CO2e per Unit"; + end; + + local procedure UpdateSustainabilityItemJournalLine(var ItemJournalLine: Record "Item Journal Line"; var TransferLine: Record "Transfer Line") + var + GHGCredit: Boolean; + Sign: Integer; + CO2eToPost: Decimal; + begin + GHGCredit := IsGHGCreditLine(TransferLine); + + Sign := GetPostingSign(ItemJournalLine."Document Type" = ItemJournalLine."Document Type"::"Transfer Shipment", ItemJournalLine."Document Type" = ItemJournalLine."Document Type"::"Transfer Receipt", GHGCredit); + + CO2eToPost := TransferLine."CO2e per Unit" * Abs(ItemJournalLine.Quantity) * TransferLine."Qty. per Unit of Measure"; + + CO2eToPost := CO2eToPost * Sign; + + if not CanPostSustainabilityJnlLine(TransferLine."Sust. Account No.", TransferLine."Sust. Account Category", TransferLine."Sust. Account Subcategory", CO2eToPost) then + exit; + + ItemJournalLine."Sust. Account No." := TransferLine."Sust. Account No."; + ItemJournalLine."Sust. Account Name" := TransferLine."Sust. Account Name"; + ItemJournalLine."Sust. Account Category" := TransferLine."Sust. Account Category"; + ItemJournalLine."Sust. Account Subcategory" := TransferLine."Sust. Account Subcategory"; + ItemJournalLine."CO2e per Unit" := TransferLine."CO2e per Unit"; + ItemJournalLine."Total CO2e" := CO2eToPost; + end; + + local procedure UpdatePostedSustainabilityEmissionOrderLine(var TransferLine: Record "Transfer Line"; Qty: Decimal; Ship: Boolean; Receive: Boolean) + var + PostedEmissionCO2e: Decimal; + GHGCredit: Boolean; + Sign: Integer; + begin + GHGCredit := IsGHGCreditLine(TransferLine); + Sign := GetPostingSign(Ship, Receive, GHGCredit); + + UpdatePostedSustainabilityEmission(TransferLine, Qty, Sign, PostedEmissionCO2e); + + if Ship then + TransferLine."Posted Shipped Total CO2e" += PostedEmissionCO2e; + + if Receive then + TransferLine."Posted Received Total CO2e" += PostedEmissionCO2e; + + TransferLine.Modify(); + end; + + local procedure UpdatePostedSustainabilityEmission(TransferLine: Record "Transfer Line"; Quantity: Decimal; Sign: Integer; var PostedEmissionCO2e: Decimal) + begin + PostedEmissionCO2e := (TransferLine."CO2e per Unit" * Abs(Quantity) * TransferLine."Qty. per Unit of Measure") * Sign; + end; + + local procedure PostSustainabilityLine(TransferLine: Record "Transfer Line"; Qty: Decimal; Ship: Boolean; Receive: Boolean; SrcCode: Code[10]; GenJnlLineDocNo: Code[20]) + var + TransferHeader: Record "Transfer Header"; + SustainabilityJnlLine: Record "Sustainability Jnl. Line"; + SustainabilityPostMgt: Codeunit "Sustainability Post Mgt"; + GHGCredit: Boolean; + CO2eToPost: Decimal; + Sign: Integer; + begin + GHGCredit := IsGHGCreditLine(TransferLine); + + Sign := GetPostingSign(Ship, Receive, GHGCredit); + + CO2eToPost := TransferLine."CO2e per Unit" * Abs(Qty) * TransferLine."Qty. per Unit of Measure" * Sign; + + TransferHeader.Get(TransferLine."Document No."); + if not CanPostSustainabilityJnlLine(TransferLine."Sust. Account No.", TransferLine."Sust. Account Category", TransferLine."Sust. Account Subcategory", CO2eToPost) then + exit; + + SustainabilityJnlLine.Init(); + SustainabilityJnlLine."Journal Template Name" := ''; + SustainabilityJnlLine."Journal Batch Name" := ''; + SustainabilityJnlLine."Source Code" := SrcCode; + SustainabilityJnlLine.Validate("Posting Date", TransferHeader."Posting Date"); + + if GHGCredit then + SustainabilityJnlLine.Validate("Document Type", SustainabilityJnlLine."Document Type"::"GHG Credit") + else + SustainabilityJnlLine.Validate("Document Type", SustainabilityJnlLine."Document Type"::Invoice); + + SustainabilityJnlLine.Validate("Document No.", GenJnlLineDocNo); + SustainabilityJnlLine.Validate("Account No.", TransferLine."Sust. Account No."); + SustainabilityJnlLine.Validate("Account Category", TransferLine."Sust. Account Category"); + SustainabilityJnlLine.Validate("Account Subcategory", TransferLine."Sust. Account Subcategory"); + SustainabilityJnlLine.Validate("Unit of Measure", TransferLine."Unit of Measure Code"); + SustainabilityJnlLine."Dimension Set ID" := TransferLine."Dimension Set ID"; + SustainabilityJnlLine."Shortcut Dimension 1 Code" := TransferLine."Shortcut Dimension 1 Code"; + SustainabilityJnlLine."Shortcut Dimension 2 Code" := TransferLine."Shortcut Dimension 2 Code"; + SustainabilityJnlLine.Validate("CO2e Emission", CO2eToPost); + SustainabilityPostMgt.InsertLedgerEntry(SustainabilityJnlLine, TransferLine); + end; + + local procedure GetPostingSign(Ship: Boolean; Receive: Boolean; GHGCredit: Boolean): Integer + var + Sign: Integer; + begin + Sign := 1; + + if Ship then + if not GHGCredit then + Sign := -1; + + if Receive then + if GHGCredit then + Sign := -1; + + exit(Sign); + end; + + local procedure IsGHGCreditLine(TransLine: Record "Transfer Line"): Boolean + var + Item: Record Item; + begin + if TransLine."Item No." = '' then + exit(false); + + Item.Get(TransLine."Item No."); + + exit(Item."GHG Credit"); + end; + + local procedure CanPostSustainabilityJnlLine(AccountNo: Code[20]; AccountCategory: Code[20]; AccountSubCategory: Code[20]; CO2eToPost: Decimal): Boolean + var + SustAccountCategory: Record "Sustain. Account Category"; + SustainAccountSubcategory: Record "Sustain. Account Subcategory"; + begin + if AccountNo = '' then + exit(false); + + if SustAccountCategory.Get(AccountCategory) then + if SustAccountCategory."Water Intensity" or SustAccountCategory."Waste Intensity" or SustAccountCategory."Discharged Into Water" then + Error(NotAllowedToPostSustLedEntryForWaterOrWasteErr, AccountNo); + + if SustainAccountSubcategory.Get(AccountCategory, AccountSubCategory) then + if not SustainAccountSubcategory."Renewable Energy" then + if (CO2eToPost = 0) then + Error(EmissionMustNotBeZeroErr); + + if (CO2eToPost <> 0) then + exit(true); + end; + + var + EmissionMustNotBeZeroErr: Label 'The Emission fields must have a value that is not 0.'; + NotAllowedToPostSustLedEntryForWaterOrWasteErr: Label 'It is not allowed to post Sustainability Ledger Entry for water or waste in sales document for Account No. %1', Comment = '%1 = Sustainability Account No.'; +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/app/src/Transfer/SustainabilityTransferHeader.TableExt.al b/Apps/W1/Sustainability/app/src/Transfer/SustainabilityTransferHeader.TableExt.al new file mode 100644 index 0000000000..be2e2644f2 --- /dev/null +++ b/Apps/W1/Sustainability/app/src/Transfer/SustainabilityTransferHeader.TableExt.al @@ -0,0 +1,48 @@ +namespace Microsoft.Sustainability.Transfer; + +using Microsoft.Inventory.Transfer; +using Microsoft.Sustainability.Setup; + +tableextension 6249 "Sustainability Transfer Header" extends "Transfer Header" +{ + fields + { + field(6210; "Sustainability Lines Exist"; Boolean) + { + Caption = 'Sustainability Lines Exist'; + Editable = false; + FieldClass = FlowField; + CalcFormula = exist("Transfer Line" where("Sust. Account No." = filter('<>'''''), "Document No." = field("No."))); + } + field(6211; "Total CO2e"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + CalcFormula = sum("Transfer Line"."Total CO2e" where("Document No." = field("No."), "Derived From Line No." = const(0))); + Caption = 'Total CO2e'; + Editable = false; + FieldClass = FlowField; + } + field(6212; "Posted Shipped Total CO2e"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + CalcFormula = sum("Transfer Line"."Posted Shipped Total CO2e" where("Document No." = field("No."), "Derived From Line No." = const(0))); + Caption = 'Posted Shipped Total CO2e'; + Editable = false; + FieldClass = FlowField; + } + field(6213; "Posted Received Total CO2e"; Decimal) + { + AutoFormatType = 11; + AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places")); + CalcFormula = sum("Transfer Line"."Posted Received Total CO2e" where("Document No." = field("No."), "Derived From Line No." = const(0))); + Caption = 'Posted Received Total CO2e'; + Editable = false; + FieldClass = FlowField; + } + } + + var + SustainabilitySetup: Record "Sustainability Setup"; +} \ No newline at end of file diff --git a/Apps/W1/Sustainability/test/src/SustCertificateTest.Codeunit.al b/Apps/W1/Sustainability/test/src/SustCertificateTest.Codeunit.al index 2de8c1fe31..c5ea726251 100644 --- a/Apps/W1/Sustainability/test/src/SustCertificateTest.Codeunit.al +++ b/Apps/W1/Sustainability/test/src/SustCertificateTest.Codeunit.al @@ -1733,7 +1733,6 @@ codeunit 148187 "Sust. Certificate Test" ItemCard."Replenishment System".SetValue(Item."Replenishment System"::"Prod. Order"); // [VERIFY] Verify Default Emissions field should be clear when Replenishment System is set to non-purchase on item. - ItemCard."Default Sust. Account".AssertEquals(''); ItemCard."Default CH4 Emission".AssertEquals(0); ItemCard."Default CO2 Emission".AssertEquals(0); ItemCard."Default N2O Emission".AssertEquals(0); diff --git a/Apps/W1/Sustainability/test/src/SustValueEntryTest.Codeunit.al b/Apps/W1/Sustainability/test/src/SustValueEntryTest.Codeunit.al index 6bc1942e46..415debc155 100644 --- a/Apps/W1/Sustainability/test/src/SustValueEntryTest.Codeunit.al +++ b/Apps/W1/Sustainability/test/src/SustValueEntryTest.Codeunit.al @@ -2142,6 +2142,7 @@ codeunit 148190 "Sust. Value Entry Test" ProductionOrder: Record "Production Order"; ProductionBOMHeader: Record "Production BOM Header"; SustainabilityAccount: Record "Sustainability Account"; + ProductionOrderLine: Record "Prod. Order Line"; CategoryCode: Code[20]; SubcategoryCode: Code[20]; AccountCode: Code[20]; @@ -2173,7 +2174,6 @@ codeunit 148190 "Sust. Value Entry Test" CompItem.Modify(); // [GIVEN] Update "Default Sust. Account","CO2e per Unit" in Production Item. - ProdItem.Validate("Default Sust. Account", AccountCode); ProdItem.Validate("CO2e per Unit", LibraryRandom.RandInt(100)); ProdItem.Modify(); @@ -2189,11 +2189,20 @@ codeunit 148190 "Sust. Value Entry Test" Quanity := LibraryRandom.RandIntInRange(10, 10); ExpectedCO2ePerUnit := (WorkCenter."CO2e per Unit" * Quanity + CompItem."CO2e per Unit" * Quanity) / Quanity; - // [WHEN] Create and Refresh Production Order. + // [GIVEN] Create and Refresh Production Order. CreateAndRefreshProductionOrder(ProductionOrder, ProductionOrder.Status::Released, ProdItem."No.", Quanity); + // [GIVEN] Find Prod Order Line. + ProductionOrderLine.SetRange(Status, ProductionOrder.Status); + ProductionOrderLine.SetRange("Prod. Order No.", ProductionOrder."No."); + ProductionOrderLine.FindFirst(); + + // [WHEN] Update "Sust. Account No." in Prod Order Line. + ProductionOrderLine.Validate("Sust. Account No.", AccountCode); + ProductionOrderLine.Modify(); + // [THEN] Verify "Default Sust. Account","CO2e per Unit","Total CO2e" should be updated after refresh Production Order. - VerifyProductionOrderLine(ProductionOrder, ProdItem."Default Sust. Account", ExpectedCO2ePerUnit, ExpectedCO2ePerUnit * Quanity, 0); + VerifyProductionOrderLine(ProductionOrder, AccountCode, ExpectedCO2ePerUnit, ExpectedCO2ePerUnit * Quanity, 0); VerifyProductionOrderComponent(ProductionOrder, CompItem."Default Sust. Account", CompItem."CO2e per Unit", CompItem."CO2e per Unit" * Quanity, 0); VerifyProductionOrderRoutingLine(ProductionOrder, WorkCenter."Default Sust. Account", WorkCenter."CO2e per Unit", WorkCenter."CO2e per Unit" * Quanity, 0); end; @@ -2208,6 +2217,7 @@ codeunit 148190 "Sust. Value Entry Test" ProductionOrder: Record "Production Order"; ProductionBOMHeader: Record "Production BOM Header"; SustainabilityAccount: Record "Sustainability Account"; + ProductionOrderLine: Record "Prod. Order Line"; ExpectedCO2ePerUnit: array[2] of Decimal; CategoryCode: Code[20]; SubcategoryCode: Code[20]; @@ -2244,7 +2254,6 @@ codeunit 148190 "Sust. Value Entry Test" CompItem.Modify(); // [GIVEN] Update "Default Sust. Account","CO2e per Unit" in Production Item. - ProdItem.Validate("Default Sust. Account", AccountCode); ProdItem.Validate("CO2e per Unit", LibraryRandom.RandInt(100)); ProdItem.Modify(); @@ -2265,8 +2274,17 @@ codeunit 148190 "Sust. Value Entry Test" // [WHEN] Create and Refresh Production Order. CreateAndRefreshProductionOrder(ProductionOrder, ProductionOrder.Status::Released, ProdItem."No.", Quanity); + // [GIVEN] Find Prod Order Line. + ProductionOrderLine.SetRange(Status, ProductionOrder.Status); + ProductionOrderLine.SetRange("Prod. Order No.", ProductionOrder."No."); + ProductionOrderLine.FindFirst(); + + // [WHEN] Update "Sust. Account No." in Prod Order Line. + ProductionOrderLine.Validate("Sust. Account No.", AccountCode); + ProductionOrderLine.Modify(); + // [THEN] Verify "Default Sust. Account","CO2e per Unit","Total CO2e" should be updated after refresh Production Order. - VerifyProductionOrderLine(ProductionOrder, ProdItem."Default Sust. Account", ExpectedCO2ePerUnitForProdOrderLine, ExpectedCO2ePerUnitForProdOrderLine * Quanity, 0); + VerifyProductionOrderLine(ProductionOrder, AccountCode, ExpectedCO2ePerUnitForProdOrderLine, ExpectedCO2ePerUnitForProdOrderLine * Quanity, 0); VerifyProductionOrderRoutingLine(ProductionOrder, WorkCenter."Default Sust. Account", ExpectedCO2ePerUnit[1], ExpectedCO2ePerUnit[1] * Quanity, 0); VerifyProductionOrderComponent(ProductionOrder, CompItem."Default Sust. Account", ExpectedCO2ePerUnit[2], ExpectedCO2ePerUnit[2] * Quanity, 0); end; diff --git a/Apps/W1/Sustainability/test/src/SustainabilityPostingTest.Codeunit.al b/Apps/W1/Sustainability/test/src/SustainabilityPostingTest.Codeunit.al index 808a7c769b..ec6bdccf6c 100644 --- a/Apps/W1/Sustainability/test/src/SustainabilityPostingTest.Codeunit.al +++ b/Apps/W1/Sustainability/test/src/SustainabilityPostingTest.Codeunit.al @@ -12,7 +12,11 @@ codeunit 148184 "Sustainability Posting Test" LibraryInventory: Codeunit "Library - Inventory"; LibraryERM: Codeunit "Library - ERM"; LibrarySales: Codeunit "Library - Sales"; + LibraryAssembly: Codeunit "Library - Assembly"; + LibraryWarehouse: Codeunit "Library - Warehouse"; + LibraryManufacturing: Codeunit "Library - Manufacturing"; LibraryVariableStorage: Codeunit "Library - Variable Storage"; + NotificationLifecycleMgt: Codeunit "Notification Lifecycle Mgt."; InformationTakenToLedgerEntryLbl: Label '%1 on the Ledger Entry should be taken from %2', Locked = true; ValueMustBeEqualErr: Label '%1 must be equal to %2 in the %3.', Comment = '%1 = Field Caption , %2 = Expected Value, %3 = Table Caption'; FilterMustBeEqualErr: Label 'Filter must be equal to %1 in the %2', Comment = '%1 = Expected Value , %2 = Page Caption'; @@ -2818,6 +2822,973 @@ codeunit 148184 "Sustainability Posting Test" PostedSalesCrMemoSubformPage."Total CO2e".AssertEquals(TotalCO2e); end; + [Test] + procedure VerifySustainabilityFieldsShouldBeUpdatedFromItemInAssemblyDocument() + var + CompItem: Record Item; + ParentItem: Record Item; + AssemblyHeader: Record "Assembly Header"; + AssemblyLine: Record "Assembly Line"; + SustainabilityAccount: Record "Sustainability Account"; + CategoryCode: Code[20]; + SubcategoryCode: Code[20]; + Quantity: Decimal; + AccountCode: array[2] of Code[20]; + CO2ePerUnit: array[2] of Decimal; + begin + // [SCENARIO 537480] Verify Sustainability fields should be updated from Item When "Item No." and Quantity is validated in Assembly Header and Line. + LibrarySustainability.CleanUpBeforeTesting(); + + // [GIVEN] Create a Sustainability Account. + CreateSustainabilityAccount(AccountCode[1], CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10)); + SustainabilityAccount.Get(AccountCode[1]); + + // [GIVEN] Generate Random "CO2e Per Unit" and Quantity. + CO2ePerUnit[1] := LibraryRandom.RandIntInRange(10, 10); + CO2ePerUnit[2] := LibraryRandom.RandIntInRange(20, 20); + Quantity := LibraryRandom.RandIntInRange(10, 10); + + // [GIVEN] Create a Assembly Item. + CreateAssembledItem(ParentItem, "Assembly Policy"::"Assemble-to-Order", 1, 1); + ParentItem.Validate("Default Sust. Account", AccountCode[1]); + ParentItem.Validate("CO2e per Unit", CO2ePerUnit[1]); + ParentItem.Modify(); + + // [GIVEN] Create Sustainability Account and update Sustainability Account No., "CO2e per unit" in Component item. + CreateAndUpdateSustAccOnCompItem(ParentItem, CompItem, AccountCode[2], CO2ePerUnit[2]); + + // [WHEN] Create Assembly Document. + LibraryAssembly.CreateAssemblyHeader(AssemblyHeader, WorkDate() + 1, ParentItem."No.", '', Quantity, ''); + AssemblyHeader.Validate("Location Code", ''); + AssemblyHeader.Modify(); + + // [THEN] Verify "Sust. Account No.","CO2e per Unit","Total CO2e" in Assembly Header and Assembly Line. + GetAssemblyLine(AssemblyHeader, AssemblyLine); + Assert.AreEqual( + AccountCode[1], + AssemblyHeader."Sust. Account No.", + StrSubstNo(ValueMustBeEqualErr, AssemblyHeader.FieldCaption("Sust. Account No."), AccountCode[1], AssemblyHeader.TableCaption())); + Assert.AreEqual( + CO2ePerUnit[1], + AssemblyHeader."CO2e per Unit", + StrSubstNo(ValueMustBeEqualErr, AssemblyHeader.FieldCaption("CO2e per Unit"), CO2ePerUnit[1], AssemblyHeader.TableCaption())); + Assert.AreEqual( + CO2ePerUnit[1] * Quantity, + AssemblyHeader."Total CO2e", + StrSubstNo(ValueMustBeEqualErr, AssemblyHeader.FieldCaption("Total CO2e"), CO2ePerUnit[1] * Quantity, AssemblyHeader.TableCaption())); + Assert.AreEqual( + AccountCode[2], + AssemblyLine."Sust. Account No.", + StrSubstNo(ValueMustBeEqualErr, AssemblyLine.FieldCaption("Sust. Account No."), AccountCode[2], AssemblyLine.TableCaption())); + Assert.AreEqual( + CO2ePerUnit[2], + AssemblyLine."CO2e per Unit", + StrSubstNo(ValueMustBeEqualErr, AssemblyLine.FieldCaption("CO2e per Unit"), CO2ePerUnit[2], AssemblyLine.TableCaption())); + Assert.AreEqual( + CO2ePerUnit[2] * Quantity, + AssemblyLine."Total CO2e", + StrSubstNo(ValueMustBeEqualErr, AssemblyLine.FieldCaption("Total CO2e"), CO2ePerUnit[2] * Quantity, AssemblyLine.TableCaption())); + NotificationLifecycleMgt.RecallAllNotifications(); + end; + + [Test] + procedure VerifySustainabilityFieldsShouldBeUpdatedWhenDocumentIsPosted() + var + CompItem: Record Item; + ParentItem: Record Item; + AssemblyHeader: Record "Assembly Header"; + PostedAssemblyHeader: Record "Posted Assembly Header"; + PostedAssemblyLine: Record "Posted Assembly Line"; + SustainabilityAccount: Record "Sustainability Account"; + CategoryCode: Code[20]; + SubcategoryCode: Code[20]; + Quantity: Decimal; + AccountCode: array[2] of Code[20]; + CO2ePerUnit: array[2] of Decimal; + begin + // [SCENARIO 537480] Verify Sustainability fields should be updated When Document is posted. + LibrarySustainability.CleanUpBeforeTesting(); + + // [GIVEN] Create a Sustainability Account. + CreateSustainabilityAccount(AccountCode[1], CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10)); + SustainabilityAccount.Get(AccountCode[1]); + + // [GIVEN] Generate Random "CO2e Per Unit" and Quantity. + CO2ePerUnit[1] := LibraryRandom.RandIntInRange(10, 10); + CO2ePerUnit[2] := LibraryRandom.RandIntInRange(20, 20); + Quantity := LibraryRandom.RandIntInRange(10, 10); + + // [GIVEN] Create a Assembly Item. + CreateAssembledItem(ParentItem, "Assembly Policy"::"Assemble-to-Order", 1, 1); + ParentItem.Validate("Default Sust. Account", AccountCode[1]); + ParentItem.Validate("CO2e per Unit", CO2ePerUnit[1]); + ParentItem.Modify(); + + // [GIVEN] Create Sustainability Account and update Sustainability Account No., "CO2e per unit" in Component item. + CreateAndUpdateSustAccOnCompItem(ParentItem, CompItem, AccountCode[2], CO2ePerUnit[2]); + + // [GIVEN] Increase Inventory of an Item. + AddItemToInventory(CompItem, LibraryRandom.RandInt(1000)); + + // [GIVEN] Create Assembly Document. + LibraryAssembly.CreateAssemblyHeader(AssemblyHeader, WorkDate() + 1, ParentItem."No.", '', Quantity, ''); + + // [WHEN] Post Assembly Document. + LibraryAssembly.PostAssemblyHeader(AssemblyHeader, ''); + + // [THEN] Verify "Sust. Account No.","CO2e per Unit","Total CO2e" in Posted Assembly Header and Posted Assembly Line. + GetPostedAssemblyHeader(PostedAssemblyHeader, ParentItem."No."); + GetPostedAssemblyLine(PostedAssemblyHeader, PostedAssemblyLine); + Assert.AreEqual( + AccountCode[1], + PostedAssemblyHeader."Sust. Account No.", + StrSubstNo(ValueMustBeEqualErr, PostedAssemblyHeader.FieldCaption("Sust. Account No."), AccountCode[1], PostedAssemblyHeader.TableCaption())); + Assert.AreEqual( + CO2ePerUnit[1], + PostedAssemblyHeader."CO2e per Unit", + StrSubstNo(ValueMustBeEqualErr, PostedAssemblyHeader.FieldCaption("CO2e per Unit"), CO2ePerUnit[1], PostedAssemblyHeader.TableCaption())); + Assert.AreEqual( + CO2ePerUnit[1] * Quantity, + PostedAssemblyHeader."Total CO2e", + StrSubstNo(ValueMustBeEqualErr, PostedAssemblyHeader.FieldCaption("Total CO2e"), CO2ePerUnit[1] * Quantity, PostedAssemblyHeader.TableCaption())); + Assert.AreEqual( + AccountCode[2], + PostedAssemblyLine."Sust. Account No.", + StrSubstNo(ValueMustBeEqualErr, PostedAssemblyLine.FieldCaption("Sust. Account No."), AccountCode[2], PostedAssemblyLine.TableCaption())); + Assert.AreEqual( + CO2ePerUnit[2], + PostedAssemblyLine."CO2e per Unit", + StrSubstNo(ValueMustBeEqualErr, PostedAssemblyLine.FieldCaption("CO2e per Unit"), CO2ePerUnit[2], PostedAssemblyLine.TableCaption())); + Assert.AreEqual( + CO2ePerUnit[2] * Quantity, + PostedAssemblyLine."Total CO2e", + StrSubstNo(ValueMustBeEqualErr, PostedAssemblyLine.FieldCaption("Total CO2e"), CO2ePerUnit[2] * Quantity, PostedAssemblyLine.TableCaption())); + NotificationLifecycleMgt.RecallAllNotifications(); + end; + + [Test] + procedure VerifySustainabilityFieldsShouldBeUpdatedWhenDocumentIsPartiallyPosted() + var + CompItem: Record Item; + ParentItem: Record Item; + AssemblyHeader: Record "Assembly Header"; + PostedAssemblyHeader: Record "Posted Assembly Header"; + PostedAssemblyLine: Record "Posted Assembly Line"; + SustainabilityAccount: Record "Sustainability Account"; + CategoryCode: Code[20]; + SubcategoryCode: Code[20]; + Quantity: Decimal; + AccountCode: array[2] of Code[20]; + CO2ePerUnit: array[2] of Decimal; + begin + // [SCENARIO 537480] Verify Sustainability fields should be updated When Document is partially posted. + LibrarySustainability.CleanUpBeforeTesting(); + + // [GIVEN] Create a Sustainability Account. + CreateSustainabilityAccount(AccountCode[1], CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10)); + SustainabilityAccount.Get(AccountCode[1]); + + // [GIVEN] Generate Random "CO2e Per Unit" and Quantity. + CO2ePerUnit[1] := LibraryRandom.RandIntInRange(10, 10); + CO2ePerUnit[2] := LibraryRandom.RandIntInRange(20, 20); + Quantity := LibraryRandom.RandIntInRange(10, 10); + + // [GIVEN] Create a Assembly Item. + CreateAssembledItem(ParentItem, "Assembly Policy"::"Assemble-to-Order", 1, 1); + ParentItem.Validate("Default Sust. Account", AccountCode[1]); + ParentItem.Validate("CO2e per Unit", CO2ePerUnit[1]); + ParentItem.Modify(); + + // [GIVEN] Create Sustainability Account and update Sustainability Account No., "CO2e per unit" in Component item. + CreateAndUpdateSustAccOnCompItem(ParentItem, CompItem, AccountCode[2], CO2ePerUnit[2]); + + // [GIVEN] Increase Inventory of an Item. + AddItemToInventory(CompItem, LibraryRandom.RandInt(1000)); + + // [GIVEN] Create Assembly Document. + LibraryAssembly.CreateAssemblyHeader(AssemblyHeader, WorkDate() + 1, ParentItem."No.", '', Quantity, ''); + AssemblyHeader.Validate("Quantity to Assemble", LibraryRandom.RandIntInRange(5, 5)); + AssemblyHeader.Modify(); + + // [WHEN] Post Assembly Document. + LibraryAssembly.PostAssemblyHeader(AssemblyHeader, ''); + + // [THEN] Verify "Sust. Account No.","CO2e per Unit","Total CO2e" in Posted Assembly Header and Posted Assembly Line. + GetPostedAssemblyHeader(PostedAssemblyHeader, ParentItem."No."); + GetPostedAssemblyLine(PostedAssemblyHeader, PostedAssemblyLine); + Assert.AreEqual( + AccountCode[1], + PostedAssemblyHeader."Sust. Account No.", + StrSubstNo(ValueMustBeEqualErr, PostedAssemblyHeader.FieldCaption("Sust. Account No."), AccountCode[1], PostedAssemblyHeader.TableCaption())); + Assert.AreEqual( + CO2ePerUnit[1], + PostedAssemblyHeader."CO2e per Unit", + StrSubstNo(ValueMustBeEqualErr, PostedAssemblyHeader.FieldCaption("CO2e per Unit"), CO2ePerUnit[1], PostedAssemblyHeader.TableCaption())); + Assert.AreEqual( + CO2ePerUnit[1] * LibraryRandom.RandIntInRange(5, 5), + PostedAssemblyHeader."Total CO2e", + StrSubstNo(ValueMustBeEqualErr, PostedAssemblyHeader.FieldCaption("Total CO2e"), CO2ePerUnit[1] * LibraryRandom.RandIntInRange(5, 5), PostedAssemblyHeader.TableCaption())); + Assert.AreEqual( + AccountCode[2], + PostedAssemblyLine."Sust. Account No.", + StrSubstNo(ValueMustBeEqualErr, PostedAssemblyLine.FieldCaption("Sust. Account No."), AccountCode[2], PostedAssemblyLine.TableCaption())); + Assert.AreEqual( + CO2ePerUnit[2], + PostedAssemblyLine."CO2e per Unit", + StrSubstNo(ValueMustBeEqualErr, PostedAssemblyLine.FieldCaption("CO2e per Unit"), CO2ePerUnit[2], PostedAssemblyLine.TableCaption())); + Assert.AreEqual( + CO2ePerUnit[2] * LibraryRandom.RandIntInRange(5, 5), + PostedAssemblyLine."Total CO2e", + StrSubstNo(ValueMustBeEqualErr, PostedAssemblyLine.FieldCaption("Total CO2e"), CO2ePerUnit[2] * LibraryRandom.RandIntInRange(5, 5), PostedAssemblyLine.TableCaption())); + NotificationLifecycleMgt.RecallAllNotifications(); + end; + + [Test] + procedure VerifySustainabilityValueAndLedgerEntryShouldBeCreatedWhenDocumentIsPosted() + var + CompItem: Record Item; + ParentItem: Record Item; + AssemblyHeader: Record "Assembly Header"; + PostedAssemblyHeader: Record "Posted Assembly Header"; + SustainabilityAccount: Record "Sustainability Account"; + SustainabilityLedgerEntry: Record "Sustainability Ledger Entry"; + SustainabilityValueEntry: Record "Sustainability Value Entry"; + CategoryCode: Code[20]; + SubcategoryCode: Code[20]; + Quantity: Decimal; + AccountCode: array[2] of Code[20]; + CO2ePerUnit: array[2] of Decimal; + begin + // [SCENARIO 537480] Verify Sustainability Value and Ledger Entry should be created When Document is posted. + LibrarySustainability.CleanUpBeforeTesting(); + + // [GIVEN] Create a Sustainability Account. + CreateSustainabilityAccount(AccountCode[1], CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10)); + SustainabilityAccount.Get(AccountCode[1]); + + // [GIVEN] Generate Random "CO2e Per Unit" and Quantity. + CO2ePerUnit[1] := LibraryRandom.RandIntInRange(10, 10); + CO2ePerUnit[2] := LibraryRandom.RandIntInRange(20, 20); + Quantity := LibraryRandom.RandIntInRange(10, 10); + + // [GIVEN] Create a Assembly Item. + CreateAssembledItem(ParentItem, "Assembly Policy"::"Assemble-to-Order", 1, 1); + ParentItem.Validate("Default Sust. Account", AccountCode[1]); + ParentItem.Validate("CO2e per Unit", CO2ePerUnit[1]); + ParentItem.Modify(); + + // [GIVEN] Create Sustainability Account and update Sustainability Account No., "CO2e per unit" in Component item. + CreateAndUpdateSustAccOnCompItem(ParentItem, CompItem, AccountCode[2], CO2ePerUnit[2]); + + // [GIVEN] Increase Inventory of an Item. + AddItemToInventory(CompItem, LibraryRandom.RandInt(1000)); + + // [GIVEN] Create Assembly Document. + LibraryAssembly.CreateAssemblyHeader(AssemblyHeader, WorkDate() + 1, ParentItem."No.", '', Quantity, ''); + + // [WHEN] Post Assembly Document. + LibraryAssembly.PostAssemblyHeader(AssemblyHeader, ''); + + // [THEN] Verify Sustainability Value and Ledger Entry When Assembly Document is posted. + GetPostedAssemblyHeader(PostedAssemblyHeader, ParentItem."No."); + SustainabilityLedgerEntry.SetRange("Document No.", PostedAssemblyHeader."No."); + + Assert.RecordCount(SustainabilityLedgerEntry, 2); + VerifySustainabilityLedgerEntry(AccountCode[1], CO2ePerUnit[1] * Quantity); + VerifySustainabilityLedgerEntry(AccountCode[2], -CO2ePerUnit[2] * Quantity); + + SustainabilityValueEntry.SetRange("Document No.", PostedAssemblyHeader."No."); + Assert.RecordCount(SustainabilityValueEntry, 2); + VerifySustainabilityValueEntry(ParentItem."No.", 0, CO2ePerUnit[1] * Quantity); + VerifySustainabilityValueEntry(CompItem."No.", 0, -CO2ePerUnit[2] * Quantity); + + NotificationLifecycleMgt.RecallAllNotifications(); + end; + + [Test] + procedure VerifySustainabilityValueAndLedgerEntryShouldBeCreatedWhenDocumentIsPartiallyPosted() + var + CompItem: Record Item; + ParentItem: Record Item; + AssemblyHeader: Record "Assembly Header"; + PostedAssemblyHeader: Record "Posted Assembly Header"; + SustainabilityAccount: Record "Sustainability Account"; + SustainabilityLedgerEntry: Record "Sustainability Ledger Entry"; + SustainabilityValueEntry: Record "Sustainability Value Entry"; + CategoryCode: Code[20]; + SubcategoryCode: Code[20]; + Quantity: Decimal; + AccountCode: array[2] of Code[20]; + CO2ePerUnit: array[2] of Decimal; + begin + // [SCENARIO 537480] Verify Sustainability Value and Ledger Entry should be created When Document is partially posted. + LibrarySustainability.CleanUpBeforeTesting(); + + // [GIVEN] Create a Sustainability Account. + CreateSustainabilityAccount(AccountCode[1], CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10)); + SustainabilityAccount.Get(AccountCode[1]); + + // [GIVEN] Generate Random "CO2e Per Unit" and Quantity. + CO2ePerUnit[1] := LibraryRandom.RandIntInRange(10, 10); + CO2ePerUnit[2] := LibraryRandom.RandIntInRange(20, 20); + Quantity := LibraryRandom.RandIntInRange(10, 10); + + // [GIVEN] Create a Assembly Item. + CreateAssembledItem(ParentItem, "Assembly Policy"::"Assemble-to-Order", 1, 1); + ParentItem.Validate("Default Sust. Account", AccountCode[1]); + ParentItem.Validate("CO2e per Unit", CO2ePerUnit[1]); + ParentItem.Modify(); + + // [GIVEN] Create Sustainability Account and update Sustainability Account No., "CO2e per unit" in Component item. + CreateAndUpdateSustAccOnCompItem(ParentItem, CompItem, AccountCode[2], CO2ePerUnit[2]); + + // [GIVEN] Increase Inventory of an Item. + AddItemToInventory(CompItem, LibraryRandom.RandInt(1000)); + + // [GIVEN] Create Assembly Document. + LibraryAssembly.CreateAssemblyHeader(AssemblyHeader, WorkDate() + 1, ParentItem."No.", '', Quantity, ''); + AssemblyHeader.Validate("Quantity to Assemble", LibraryRandom.RandIntInRange(5, 5)); + AssemblyHeader.Modify(); + + // [WHEN] Post Assembly Document. + LibraryAssembly.PostAssemblyHeader(AssemblyHeader, ''); + + // [THEN] Verify Sustainability Value and Ledger Entry When Assembly Document is partially posted. + GetPostedAssemblyHeader(PostedAssemblyHeader, ParentItem."No."); + SustainabilityLedgerEntry.SetRange("Document No.", PostedAssemblyHeader."No."); + + Assert.RecordCount(SustainabilityLedgerEntry, 2); + VerifySustainabilityLedgerEntry(AccountCode[1], CO2ePerUnit[1] * LibraryRandom.RandIntInRange(5, 5)); + VerifySustainabilityLedgerEntry(AccountCode[2], -CO2ePerUnit[2] * LibraryRandom.RandIntInRange(5, 5)); + + SustainabilityValueEntry.SetRange("Document No.", PostedAssemblyHeader."No."); + Assert.RecordCount(SustainabilityValueEntry, 2); + VerifySustainabilityValueEntry(ParentItem."No.", 0, CO2ePerUnit[1] * LibraryRandom.RandIntInRange(5, 5)); + VerifySustainabilityValueEntry(CompItem."No.", 0, -CO2ePerUnit[2] * LibraryRandom.RandIntInRange(5, 5)); + + NotificationLifecycleMgt.RecallAllNotifications(); + end; + + [Test] + [HandlerFunctions('GLPostingPreviewHandlerForAssemblyOrder')] + procedure VerifySustainabilityValueAndLedgerEntryShouldBeCreatedDuringPreviePostingofAssemblyOrder() + var + CompItem: Record Item; + ParentItem: Record Item; + AssemblyHeader: Record "Assembly Header"; + SustainabilityAccount: Record "Sustainability Account"; + AssemblyPostYesNo: Codeunit "Assembly-Post (Yes/No)"; + CategoryCode: Code[20]; + SubcategoryCode: Code[20]; + Quantity: Decimal; + AccountCode: array[2] of Code[20]; + CO2ePerUnit: array[2] of Decimal; + begin + // [SCENARIO 537480] Verify Sustainability Value and Ledger Entry should be created during preview posting of Assembly Order. + LibrarySustainability.CleanUpBeforeTesting(); + + // [GIVEN] Create a Sustainability Account. + CreateSustainabilityAccount(AccountCode[1], CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10)); + SustainabilityAccount.Get(AccountCode[1]); + + // [GIVEN] Generate Random "CO2e Per Unit" and Quantity. + CO2ePerUnit[1] := LibraryRandom.RandIntInRange(10, 10); + CO2ePerUnit[2] := LibraryRandom.RandIntInRange(20, 20); + Quantity := LibraryRandom.RandIntInRange(10, 10); + + // [GIVEN] Create a Assembly Item. + CreateAssembledItem(ParentItem, "Assembly Policy"::"Assemble-to-Order", 1, 1); + ParentItem.Validate("Default Sust. Account", AccountCode[1]); + ParentItem.Validate("CO2e per Unit", CO2ePerUnit[1]); + ParentItem.Modify(); + + // [GIVEN] Create Sustainability Account and update Sustainability Account No., "CO2e per unit" in Component item. + CreateAndUpdateSustAccOnCompItem(ParentItem, CompItem, AccountCode[2], CO2ePerUnit[2]); + + // [GIVEN] Increase Inventory of an Item. + AddItemToInventory(CompItem, LibraryRandom.RandInt(1000)); + + // [GIVEN] Create Assembly Document. + LibraryAssembly.CreateAssemblyHeader(AssemblyHeader, WorkDate() + 1, ParentItem."No.", '', Quantity, ''); + + // [GIVEN] Save a transaction. + Commit(); + + // [WHEN] Preview Assembly Document. + asserterror AssemblyPostYesNo.Preview(AssemblyHeader); + + // [VERIFY] No errors occured - preview mode error only. + Assert.ExpectedError(''); + NotificationLifecycleMgt.RecallAllNotifications(); + end; + + [Test] + [HandlerFunctions('NavigateFindEntriesHandlerForAssemblyOrder')] + procedure VerifySustainabilityLedgerAndValueEntryShouldBeShownWhenNavigatingPostedAssemblyOrder() + var + CompItem: Record Item; + ParentItem: Record Item; + AssemblyHeader: Record "Assembly Header"; + PostedAssemblyHeader: Record "Posted Assembly Header"; + SustainabilityAccount: Record "Sustainability Account"; + CategoryCode: Code[20]; + SubcategoryCode: Code[20]; + Quantity: Decimal; + AccountCode: array[2] of Code[20]; + CO2ePerUnit: array[2] of Decimal; + begin + // [SCENARIO 537480] Verify Sustainability Ledger and Value Entry should be shown when navigating Posted Assembly Order through NavigateFindEntriesHandler handler. + LibrarySustainability.CleanUpBeforeTesting(); + + // [GIVEN] Create a Sustainability Account. + CreateSustainabilityAccount(AccountCode[1], CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10)); + SustainabilityAccount.Get(AccountCode[1]); + + // [GIVEN] Generate Random "CO2e Per Unit" and Quantity. + CO2ePerUnit[1] := LibraryRandom.RandIntInRange(10, 10); + CO2ePerUnit[2] := LibraryRandom.RandIntInRange(20, 20); + Quantity := LibraryRandom.RandIntInRange(10, 10); + + // [GIVEN] Create a Assembly Item. + CreateAssembledItem(ParentItem, "Assembly Policy"::"Assemble-to-Order", 1, 1); + ParentItem.Validate("Default Sust. Account", AccountCode[1]); + ParentItem.Validate("CO2e per Unit", CO2ePerUnit[1]); + ParentItem.Modify(); + + // [GIVEN] Create Sustainability Account and update Sustainability Account No., "CO2e per unit" in Component item. + CreateAndUpdateSustAccOnCompItem(ParentItem, CompItem, AccountCode[2], CO2ePerUnit[2]); + + // [GIVEN] Increase Inventory of an Item. + AddItemToInventory(CompItem, LibraryRandom.RandInt(1000)); + + // [GIVEN] Create Assembly Document. + LibraryAssembly.CreateAssemblyHeader(AssemblyHeader, WorkDate() + 1, ParentItem."No.", '', Quantity, ''); + + // [WHEN] Post Assembly Document. + LibraryAssembly.PostAssemblyHeader(AssemblyHeader, ''); + + // [VERIFY] Verify Sustainability Ledger Entry should be shown when navigating Posted Sales Invoice through NavigateFindEntriesHandler handler. + GetPostedAssemblyHeader(PostedAssemblyHeader, ParentItem."No."); + PostedAssemblyHeader.Navigate(); + + NotificationLifecycleMgt.RecallAllNotifications(); + end; + + [Test] + procedure VerifySustainabilityFieldsShouldBeUpdatedFromItemInTransferLine() + var + SustainabilityAccount: Record "Sustainability Account"; + TransferHeader: Record "Transfer Header"; + TransferLine: Record "Transfer Line"; + FromLocation: Record Location; + ToLocation: Record Location; + InTransitLocation: Record Location; + Item: Record Item; + CO2ePerUnit: Decimal; + Quantity: Decimal; + CategoryCode: Code[20]; + SubcategoryCode: Code[20]; + AccountCode: Code[20]; + begin + // [SCENARIO 537480] Verify Sustainability fields should be updated from Item When "Item No." in Transfer Line. + LibrarySustainability.CleanUpBeforeTesting(); + + // [GIVEN] Create a Sustainability Account. + CreateSustainabilityAccount(AccountCode, CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10)); + SustainabilityAccount.Get(AccountCode); + + // [GIVEN] Generate "CO2e per unit" and Quantity. + CO2ePerUnit := LibraryRandom.RandIntInRange(100, 100); + Quantity := LibraryRandom.RandIntInRange(10, 10); + + // [GIVEN] Create FromLocation, ToLocation and IntransitLocation that will be used to create Transfer Order. + LibraryWarehouse.CreateTransferLocations(FromLocation, ToLocation, InTransitLocation); + + // [GIVEN] Create Item with Inventory. + CreateItemWithInventory(Item, FromLocation.Code); + + // [GIVEN] Update "Default Sust. Account", "CO2e per Unit" in an Item. + Item.Get(Item."No."); + Item.Validate("Default Sust. Account", AccountCode); + Item.Validate("CO2e per Unit", CO2ePerUnit); + Item.Modify(); + + // [WHEN] Create Transfer Order. + CreateTransferOrderWithLocation(TransferHeader, Item, FromLocation.Code, ToLocation.Code, InTransitLocation.Code, Quantity); + + // [VERIFY] Verify Sustainability Ledger entry should be created when the sales document is posted. + GetTransferLine(TransferHeader, TransferLine); + Assert.AreEqual( + AccountCode, + TransferLine."Sust. Account No.", + StrSubstNo(ValueMustBeEqualErr, TransferLine.FieldCaption("Sust. Account No."), AccountCode, TransferLine.TableCaption())); + Assert.AreEqual( + CO2ePerUnit, + TransferLine."CO2e per Unit", + StrSubstNo(ValueMustBeEqualErr, TransferLine.FieldCaption("CO2e per Unit"), CO2ePerUnit, TransferLine.TableCaption())); + Assert.AreEqual( + CO2ePerUnit * Quantity, + TransferLine."Total CO2e", + StrSubstNo(ValueMustBeEqualErr, TransferLine.FieldCaption("Total CO2e"), CO2ePerUnit * Quantity, TransferLine.TableCaption())); + end; + + [Test] + procedure VerifySustainabilityFieldsShouldBeUpdatedInTransferShipmentAndReceiptLine() + var + SustainabilityAccount: Record "Sustainability Account"; + TransferShipmentLine: Record "Transfer Shipment Line"; + TransferReceiptLine: Record "Transfer Receipt Line"; + TransferHeader: Record "Transfer Header"; + FromLocation: Record Location; + ToLocation: Record Location; + InTransitLocation: Record Location; + Item: Record Item; + CO2ePerUnit: Decimal; + Quantity: Decimal; + CategoryCode: Code[20]; + SubcategoryCode: Code[20]; + AccountCode: Code[20]; + begin + // [SCENARIO 537480] Verify Sustainability fields should be updated in "Transfer Shipment Line" and "Transfer Receipt Line". + LibrarySustainability.CleanUpBeforeTesting(); + + // [GIVEN] Create a Sustainability Account. + CreateSustainabilityAccount(AccountCode, CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10)); + SustainabilityAccount.Get(AccountCode); + + // [GIVEN] Generate "CO2e per unit" and Quantity. + CO2ePerUnit := LibraryRandom.RandIntInRange(100, 100); + Quantity := LibraryRandom.RandIntInRange(10, 10); + + // [GIVEN] Create FromLocation, ToLocation and IntransitLocation that will be used to create Transfer Order. + LibraryWarehouse.CreateTransferLocations(FromLocation, ToLocation, InTransitLocation); + + // [GIVEN] Create Item with Inventory. + CreateItemWithInventory(Item, FromLocation.Code); + + // [GIVEN] Update "Default Sust. Account", "CO2e per Unit" in an Item. + Item.Get(Item."No."); + Item.Validate("Default Sust. Account", AccountCode); + Item.Validate("CO2e per Unit", CO2ePerUnit); + Item.Modify(); + + // [GIVEN] Create Transfer Order. + CreateTransferOrderWithLocation(TransferHeader, Item, FromLocation.Code, ToLocation.Code, InTransitLocation.Code, Quantity); + + // [WHEN] Post Transfer Order. + LibraryWarehouse.PostTransferOrder(TransferHeader, true, true); + + // [VERIFY] Verify Sustainability fields should be updated in "Transfer Shipment Line" and "Transfer Receipt Line". + GetTransferShipmentLine(TransferShipmentLine, Item."No."); + Assert.AreEqual( + AccountCode, + TransferShipmentLine."Sust. Account No.", + StrSubstNo(ValueMustBeEqualErr, TransferShipmentLine.FieldCaption("Sust. Account No."), AccountCode, TransferShipmentLine.TableCaption())); + Assert.AreEqual( + CO2ePerUnit, + TransferShipmentLine."CO2e per Unit", + StrSubstNo(ValueMustBeEqualErr, TransferShipmentLine.FieldCaption("CO2e per Unit"), CO2ePerUnit, TransferShipmentLine.TableCaption())); + Assert.AreEqual( + CO2ePerUnit * Quantity, + TransferShipmentLine."Total CO2e", + StrSubstNo(ValueMustBeEqualErr, TransferShipmentLine.FieldCaption("Total CO2e"), CO2ePerUnit * Quantity, TransferShipmentLine.TableCaption())); + + GetTransferReceiptLine(TransferReceiptLine, Item."No."); + Assert.AreEqual( + AccountCode, + TransferReceiptLine."Sust. Account No.", + StrSubstNo(ValueMustBeEqualErr, TransferReceiptLine.FieldCaption("Sust. Account No."), AccountCode, TransferReceiptLine.TableCaption())); + Assert.AreEqual( + CO2ePerUnit, + TransferReceiptLine."CO2e per Unit", + StrSubstNo(ValueMustBeEqualErr, TransferReceiptLine.FieldCaption("CO2e per Unit"), CO2ePerUnit, TransferReceiptLine.TableCaption())); + Assert.AreEqual( + CO2ePerUnit * Quantity, + TransferReceiptLine."Total CO2e", + StrSubstNo(ValueMustBeEqualErr, TransferReceiptLine.FieldCaption("Total CO2e"), CO2ePerUnit * Quantity, TransferReceiptLine.TableCaption())); + end; + + [Test] + procedure VerifySustainabilityFieldsShouldBeUpdatedInTransferShipmentAndReceiptLineWhenDocumentIsPartiallyPosted() + var + SustainabilityAccount: Record "Sustainability Account"; + TransferShipmentLine: Record "Transfer Shipment Line"; + TransferReceiptLine: Record "Transfer Receipt Line"; + TransferHeader: Record "Transfer Header"; + TransferLine: Record "Transfer Line"; + FromLocation: Record Location; + ToLocation: Record Location; + InTransitLocation: Record Location; + Item: Record Item; + CO2ePerUnit: Decimal; + Quantity: Decimal; + CategoryCode: Code[20]; + SubcategoryCode: Code[20]; + AccountCode: Code[20]; + begin + // [SCENARIO 537480] Verify Sustainability fields should be updated in "Transfer Shipment Line" and "Transfer Receipt Line" When Transfer Order is partially posted. + LibrarySustainability.CleanUpBeforeTesting(); + + // [GIVEN] Create a Sustainability Account. + CreateSustainabilityAccount(AccountCode, CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10)); + SustainabilityAccount.Get(AccountCode); + + // [GIVEN] Generate "CO2e per unit" and Quantity. + CO2ePerUnit := LibraryRandom.RandIntInRange(100, 100); + Quantity := LibraryRandom.RandIntInRange(10, 10); + + // [GIVEN] Create FromLocation, ToLocation and IntransitLocation that will be used to create Transfer Order. + LibraryWarehouse.CreateTransferLocations(FromLocation, ToLocation, InTransitLocation); + + // [GIVEN] Create Item with Inventory. + CreateItemWithInventory(Item, FromLocation.Code); + + // [GIVEN] Update "Default Sust. Account", "CO2e per Unit" in an Item. + Item.Get(Item."No."); + Item.Validate("Default Sust. Account", AccountCode); + Item.Validate("CO2e per Unit", CO2ePerUnit); + Item.Modify(); + + // [GIVEN] Create Transfer Order. + CreateTransferOrderWithLocation(TransferHeader, Item, FromLocation.Code, ToLocation.Code, InTransitLocation.Code, Quantity); + + // [GIVEN] Update "Qty. to Ship" in Transfer Line. + GetTransferLine(TransferHeader, TransferLine); + TransferLine.Validate("Qty. to Ship", LibraryRandom.RandIntInRange(5, 5)); + TransferLine.Modify(); + + // [WHEN] Post Transfer Order. + LibraryWarehouse.PostTransferOrder(TransferHeader, true, true); + + // [VERIFY] Verify Sustainability fields should be updated in "Transfer Shipment Line" and "Transfer Receipt Line". + GetTransferShipmentLine(TransferShipmentLine, Item."No."); + Assert.AreEqual( + AccountCode, + TransferShipmentLine."Sust. Account No.", + StrSubstNo(ValueMustBeEqualErr, TransferShipmentLine.FieldCaption("Sust. Account No."), AccountCode, TransferShipmentLine.TableCaption())); + Assert.AreEqual( + CO2ePerUnit, + TransferShipmentLine."CO2e per Unit", + StrSubstNo(ValueMustBeEqualErr, TransferShipmentLine.FieldCaption("CO2e per Unit"), CO2ePerUnit, TransferShipmentLine.TableCaption())); + Assert.AreEqual( + CO2ePerUnit * LibraryRandom.RandIntInRange(5, 5), + TransferShipmentLine."Total CO2e", + StrSubstNo(ValueMustBeEqualErr, TransferShipmentLine.FieldCaption("Total CO2e"), CO2ePerUnit * LibraryRandom.RandIntInRange(5, 5), TransferShipmentLine.TableCaption())); + + GetTransferReceiptLine(TransferReceiptLine, Item."No."); + Assert.AreEqual( + AccountCode, + TransferReceiptLine."Sust. Account No.", + StrSubstNo(ValueMustBeEqualErr, TransferReceiptLine.FieldCaption("Sust. Account No."), AccountCode, TransferReceiptLine.TableCaption())); + Assert.AreEqual( + CO2ePerUnit, + TransferReceiptLine."CO2e per Unit", + StrSubstNo(ValueMustBeEqualErr, TransferReceiptLine.FieldCaption("CO2e per Unit"), CO2ePerUnit, TransferReceiptLine.TableCaption())); + Assert.AreEqual( + CO2ePerUnit * LibraryRandom.RandIntInRange(5, 5), + TransferReceiptLine."Total CO2e", + StrSubstNo(ValueMustBeEqualErr, TransferReceiptLine.FieldCaption("Total CO2e"), CO2ePerUnit * LibraryRandom.RandIntInRange(5, 5), TransferReceiptLine.TableCaption())); + end; + + [Test] + procedure VerifySustainabilityValueAndLedgerEntryWhenDocumentIsPartiallyPosted() + var + SustainabilityAccount: Record "Sustainability Account"; + TransferShipmentLine: Record "Transfer Shipment Line"; + TransferReceiptLine: Record "Transfer Receipt Line"; + TransferHeader: Record "Transfer Header"; + TransferLine: Record "Transfer Line"; + FromLocation: Record Location; + ToLocation: Record Location; + InTransitLocation: Record Location; + Item: Record Item; + CO2ePerUnit: Decimal; + Quantity: Decimal; + CategoryCode: Code[20]; + SubcategoryCode: Code[20]; + AccountCode: Code[20]; + begin + // [SCENARIO 537480] Verify Sustainability Value Entry and Sustainability Ledger Entry When Transfer Order is partially posted. + LibrarySustainability.CleanUpBeforeTesting(); + + // [GIVEN] Create a Sustainability Account. + CreateSustainabilityAccount(AccountCode, CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10)); + SustainabilityAccount.Get(AccountCode); + + // [GIVEN] Generate "CO2e per unit" and Quantity. + CO2ePerUnit := LibraryRandom.RandIntInRange(100, 100); + Quantity := LibraryRandom.RandIntInRange(10, 10); + + // [GIVEN] Create FromLocation, ToLocation and IntransitLocation that will be used to create Transfer Order. + LibraryWarehouse.CreateTransferLocations(FromLocation, ToLocation, InTransitLocation); + + // [GIVEN] Create Item with Inventory. + CreateItemWithInventory(Item, FromLocation.Code); + + // [GIVEN] Update "Default Sust. Account", "CO2e per Unit" in an Item. + Item.Get(Item."No."); + Item.Validate("Default Sust. Account", AccountCode); + Item.Validate("CO2e per Unit", CO2ePerUnit); + Item.Modify(); + + // [GIVEN] Create Transfer Order. + CreateTransferOrderWithLocation(TransferHeader, Item, FromLocation.Code, ToLocation.Code, InTransitLocation.Code, Quantity); + + // [GIVEN] Update "Qty. to Ship" in Transfer Line. + GetTransferLine(TransferHeader, TransferLine); + TransferLine.Validate("Qty. to Ship", LibraryRandom.RandIntInRange(5, 5)); + TransferLine.Modify(); + + // [WHEN] Post Transfer Order. + LibraryWarehouse.PostTransferOrder(TransferHeader, true, true); + + // [VERIFY] Verify Sustainability Value Entry and Sustainability Ledger Entry for "Transfer Shipment" and "Transfer Receipt". + GetTransferShipmentLine(TransferShipmentLine, Item."No."); + VerifySustainabilityValueEntryForTransferOrder(TransferShipmentLine."Document No.", CO2ePerUnit, -CO2ePerUnit * LibraryRandom.RandIntInRange(5, 5)); + VerifySustainabilityLedgerEntryForTransferOrder(TransferShipmentLine."Document No.", AccountCode, -CO2ePerUnit * LibraryRandom.RandIntInRange(5, 5)); + + GetTransferReceiptLine(TransferReceiptLine, Item."No."); + VerifySustainabilityValueEntryForTransferOrder(TransferReceiptLine."Document No.", CO2ePerUnit, CO2ePerUnit * LibraryRandom.RandIntInRange(5, 5)); + VerifySustainabilityLedgerEntryForTransferOrder(TransferReceiptLine."Document No.", AccountCode, CO2ePerUnit * LibraryRandom.RandIntInRange(5, 5)); + end; + + [Test] + [HandlerFunctions('TransferOrderPostOptionsHandler,GLPostingPreviewHandlerForTransferOrder')] + procedure VerifySustainabilityValueAndLedgerEntryShouldBeCreatedForShipDuringPreviePostingofTransferOrder() + var + SustainabilityAccount: Record "Sustainability Account"; + TransferHeader: Record "Transfer Header"; + TransferLine: Record "Transfer Line"; + FromLocation: Record Location; + ToLocation: Record Location; + InTransitLocation: Record Location; + Item: Record Item; + TransferOrderPostYesNo: Codeunit "TransferOrder-Post (Yes/No)"; + CO2ePerUnit: Decimal; + Quantity: Decimal; + CategoryCode: Code[20]; + SubcategoryCode: Code[20]; + AccountCode: Code[20]; + begin + // [SCENARIO 537480] Verify Sustainability Value and Ledger Entry should be created for ship during preview posting of Transfer Order. + LibrarySustainability.CleanUpBeforeTesting(); + + // [GIVEN] Create a Sustainability Account. + CreateSustainabilityAccount(AccountCode, CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10)); + SustainabilityAccount.Get(AccountCode); + + // [GIVEN] Generate "CO2e per unit" and Quantity. + CO2ePerUnit := LibraryRandom.RandIntInRange(100, 100); + Quantity := LibraryRandom.RandIntInRange(10, 10); + + // [GIVEN] Create FromLocation, ToLocation and IntransitLocation that will be used to create Transfer Order. + LibraryWarehouse.CreateTransferLocations(FromLocation, ToLocation, InTransitLocation); + + // [GIVEN] Create Item with Inventory. + CreateItemWithInventory(Item, FromLocation.Code); + + // [GIVEN] Update "Default Sust. Account", "CO2e per Unit" in an Item. + Item.Get(Item."No."); + Item.Validate("Default Sust. Account", AccountCode); + Item.Validate("CO2e per Unit", CO2ePerUnit); + Item.Modify(); + + // [GIVEN] Create Transfer Order. + CreateTransferOrderWithLocation(TransferHeader, Item, FromLocation.Code, ToLocation.Code, InTransitLocation.Code, Quantity); + + // [GIVEN] Update "Qty. to Ship" in Transfer Line. + GetTransferLine(TransferHeader, TransferLine); + TransferLine.Validate("Qty. to Ship", LibraryRandom.RandIntInRange(5, 5)); + TransferLine.Modify(); + + // [GIVEN] Save a transaction. + Commit(); + + // [WHEN] Preview Transfer Order for Ship. + LibraryVariableStorage.Enqueue(1); // Choice 1 is ship + asserterror TransferOrderPostYesNo.Preview(TransferHeader); + + // [VERIFY] No errors occured - preview mode error only. + Assert.ExpectedError(''); + end; + + [Test] + [HandlerFunctions('TransferOrderPostOptionsHandler,GLPostingPreviewHandlerForTransferOrder')] + procedure VerifySustainabilityValueAndLedgerEntryShouldBeCreatedForReceiptDuringPreviePostingofTransferOrder() + var + SustainabilityAccount: Record "Sustainability Account"; + TransferHeader: Record "Transfer Header"; + TransferLine: Record "Transfer Line"; + FromLocation: Record Location; + ToLocation: Record Location; + InTransitLocation: Record Location; + Item: Record Item; + TransferOrderPostYesNo: Codeunit "TransferOrder-Post (Yes/No)"; + CO2ePerUnit: Decimal; + Quantity: Decimal; + CategoryCode: Code[20]; + SubcategoryCode: Code[20]; + AccountCode: Code[20]; + begin + // [SCENARIO 537480] Verify Sustainability Value and Ledger Entry should be created for Receipt during preview posting of Transfer Order. + LibrarySustainability.CleanUpBeforeTesting(); + + // [GIVEN] Create a Sustainability Account. + CreateSustainabilityAccount(AccountCode, CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10)); + SustainabilityAccount.Get(AccountCode); + + // [GIVEN] Generate "CO2e per unit" and Quantity. + CO2ePerUnit := LibraryRandom.RandIntInRange(100, 100); + Quantity := LibraryRandom.RandIntInRange(10, 10); + + // [GIVEN] Create FromLocation, ToLocation and IntransitLocation that will be used to create Transfer Order. + LibraryWarehouse.CreateTransferLocations(FromLocation, ToLocation, InTransitLocation); + + // [GIVEN] Create Item with Inventory. + CreateItemWithInventory(Item, FromLocation.Code); + + // [GIVEN] Update "Default Sust. Account", "CO2e per Unit" in an Item. + Item.Get(Item."No."); + Item.Validate("Default Sust. Account", AccountCode); + Item.Validate("CO2e per Unit", CO2ePerUnit); + Item.Modify(); + + // [GIVEN] Create Transfer Order. + CreateTransferOrderWithLocation(TransferHeader, Item, FromLocation.Code, ToLocation.Code, InTransitLocation.Code, Quantity); + + // [GIVEN] Update "Qty. to Ship" in Transfer Line. + GetTransferLine(TransferHeader, TransferLine); + TransferLine.Validate("Qty. to Ship", LibraryRandom.RandIntInRange(5, 5)); + TransferLine.Modify(); + + // [GIVEN] Post Tranfer Order + LibraryWarehouse.PostTransferOrder(TransferHeader, true, false); + + // [GIVEN] Save a transaction. + Commit(); + + // [WHEN] Preview Transfer Order for Receive. + LibraryVariableStorage.Enqueue(2); // Choice 1 is Receive + asserterror TransferOrderPostYesNo.Preview(TransferHeader); + + // [VERIFY] No errors occured - preview mode error only. + Assert.ExpectedError(''); + end; + + [Test] + [HandlerFunctions('NavigateFindEntriesHandlerForTransferOrder')] + procedure VerifySustainabilityValueAndLedgerEntryShouldBeShownWhenNavigatingTransferShipment() + var + SustainabilityAccount: Record "Sustainability Account"; + TransferShipmentHeader: Record "Transfer Shipment Header"; + TransferHeader: Record "Transfer Header"; + TransferLine: Record "Transfer Line"; + FromLocation: Record Location; + ToLocation: Record Location; + InTransitLocation: Record Location; + Item: Record Item; + CO2ePerUnit: Decimal; + Quantity: Decimal; + CategoryCode: Code[20]; + SubcategoryCode: Code[20]; + AccountCode: Code[20]; + begin + // [SCENARIO 537480] Verify Sustainability Ledger and Value Entry should be shown when navigating Transfer Shipment through NavigateFindEntriesHandler handler. + LibrarySustainability.CleanUpBeforeTesting(); + + // [GIVEN] Create a Sustainability Account. + CreateSustainabilityAccount(AccountCode, CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10)); + SustainabilityAccount.Get(AccountCode); + + // [GIVEN] Generate "CO2e per unit" and Quantity. + CO2ePerUnit := LibraryRandom.RandIntInRange(100, 100); + Quantity := LibraryRandom.RandIntInRange(10, 10); + + // [GIVEN] Create FromLocation, ToLocation and IntransitLocation that will be used to create Transfer Order. + LibraryWarehouse.CreateTransferLocations(FromLocation, ToLocation, InTransitLocation); + + // [GIVEN] Create Item with Inventory. + CreateItemWithInventory(Item, FromLocation.Code); + + // [GIVEN] Update "Default Sust. Account", "CO2e per Unit" in an Item. + Item.Get(Item."No."); + Item.Validate("Default Sust. Account", AccountCode); + Item.Validate("CO2e per Unit", CO2ePerUnit); + Item.Modify(); + + // [GIVEN] Create Transfer Order. + CreateTransferOrderWithLocation(TransferHeader, Item, FromLocation.Code, ToLocation.Code, InTransitLocation.Code, Quantity); + + // [GIVEN] Update "Qty. to Ship" in Transfer Line. + GetTransferLine(TransferHeader, TransferLine); + TransferLine.Validate("Qty. to Ship", LibraryRandom.RandIntInRange(5, 5)); + TransferLine.Modify(); + + // [WHEN] Post Transfer Order. + LibraryWarehouse.PostTransferOrder(TransferHeader, true, false); + + // [VERIFY] Verify Sustainability Value and Ledger Entry should be shown when navigating Transfer Shipment Header through NavigateFindEntriesHandler handler. + GetTransferShipmentHeader(TransferShipmentHeader, FromLocation.Code); + TransferShipmentHeader.Navigate(); + end; + + [Test] + [HandlerFunctions('NavigateFindEntriesHandlerForTransferOrder')] + procedure VerifySustainabilityValueAndLedgerEntryShouldBeShownWhenNavigatingTransferReceipt() + var + SustainabilityAccount: Record "Sustainability Account"; + TransferReceiptHeader: Record "Transfer Receipt Header"; + TransferHeader: Record "Transfer Header"; + TransferLine: Record "Transfer Line"; + FromLocation: Record Location; + ToLocation: Record Location; + InTransitLocation: Record Location; + Item: Record Item; + CO2ePerUnit: Decimal; + Quantity: Decimal; + CategoryCode: Code[20]; + SubcategoryCode: Code[20]; + AccountCode: Code[20]; + begin + // [SCENARIO 537480] Verify Sustainability Ledger and Value Entry should be shown when navigating Transfer Receipt through NavigateFindEntriesHandler handler. + LibrarySustainability.CleanUpBeforeTesting(); + + // [GIVEN] Create a Sustainability Account. + CreateSustainabilityAccount(AccountCode, CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10)); + SustainabilityAccount.Get(AccountCode); + + // [GIVEN] Generate "CO2e per unit" and Quantity. + CO2ePerUnit := LibraryRandom.RandIntInRange(100, 100); + Quantity := LibraryRandom.RandIntInRange(10, 10); + + // [GIVEN] Create FromLocation, ToLocation and IntransitLocation that will be used to create Transfer Order. + LibraryWarehouse.CreateTransferLocations(FromLocation, ToLocation, InTransitLocation); + + // [GIVEN] Create Item with Inventory. + CreateItemWithInventory(Item, FromLocation.Code); + + // [GIVEN] Update "Default Sust. Account", "CO2e per Unit" in an Item. + Item.Get(Item."No."); + Item.Validate("Default Sust. Account", AccountCode); + Item.Validate("CO2e per Unit", CO2ePerUnit); + Item.Modify(); + + // [GIVEN] Create Transfer Order. + CreateTransferOrderWithLocation(TransferHeader, Item, FromLocation.Code, ToLocation.Code, InTransitLocation.Code, Quantity); + + // [GIVEN] Update "Qty. to Ship" in Transfer Line. + GetTransferLine(TransferHeader, TransferLine); + TransferLine.Validate("Qty. to Ship", LibraryRandom.RandIntInRange(5, 5)); + TransferLine.Modify(); + + // [WHEN] Post Transfer Order. + LibraryWarehouse.PostTransferOrder(TransferHeader, true, true); + + // [VERIFY] Verify Sustainability Value and Ledger Entry should be shown when navigating Transfer Receipt Header through NavigateFindEntriesHandler handler. + GetTransferReceiptHeader(TransferReceiptHeader, FromLocation.Code); + TransferReceiptHeader.Navigate(); + end; + local procedure CreateUserSetup(var UserSetup: Record "User Setup"; UserID: Code[50]) begin UserSetup.Init(); @@ -3146,6 +4117,214 @@ codeunit 148184 "Sustainability Posting Test" exit(SalesHeader."No."); end; + local procedure CreateAssembledItem(var Item: Record Item; AssemblyPolicy: Enum "Assembly Policy"; NoOfComponents: Integer; QtyPer: Decimal) + begin + LibraryInventory.CreateItem(Item); + Item.Validate("Replenishment System", Item."Replenishment System"::Assembly); + Item.Validate("Assembly Policy", AssemblyPolicy); + Item.Modify(true); + + CreateAssemblyList(Item, NoOfComponents, QtyPer); + end; + + local procedure CreateAssemblyList(ParentItem: Record Item; NoOfComponents: Integer; QtyPer: Decimal) + var + Item: Record Item; + AssemblyLine: Record "Assembly Line"; + BOMComponent: Record "BOM Component"; + CompCount: Integer; + begin + // Add components - qty per is increasing same as no of components + for CompCount := 1 to NoOfComponents do begin + Clear(Item); + LibraryInventory.CreateItem(Item); + LibraryAssembly.AddEntityDimensions(AssemblyLine.Type::Item, Item."No."); + AddComponentToAssemblyList(BOMComponent, "BOM Component Type"::Item, Item."No.", ParentItem."No.", '', Item."Base Unit of Measure", QtyPer); + end; + end; + + local procedure AddComponentToAssemblyList(var BOMComponent: Record "BOM Component"; ComponentType: Enum "BOM Component Type"; ComponentNo: Code[20]; ParentItemNo: Code[20]; VariantCode: Code[10]; UOM: Code[10]; QuantityPer: Decimal) + begin + LibraryManufacturing.CreateBOMComponent(BOMComponent, ParentItemNo, ComponentType, ComponentNo, QuantityPer, UOM); + BOMComponent.Validate("Variant Code", VariantCode); + if ComponentNo = '' then + BOMComponent.Validate(Description, + LibraryUtility.GenerateRandomCode(BOMComponent.FieldNo(Description), DATABASE::"BOM Component")); + BOMComponent.Modify(true); + end; + + local procedure CreateAndUpdateSustAccOnCompItem(ParentItem: Record Item; var CompItem: Record Item; var AccountCode: Code[20]; CO2ePerUnit: Decimal) + var + CategoryCode: Code[20]; + SubcategoryCode: Code[20]; + begin + CreateSustainabilityAccount(AccountCode, CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10)); + + CompItem.Get(GetBOMComponentItemNo(ParentItem)); + CompItem.Validate("Default Sust. Account", AccountCode); + CompItem.Validate("CO2e per Unit", CO2ePerUnit); + CompItem.Modify(); + end; + + local procedure GetBOMComponentItemNo(ParentItem: Record Item): Code[20] + var + BOMComponent: Record "BOM Component"; + begin + BOMComponent.SetRange("Parent Item No.", ParentItem."No."); + BOMComponent.FindSet(); + + exit(BOMComponent."No.") + end; + + local procedure GetAssemblyLine(AssemblyHeader: Record "Assembly Header"; var AssemblyLine: Record "Assembly Line") + begin + AssemblyLine.SetRange("Document Type", AssemblyHeader."Document Type"); + AssemblyLine.SetRange("Document No.", AssemblyHeader."No."); + AssemblyLine.FindSet(); + end; + + local procedure GetPostedAssemblyHeader(var PostedAssemblyHeader: Record "Posted Assembly Header"; ItemNo: Code[20]) + begin + PostedAssemblyHeader.SetRange("Item No.", ItemNo); + PostedAssemblyHeader.FindSet(); + end; + + local procedure GetPostedAssemblyLine(PostedAssemblyHeader: Record "Posted Assembly Header"; var PostedAssemblyLine: Record "Posted Assembly Line") + begin + PostedAssemblyLine.SetRange("Document No.", PostedAssemblyHeader."No."); + PostedAssemblyLine.FindSet(); + end; + + local procedure AddItemToInventory(Item: Record Item; Quantity: Decimal) + var + ItemJournalLine: Record "Item Journal Line"; + ItemJournalTemplate: Record "Item Journal Template"; + ItemJournalBatch: Record "Item Journal Batch"; + begin + ItemJournalTemplate.SetRange(Type, ItemJournalTemplate.Type::Item); + ItemJournalTemplate.SetRange(Recurring, false); + ItemJournalTemplate.FindFirst(); + ItemJournalBatch.SetRange("Journal Template Name", ItemJournalTemplate.Name); + ItemJournalBatch.FindFirst(); + + LibraryInventory.CreateItemJournalLine( + ItemJournalLine, ItemJournalTemplate.Name, ItemJournalBatch.Name, + ItemJournalLine."Entry Type"::"Positive Adjmt.", Item."No.", Quantity); + + LibraryInventory.PostItemJournalLine(ItemJournalTemplate.Name, ItemJournalBatch.Name); + end; + + local procedure VerifySustainabilityLedgerEntry(AccountCode: Code[20]; CO2eEmission: Decimal) + var + SustainabilityLedgerEntry: Record "Sustainability Ledger Entry"; + begin + SustainabilityLedgerEntry.SetRange("Account No.", AccountCode); + SustainabilityLedgerEntry.FindFirst(); + + Assert.AreEqual( + CO2eEmission, + SustainabilityLedgerEntry."CO2e Emission", + StrSubstNo(ValueMustBeEqualErr, SustainabilityLedgerEntry.FieldCaption("CO2e Emission"), CO2eEmission, SustainabilityLedgerEntry.TableCaption())); + end; + + local procedure VerifySustainabilityValueEntry(ItemNo: Code[20]; CO2eEmissionExpected: Decimal; CO2eEmissionActual: Decimal) + var + SustainabilityValueEntry: Record "Sustainability Value Entry"; + begin + SustainabilityValueEntry.SetRange("Item No.", ItemNo); + SustainabilityValueEntry.FindFirst(); + + Assert.AreEqual( + CO2eEmissionExpected, + SustainabilityValueEntry."CO2e Amount (Expected)", + StrSubstNo(ValueMustBeEqualErr, SustainabilityValueEntry.FieldCaption("CO2e Amount (Expected)"), CO2eEmissionExpected, SustainabilityValueEntry.TableCaption())); + Assert.AreEqual( + CO2eEmissionActual, + SustainabilityValueEntry."CO2e Amount (Actual)", + StrSubstNo(ValueMustBeEqualErr, SustainabilityValueEntry.FieldCaption("CO2e Amount (Actual)"), CO2eEmissionActual, SustainabilityValueEntry.TableCaption())); + end; + + local procedure CreateItemWithInventory(var Item: Record Item; FromLocationCode: Code[10]) + var + ItemJournalLine: Record "Item Journal Line"; + begin + LibraryInventory.CreateItem(Item); + LibraryInventory.CreateItemJournalLineInItemTemplate(ItemJournalLine, Item."No.", FromLocationCode, '', LibraryRandom.RandIntInRange(100, 200)); + LibraryInventory.PostItemJournalLine(ItemJournalLine."Journal Template Name", ItemJournalLine."Journal Batch Name"); + end; + + local procedure CreateTransferOrderWithLocation(var TransferHeader: Record "Transfer Header"; Item: Record Item; FromLocationCode: Code[10]; ToLocationCode: Code[10]; IntransitLocationCode: Code[10]; Quantity: Decimal) + var + TransferLine: Record "Transfer Line"; + begin + LibraryWarehouse.CreateTransferHeader(TransferHeader, FromLocationCode, ToLocationCode, IntransitLocationCode); + LibraryWarehouse.CreateTransferLine(TransferHeader, TransferLine, Item."No.", Quantity); + end; + + local procedure GetTransferLine(TransferHeader: Record "Transfer Header"; var TransferLine: Record "Transfer Line") + begin + TransferLine.SetRange("Document No.", TransferHeader."No."); + TransferLine.FindSet(); + end; + + local procedure GetTransferShipmentHeader(var TransferShipmentHeader: Record "Transfer Shipment Header"; FromLocationCode: Code[10]) + begin + TransferShipmentHeader.SetRange("Transfer-from Code", FromLocationCode); + TransferShipmentHeader.FindSet(); + end; + + local procedure GetTransferShipmentLine(var TransferShipmentLine: Record "Transfer Shipment Line"; ItemNo: Code[20]) + begin + TransferShipmentLine.SetRange("Item No.", ItemNo); + TransferShipmentLine.FindSet(); + end; + + local procedure GetTransferReceiptHeader(var TransferReceiptHeader: Record "Transfer Receipt Header"; FromLocationCode: Code[10]) + begin + TransferReceiptHeader.SetRange("Transfer-from Code", FromLocationCode); + TransferReceiptHeader.FindSet(); + end; + + local procedure GetTransferReceiptLine(var TransferReceiptLine: Record "Transfer Receipt Line"; ItemNo: Code[20]) + begin + TransferReceiptLine.SetRange("Item No.", ItemNo); + TransferReceiptLine.FindSet(); + end; + + local procedure VerifySustainabilityValueEntryForTransferOrder(DocumentNo: Code[20]; CO2ePerUnit: Decimal; CO2eEmission: Decimal) + var + SustainabilityValueEntry: Record "Sustainability Value Entry"; + begin + SustainabilityValueEntry.SetRange("Document No.", DocumentNo); + SustainabilityValueEntry.FindFirst(); + Assert.RecordCount(SustainabilityValueEntry, 1); + Assert.AreEqual( + CO2ePerUnit, + SustainabilityValueEntry."CO2e per Unit", + StrSubstNo(ValueMustBeEqualErr, SustainabilityValueEntry.FieldCaption("CO2e per Unit"), CO2ePerUnit, SustainabilityValueEntry.TableCaption())); + Assert.AreEqual( + CO2eEmission, + SustainabilityValueEntry."CO2e Amount (Actual)", + StrSubstNo(ValueMustBeEqualErr, SustainabilityValueEntry.FieldCaption("CO2e Amount (Actual)"), CO2eEmission, SustainabilityValueEntry.TableCaption())); + end; + + local procedure VerifySustainabilityLedgerEntryForTransferOrder(DocumentNo: Code[20]; AccountCode: Code[20]; CO2eEmission: Decimal) + var + SustainabilityLedgerEntry: Record "Sustainability Ledger Entry"; + begin + SustainabilityLedgerEntry.SetRange("Document No.", DocumentNo); + SustainabilityLedgerEntry.FindFirst(); + Assert.RecordCount(SustainabilityLedgerEntry, 1); + Assert.AreEqual( + AccountCode, + SustainabilityLedgerEntry."Account No.", + StrSubstNo(ValueMustBeEqualErr, SustainabilityLedgerEntry.FieldCaption("Account No."), AccountCode, SustainabilityLedgerEntry.TableCaption())); + Assert.AreEqual( + CO2eEmission, + SustainabilityLedgerEntry."CO2e Emission", + StrSubstNo(ValueMustBeEqualErr, SustainabilityLedgerEntry.FieldCaption("CO2e Emission"), CO2eEmission, SustainabilityLedgerEntry.TableCaption())); + end; + [ModalPageHandler] [Scope('OnPrem')] procedure PurchaseOrderStatisticsPageHandler(var PurchaseOrderStatisticsPage: TestPage "Purchase Order Statistics") @@ -3272,4 +4451,60 @@ codeunit 148184 "Sustainability Posting Test" procedure MessageHandler(Msg: Text[1024]) begin end; + + [PageHandler] + [Scope('OnPrem')] + procedure GLPostingPreviewHandlerForAssemblyOrder(var GLPostingPreview: TestPage "G/L Posting Preview") + begin + GLPostingPreview.Filter.SetFilter("Table ID", Format(Database::"Sustainability Value Entry")); + GLPostingPreview."No. of Records".AssertEquals(2); + + GLPostingPreview.Filter.SetFilter("Table ID", Format(Database::"Sustainability Ledger Entry")); + GLPostingPreview."No. of Records".AssertEquals(2); + GLPostingPreview.OK().Invoke(); + end; + + [PageHandler] + [Scope('OnPrem')] + + procedure NavigateFindEntriesHandlerForAssemblyOrder(var Navigate: TestPage Navigate) + begin + Navigate.Filter.SetFilter("Table ID", Format(Database::"Sustainability Ledger Entry")); + Navigate."No. of Records".AssertEquals(2); + + Navigate.Filter.SetFilter("Table ID", Format(Database::"Sustainability Value Entry")); + Navigate."No. of Records".AssertEquals(2); + Navigate.OK().Invoke(); + end; + + [PageHandler] + [Scope('OnPrem')] + procedure GLPostingPreviewHandlerForTransferOrder(var GLPostingPreview: TestPage "G/L Posting Preview") + begin + GLPostingPreview.Filter.SetFilter("Table ID", Format(Database::"Sustainability Value Entry")); + GLPostingPreview."No. of Records".AssertEquals(1); + + GLPostingPreview.Filter.SetFilter("Table ID", Format(Database::"Sustainability Ledger Entry")); + GLPostingPreview."No. of Records".AssertEquals(1); + GLPostingPreview.OK().Invoke(); + end; + + [PageHandler] + [Scope('OnPrem')] + procedure NavigateFindEntriesHandlerForTransferOrder(var Navigate: TestPage Navigate) + begin + Navigate.Filter.SetFilter("Table ID", Format(Database::"Sustainability Ledger Entry")); + Navigate."No. of Records".AssertEquals(1); + + Navigate.Filter.SetFilter("Table ID", Format(Database::"Sustainability Value Entry")); + Navigate."No. of Records".AssertEquals(1); + Navigate.OK().Invoke(); + end; + + [StrMenuHandler] + [Scope('OnPrem')] + procedure TransferOrderPostOptionsHandler(Options: Text[1024]; var Choice: Integer; Instruction: Text[1024]) + begin + Choice := LibraryVariableStorage.DequeueInteger(); + end; } \ No newline at end of file diff --git a/Apps/W1/VATGroupManagement/app/src/Enums/VATGroupAuthTypeOnPrem.Enum.al b/Apps/W1/VATGroupManagement/app/src/Enums/VATGroupAuthTypeOnPrem.Enum.al index 19c948f585..6a03ceab90 100644 --- a/Apps/W1/VATGroupManagement/app/src/Enums/VATGroupAuthTypeOnPrem.Enum.al +++ b/Apps/W1/VATGroupManagement/app/src/Enums/VATGroupAuthTypeOnPrem.Enum.al @@ -6,6 +6,7 @@ namespace Microsoft.Finance.VAT.Group; enum 4704 "VAT Group Auth Type OnPrem" { +#if not CLEAN25 value(0; WebServiceAccessKey) { Caption = 'Web Service Access Key'; @@ -13,10 +14,12 @@ enum 4704 "VAT Group Auth Type OnPrem" ObsoleteReason = 'OAuth2 is the only authentication option for making a Business Central API call.'; ObsoleteTag = '25.0'; } +#endif value(1; OAuth2) { Caption = 'OAuth2'; } +#if not CLEAN25 value(2; WindowsAuthentication) { Caption = 'Windows Authentication'; @@ -24,4 +27,5 @@ enum 4704 "VAT Group Auth Type OnPrem" ObsoleteReason = 'OAuth2 is the only authentication option for making a Business Central API call.'; ObsoleteTag = '25.0'; } +#endif } \ No newline at end of file diff --git a/Apps/W1/VATGroupManagement/app/src/Enums/VATGroupAuthTypeSaas.Enum.al b/Apps/W1/VATGroupManagement/app/src/Enums/VATGroupAuthTypeSaas.Enum.al index ced723c9ab..007f3c6db3 100644 --- a/Apps/W1/VATGroupManagement/app/src/Enums/VATGroupAuthTypeSaas.Enum.al +++ b/Apps/W1/VATGroupManagement/app/src/Enums/VATGroupAuthTypeSaas.Enum.al @@ -6,6 +6,7 @@ namespace Microsoft.Finance.VAT.Group; enum 4705 "VAT Group Auth Type Saas" { +#if not CLEAN25 value(0; WebServiceAccessKey) { Caption = 'Web Service Access Key'; @@ -13,6 +14,7 @@ enum 4705 "VAT Group Auth Type Saas" ObsoleteReason = 'OAuth2 is the only authentication option for making a Business Central API call.'; ObsoleteTag = '25.0'; } +#endif value(1; OAuth2) { Caption = 'OAuth2'; diff --git a/Apps/W1/VATGroupManagement/test/src/LibraryVATGroup.Codeunit.al b/Apps/W1/VATGroupManagement/test/src/LibraryVATGroup.Codeunit.al index b9760c7051..875f26bd4f 100644 --- a/Apps/W1/VATGroupManagement/test/src/LibraryVATGroup.Codeunit.al +++ b/Apps/W1/VATGroupManagement/test/src/LibraryVATGroup.Codeunit.al @@ -228,7 +228,9 @@ codeunit 139525 "Library - VAT Group" VATReportSetup."Group Representative API URL" := CopyStr(GetRepresentativeURL(), 1, MaxStrLen(VATReportSetup."Group Representative API URL")); VATReportSetup."Group Representative Company" := CopyStr(CompanyName(), 1, MaxStrLen(VATReportSetup."Group Representative Company")); +#if not CLEAN25 VATReportSetup."VAT Group Authentication Type" := VATReportSetup."VAT Group Authentication Type"::WindowsAuthentication; +#endif VATReportSetup."Group Member ID" := CreateGuid(); VATReportSetup."Manual Receive Period CU ID" := 0; VATReportSetup.Modify(); diff --git a/Build/Packages.json b/Build/Packages.json index 771933b077..5ba44f2094 100644 --- a/Build/Packages.json +++ b/Build/Packages.json @@ -4,7 +4,7 @@ "Source": "NuGet.org" }, "AppBaselines-BCArtifacts": { - "Version": "25.1.25885.0", + "Version": "25.2.27075.0", "Source": "BCArtifacts", "_comment": "Used to fetch app baselines from BC artifacts" }