From dbc9b7d9a5c5aa928d95b35998f66637b63a8c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Tue, 29 Jul 2025 12:20:31 +0300 Subject: [PATCH 01/28] feat: Add support for Purchase Credit Memo in E-Document processing - Introduced new enum and interface for handling Purchase Credit Memos. - Updated Purchase Invoice codeunit to validate and handle additional fields. - Enhanced E-Document Purchase Header table to include E-Document Type. - Implemented logic in PEPPOL handler for processing Credit Notes. - Added methods for populating Credit Memo headers and lines in XML. - Refactored existing methods to utilize a new XML helper codeunit for cleaner code. - Updated unit of measure provider interface to include retrieval of default unit of measure. --- .../app/src/Document/EDocumentType.Enum.al | 1 + .../Helpers/EDocumentXMLHelper.Codeunit.al | 80 +++++++ .../EDocumentCreatePurchDoc.Codeunit.al | 4 + .../EDocCreatePurchCrMemo.Codeunit.al | 193 +++++++++++++++ .../FinishDraft/EDocCreatePurchCrMemo.Enum.al | 17 ++ .../EDocCreatePurchaseInvoice.Codeunit.al | 7 +- .../EDocCreatePurchaseInvoice.Enum.al | 4 +- .../Import/ImportEDocumentProcess.Codeunit.al | 6 + .../EDocProcCustomizations.Enum.al | 10 +- .../PrepareDraft/EDocProviders.Codeunit.al | 35 ++- .../PreparePurchaseEDocDraft.Codeunit.al | 2 +- .../Purchase/EDocumentPurchaseHeader.Table.al | 5 + .../EDocumentPEPPOLHandler.Codeunit.al | 221 ++++++++++-------- ...umentCreatePurchaseCreditMemo.Interface.al | 21 ++ .../IUnitOfMeasureProvider.Interface.al | 9 + 15 files changed, 506 insertions(+), 109 deletions(-) create mode 100644 Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al create mode 100644 Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al create mode 100644 Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Enum.al create mode 100644 Apps/W1/EDocument/app/src/Processing/Interfaces/IEDocumentCreatePurchaseCreditMemo.Interface.al diff --git a/Apps/W1/EDocument/app/src/Document/EDocumentType.Enum.al b/Apps/W1/EDocument/app/src/Document/EDocumentType.Enum.al index 1a9d9c8fd0..ef31a4b8d6 100644 --- a/Apps/W1/EDocument/app/src/Document/EDocumentType.Enum.al +++ b/Apps/W1/EDocument/app/src/Document/EDocumentType.Enum.al @@ -56,6 +56,7 @@ enum 6121 "E-Document Type" implements IEDocumentFinishDraft value(10; "Purchase Credit Memo") { Caption = 'Purchase Credit Memo'; + Implementation = IEDocumentFinishDraft = "E-Doc. Create Purch. Cr. Memo"; } value(11; "Service Order") { diff --git a/Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al b/Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al new file mode 100644 index 0000000000..647d652426 --- /dev/null +++ b/Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al @@ -0,0 +1,80 @@ +// ------------------------------------------------------------------------------------------------ +// 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.Helpers; + +using Microsoft.Finance.GeneralLedger.Setup; + +codeunit 6127 "EDocument XML Helper" +{ + Access = Internal; + + internal procedure SetCurrencyValueInField(XMLDocument: XmlDocument; XMLNamespaces: XmlNamespaceManager; Path: Text; MaxLength: Integer; var CurrencyField: Code[10]) + var + GLSetup: Record "General Ledger Setup"; + XMLNode: XmlNode; + CurrencyCode: Code[10]; + begin + if not XMLDocument.SelectSingleNode(Path, XMLNamespaces, XMLNode) then + exit; + + GLSetup.Get(); + +#pragma warning disable AA0139 // false positive + if XMLNode.IsXmlElement() then begin + CurrencyCode := CopyStr(XMLNode.AsXmlElement().InnerText(), 1, MaxLength); + if GLSetup."LCY Code" <> CurrencyCode then + CurrencyField := CurrencyCode; + exit; + end; + + if XMLNode.IsXmlAttribute() then begin + CurrencyCode := CopyStr(XMLNode.AsXmlAttribute().Value, 1, MaxLength); + if GLSetup."LCY Code" <> CurrencyCode then + CurrencyField := CurrencyCode; + exit; + end; +#pragma warning restore AA0139 + end; + + internal procedure SetStringValueInField(XMLDocument: XmlDocument; XMLNamespaces: XmlNamespaceManager; Path: Text; MaxLength: Integer; var Field: Text) + var + XMLNode: XmlNode; + begin + if not XMLDocument.SelectSingleNode(Path, XMLNamespaces, XMLNode) then + exit; + + if XMLNode.IsXmlElement() then begin + Field := CopyStr(XMLNode.AsXmlElement().InnerText(), 1, MaxLength); + exit; + end; + + if XMLNode.IsXmlAttribute() then begin + Field := CopyStr(XMLNode.AsXmlAttribute().Value(), 1, MaxLength); + exit; + end; + end; + + internal procedure SetNumberValueInField(XMLDocument: XmlDocument; XMLNamespaces: XmlNamespaceManager; Path: Text; var DecimalValue: Decimal) + var + XMLNode: XmlNode; + begin + if not XMLDocument.SelectSingleNode(Path, XMLNamespaces, XMLNode) then + exit; + + if XMLNode.AsXmlElement().InnerText() <> '' then + Evaluate(DecimalValue, XMLNode.AsXmlElement().InnerText(), 9); + end; + + internal procedure SetDateValueInField(XMLDocument: XmlDocument; XMLNamespaces: XmlNamespaceManager; Path: Text; var DateValue: Date) + var + XMLNode: XmlNode; + begin + if not XMLDocument.SelectSingleNode(Path, XMLNamespaces, XMLNode) then + exit; + + if XMLNode.AsXmlElement().InnerText() <> '' then + Evaluate(DateValue, XMLNode.AsXmlElement().InnerText(), 9); + end; +} \ No newline at end of file diff --git a/Apps/W1/EDocument/app/src/Processing/EDocumentCreatePurchDoc.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/EDocumentCreatePurchDoc.Codeunit.al index 928cef694e..3520716ddb 100644 --- a/Apps/W1/EDocument/app/src/Processing/EDocumentCreatePurchDoc.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/EDocumentCreatePurchDoc.Codeunit.al @@ -156,6 +156,10 @@ codeunit 6136 "E-Document Create Purch. Doc." EDocumentImportHelper.ProcessField(EDocument, DocumentLine, PurchaseField, TempDocumentLine.Field(PurchaseField."No.")); until PurchaseField.Next() = 0; + // After processing all fields, set direct unit cost to be the same as in the import file + if Format(DocumentLine.Field(PurchaseLine.FieldNo(Type)).Value()) <> '0' then + EDocumentImportHelper.ProcessDecimalField(EDocument, DocumentLine, PurchaseLine.FieldNo("Direct Unit Cost"), TempDocumentLine.Field(PurchaseLine.FieldNo("Direct Unit Cost")).Value()); + OnCreateNewPurchLineOnBeforeRecRefModify(EDocument, TempDocumentHeader, DocumentHeader, TempDocumentLine, DocumentLine); DocumentLine.Modify(true); until TempDocumentLine.Next() = 0; diff --git a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al new file mode 100644 index 0000000000..28ac635f1d --- /dev/null +++ b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al @@ -0,0 +1,193 @@ +// ------------------------------------------------------------------------------------------------ +// 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.Processing.Import; + +using Microsoft.eServices.EDocument; +using Microsoft.Finance.Dimension; +using Microsoft.Purchases.Document; +using Microsoft.eServices.EDocument.Processing.Interfaces; +using Microsoft.eServices.EDocument.Processing.Import.Purchase; +using Microsoft.Finance.GeneralLedger.Setup; +using Microsoft.Purchases.Payables; +using Microsoft.Purchases.Posting; +using System.Telemetry; +using Microsoft.Foundation.Attachment; + +/// +/// Dealing with the creation of the purchase credit memo after the draft has been populated. +/// +codeunit 6105 "E-Doc. Create Purch. Cr. Memo" implements IEDocumentFinishDraft, IEDocumentCreatePurchaseCreditMemo +{ + Access = Internal; + Permissions = tabledata "Dimension Set Tree Node" = im, + tabledata "Dimension Set Entry" = im; + + var + Telemetry: Codeunit "Telemetry"; + EDocImpSessionTelemetry: Codeunit "E-Doc. Imp. Session Telemetry"; + CreditMemoAlreadyExistsErr: Label 'A purchase credit memo with external document number %1 already exists for vendor %2.', Comment = '%1 = Vendor Credit Memo No., %2 = Vendor No.'; + DraftLineDoesNotConstantTypeAndNumberErr: Label 'One of the draft lines do not contain the type and number. Please, specify these fields manually.'; + + procedure ApplyDraftToBC(EDocument: Record "E-Document"; EDocImportParameters: Record "E-Doc. Import Parameters"): RecordId + var + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + PurchaseHeader: Record "Purchase Header"; + DocumentAttachmentMgt: Codeunit "Document Attachment Mgmt"; + IEDocumentFinishPurchaseDraft: Interface IEDocumentCreatePurchaseCreditMemo; + begin + EDocumentPurchaseHeader.GetFromEDocument(EDocument); + IEDocumentFinishPurchaseDraft := EDocImportParameters."Processing Customizations"; + PurchaseHeader := IEDocumentFinishPurchaseDraft.CreatePurchaseCreditMemo(EDocument); + PurchaseHeader.SetRecFilter(); + PurchaseHeader.FindFirst(); + PurchaseHeader."Doc. Amount Incl. VAT" := EDocumentPurchaseHeader.Total; + PurchaseHeader."Doc. Amount VAT" := EDocumentPurchaseHeader."Total VAT"; + PurchaseHeader.TestField("Document Type", "Purchase Document Type"::"Credit Memo"); + PurchaseHeader.TestField("No."); + PurchaseHeader."E-Document Link" := EDocument.SystemId; + PurchaseHeader.Modify(); + + // Post document creation + DocumentAttachmentMgt.CopyAttachments(EDocument, PurchaseHeader); + DocumentAttachmentMgt.DeleteAttachedDocuments(EDocument); + + // Post document validation - Silently emit telemetry + if not TryValidateDocumentTotals(PurchaseHeader) then + EDocImpSessionTelemetry.SetBool('Totals Validation Failed', true); + + exit(PurchaseHeader.RecordId); + end; + + procedure RevertDraftActions(EDocument: Record "E-Document") + var + PurchaseHeader: Record "Purchase Header"; + begin + PurchaseHeader.SetRange("E-Document Link", EDocument.SystemId); + if not PurchaseHeader.FindFirst() then + exit; + PurchaseHeader.TestField("Document Type", "Purchase Document Type"::"Credit Memo"); + Clear(PurchaseHeader."E-Document Link"); + PurchaseHeader.Delete(true); + end; + + procedure CreatePurchaseCreditMemo(EDocument: Record "E-Document"): Record "Purchase Header" + var + PurchaseHeader: Record "Purchase Header"; + GLSetup: Record "General Ledger Setup"; + VendorLedgerEntry: Record "Vendor Ledger Entry"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + PurchaseLine: Record "Purchase Line"; + EDocumentPurchaseHistMapping: Codeunit "E-Doc. Purchase Hist. Mapping"; + DimensionManagement: Codeunit DimensionManagement; + PurchaseLineCombinedDimensions: array[10] of Integer; + StopCreatingPurchaseCreditMemo: Boolean; + VendorCreditMemoNo: Code[35]; + GlobalDim1, GlobalDim2 : Code[20]; + begin + EDocumentPurchaseHeader.GetFromEDocument(EDocument); + if not AllDraftLinesHaveTypeAndNumberSpecificed(EDocumentPurchaseHeader) then begin + Telemetry.LogMessage('0000PLZ', 'Draft line does not contain type or number', Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::All); + Error(DraftLineDoesNotConstantTypeAndNumberErr); + end; + EDocumentPurchaseHeader.TestField("E-Document Entry No."); + PurchaseHeader.Validate("Document Type", "Purchase Document Type"::"Credit Memo"); + PurchaseHeader.Validate("Buy-from Vendor No.", EDocumentPurchaseHeader."[BC] Vendor No."); + + VendorCreditMemoNo := CopyStr(EDocumentPurchaseHeader."Sales Invoice No.", 1, MaxStrLen(PurchaseHeader."Vendor Cr. Memo No.")); + VendorLedgerEntry.SetLoadFields("Entry No."); + VendorLedgerEntry.ReadIsolation := VendorLedgerEntry.ReadIsolation::ReadUncommitted; + StopCreatingPurchaseCreditMemo := PurchaseHeader.FindPostedDocumentWithSameExternalDocNo(VendorLedgerEntry, VendorCreditMemoNo); + if StopCreatingPurchaseCreditMemo then begin + Telemetry.LogMessage('0000PHD', CreditMemoAlreadyExistsErr, Verbosity::Error, DataClassification::OrganizationIdentifiableInformation, TelemetryScope::All); + Error(CreditMemoAlreadyExistsErr, VendorCreditMemoNo, EDocumentPurchaseHeader."[BC] Vendor No."); + end; + + PurchaseHeader.Validate("Vendor Cr. Memo No.", VendorCreditMemoNo); + PurchaseHeader.Validate("Vendor Order No.", EDocumentPurchaseHeader."Purchase Order No."); + PurchaseHeader.Insert(true); + + if EDocumentPurchaseHeader."Document Date" <> 0D then + PurchaseHeader.Validate("Document Date", EDocumentPurchaseHeader."Document Date"); + if EDocumentPurchaseHeader."Due Date" <> 0D then + PurchaseHeader.Validate("Due Date", EDocumentPurchaseHeader."Due Date"); + PurchaseHeader.Modify(); + + // Validate of currency has to happen after insert. + GLSetup.GetRecordOnce(); + if EDocumentPurchaseHeader."Currency Code" <> GLSetup.GetCurrencyCode('') then begin + PurchaseHeader.Validate("Currency Code", EDocumentPurchaseHeader."Currency Code"); + PurchaseHeader.Modify(); + end; + + // Track changes for history + EDocumentPurchaseHistMapping.TrackRecord(EDocument, EDocumentPurchaseHeader, PurchaseHeader); + + EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocument."Entry No"); + if EDocumentPurchaseLine.FindSet() then + repeat + PurchaseLine."Document Type" := PurchaseHeader."Document Type"; + PurchaseLine."Document No." := PurchaseHeader."No."; + PurchaseLine."Line No." += 10000; + PurchaseLine."Unit of Measure Code" := CopyStr(EDocumentPurchaseLine."[BC] Unit of Measure", 1, MaxStrLen(PurchaseLine."Unit of Measure Code")); + PurchaseLine."Variant Code" := EDocumentPurchaseLine."[BC] Variant Code"; + PurchaseLine.Type := EDocumentPurchaseLine."[BC] Purchase Line Type"; + PurchaseLine.Validate("No.", EDocumentPurchaseLine."[BC] Purchase Type No."); + PurchaseLine.Description := EDocumentPurchaseLine.Description; + + if EDocumentPurchaseLine."[BC] Item Reference No." <> '' then + PurchaseLine.Validate("Item Reference No.", EDocumentPurchaseLine."[BC] Item Reference No."); + + PurchaseLine.Validate(Quantity, EDocumentPurchaseLine.Quantity); + PurchaseLine.Validate("Direct Unit Cost", EDocumentPurchaseLine."Unit Price"); + if EDocumentPurchaseLine."Total Discount" > 0 then + PurchaseLine.Validate("Line Discount Amount", EDocumentPurchaseLine."Total Discount"); + PurchaseLine.Validate("Deferral Code", EDocumentPurchaseLine."[BC] Deferral Code"); + + Clear(PurchaseLineCombinedDimensions); + PurchaseLineCombinedDimensions[1] := PurchaseLine."Dimension Set ID"; + PurchaseLineCombinedDimensions[2] := EDocumentPurchaseLine."[BC] Dimension Set ID"; + PurchaseLine.Validate("Dimension Set ID", DimensionManagement.GetCombinedDimensionSetID(PurchaseLineCombinedDimensions, GlobalDim1, GlobalDim2)); + PurchaseLine.Validate("Shortcut Dimension 1 Code", EDocumentPurchaseLine."[BC] Shortcut Dimension 1 Code"); + PurchaseLine.Validate("Shortcut Dimension 2 Code", EDocumentPurchaseLine."[BC] Shortcut Dimension 2 Code"); + EDocumentPurchaseHistMapping.ApplyHistoryValuesToPurchaseLine(EDocumentPurchaseLine, PurchaseLine); + PurchaseLine.Insert(); + + // Track changes for history + EDocumentPurchaseHistMapping.TrackRecord(EDocument, EDocumentPurchaseLine, PurchaseLine); + + until EDocumentPurchaseLine.Next() = 0; + + exit(PurchaseHeader); + + end; + + [TryFunction] + local procedure TryValidateDocumentTotals(PurchaseHeader: Record "Purchase Header") + var + PurchPost: Codeunit "Purch.-Post"; + begin + // If document totals are setup, we just run the validation + PurchPost.CheckDocumentTotalAmounts(PurchaseHeader); + end; + + local procedure AllDraftLinesHaveTypeAndNumberSpecificed(EDocumentPurchaseHeader: Record "E-Document Purchase Header"): Boolean + var + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + begin + EDocumentPurchaseLine.SetLoadFields("[BC] Purchase Line Type", "[BC] Purchase Type No."); + EDocumentPurchaseLine.ReadIsolation(IsolationLevel::ReadCommitted); + EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocumentPurchaseHeader."E-Document Entry No."); + if not EDocumentPurchaseLine.FindSet() then + exit(true); + repeat + if EDocumentPurchaseLine."[BC] Purchase Line Type" = EDocumentPurchaseLine."[BC] Purchase Line Type"::" " then + exit(false); + if EDocumentPurchaseLine."[BC] Purchase Type No." = '' then + exit(false); + until EDocumentPurchaseLine.Next() = 0; + exit(true); + end; +} diff --git a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Enum.al b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Enum.al new file mode 100644 index 0000000000..09052e3ec5 --- /dev/null +++ b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Enum.al @@ -0,0 +1,17 @@ +// ------------------------------------------------------------------------------------------------ +// 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.Processing.Import; + +using Microsoft.eServices.EDocument.Processing.Interfaces; + +/// +/// Enum for the implementations of the E-Doc. Create Purchase Credit Memo interface. +/// +enum 6118 "E-Doc. Create Purch. Cr. Memo" implements IEDocumentCreatePurchaseCreditMemo +{ + Extensible = true; + DefaultImplementation = IEDocumentCreatePurchaseCreditMemo = "E-Doc. Create Purch. Cr. Memo"; + value(0; "Default") { } +} diff --git a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al index 41ef481260..90784ff741 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al @@ -93,9 +93,8 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft, Error(DraftLineDoesNotConstantTypeAndNumberErr); end; EDocumentPurchaseHeader.TestField("E-Document Entry No."); - PurchaseHeader.SetRange("Buy-from Vendor No.", EDocumentPurchaseHeader."[BC] Vendor No."); // Setting the filter, so that the insert trigger assigns the right vendor to the purchase header - PurchaseHeader."Document Type" := "Purchase Document Type"::Invoice; - PurchaseHeader."Pay-to Vendor No." := EDocumentPurchaseHeader."[BC] Vendor No."; + PurchaseHeader.Validate("Document Type", "Purchase Document Type"::Invoice); + PurchaseHeader.Validate("Buy-from Vendor No.", EDocumentPurchaseHeader."[BC] Vendor No."); VendorInvoiceNo := CopyStr(EDocumentPurchaseHeader."Sales Invoice No.", 1, MaxStrLen(PurchaseHeader."Vendor Invoice No.")); VendorLedgerEntry.SetLoadFields("Entry No."); @@ -107,6 +106,7 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft, end; PurchaseHeader.Validate("Vendor Invoice No.", VendorInvoiceNo); + PurchaseHeader.Validate("Vendor Order No.", EDocumentPurchaseHeader."Purchase Order No."); PurchaseHeader.Insert(true); if EDocumentPurchaseHeader."Document Date" <> 0D then @@ -191,5 +191,4 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft, until EDocumentPurchaseLine.Next() = 0; exit(true); end; - } \ No newline at end of file diff --git a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Enum.al b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Enum.al index 2caa2656e3..63f1899d51 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Enum.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Enum.al @@ -13,7 +13,5 @@ enum 6105 "E-Doc. Create Purchase Invoice" implements IEDocumentCreatePurchaseIn { Extensible = true; DefaultImplementation = IEDocumentCreatePurchaseInvoice = "E-Doc. Create Purchase Invoice"; - value(0; "Default") - { - } + value(0; "Default") { } } \ No newline at end of file diff --git a/Apps/W1/EDocument/app/src/Processing/Import/ImportEDocumentProcess.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/ImportEDocumentProcess.Codeunit.al index 7d357c47b3..c1c662a5bc 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/ImportEDocumentProcess.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/ImportEDocumentProcess.Codeunit.al @@ -174,6 +174,7 @@ codeunit 6104 "Import E-Document Process" local procedure UndoProcessingStep(EDocument: Record "E-Document"; Step: Enum "Import E-Document Steps") var EDocumentHeaderMapping: Record "E-Document Header Mapping"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; IEDocumentFinishDraft: Interface IEDocumentFinishDraft; begin case Step of @@ -184,6 +185,11 @@ codeunit 6104 "Import E-Document Process" Clear(EDocument."Document Record ID"); EDocument.Modify(); end; + Step::"Read into Draft": + begin + EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocument."Entry No"); + EDocumentPurchaseLine.DeleteAll(false); + end; Step::"Prepare draft": begin EDocumentHeaderMapping.SetRange("E-Document Entry No.", EDocument."Entry No"); diff --git a/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/EDocProcCustomizations.Enum.al b/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/EDocProcCustomizations.Enum.al index e533729eed..a2ccea3c23 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/EDocProcCustomizations.Enum.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/EDocProcCustomizations.Enum.al @@ -11,16 +11,16 @@ enum 6110 "E-Doc. Proc. Customizations" implements IPurchaseOrderProvider, IPurchaseLineProvider, IUnitOfMeasureProvider, - IEDocumentCreatePurchaseInvoice + IEDocumentCreatePurchaseInvoice, + IEDocumentCreatePurchaseCreditMemo { Extensible = true; DefaultImplementation = IVendorProvider = "E-Doc. Providers", IPurchaseOrderProvider = "E-Doc. Providers", IPurchaseLineProvider = "E-Doc. Providers", IUnitOfMeasureProvider = "E-Doc. Providers", - IEDocumentCreatePurchaseInvoice = "E-Doc. Create Purchase Invoice"; + IEDocumentCreatePurchaseInvoice = "E-Doc. Create Purchase Invoice", + IEDocumentCreatePurchaseCreditMemo = "E-Doc. Create Purch. Cr. Memo"; - value(0; Default) - { - } + value(0; Default) { } } \ No newline at end of file diff --git a/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/EDocProviders.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/EDocProviders.Codeunit.al index df5688ee00..a4881d7859 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/EDocProviders.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/EDocProviders.Codeunit.al @@ -12,6 +12,7 @@ using Microsoft.Bank.Reconciliation; using Microsoft.eServices.EDocument.Service.Participant; using Microsoft.Inventory.Item; using Microsoft.Purchases.Document; +using Microsoft.Projects.Resources.Resource; using Microsoft.eServices.EDocument.Processing.Interfaces; using Microsoft.eServices.EDocument.Processing.Import.Purchase; @@ -110,8 +111,19 @@ codeunit 6124 "E-Doc. Providers" implements IPurchaseLineProvider, IUnitOfMeasur end; procedure GetPurchaseOrder(EDocumentPurchaseHeader: Record "E-Document Purchase Header") PurchaseHeader: Record "Purchase Header" + var + Vendor: Record Vendor; + VendorReceiveEDocumentErr: Label 'The %1 = %2 has an invalid %3 value: %4. Please correct the vendor setup.', Comment = '%1 = Vendor Table Caption, %2 = Vendor No., %3 = Receive E-Document To Field Caption, %4 = Receive E-Document To Value'; begin - if PurchaseHeader.Get("Purchase Document Type"::Order, EDocumentPurchaseHeader."Purchase Order No.") then; + Vendor.Get(EDocumentPurchaseHeader."[BC] Vendor No."); + case Vendor."Receive E-Document To" of + Vendor."Receive E-Document To"::"Purchase Invoice": + if PurchaseHeader.Get("Purchase Document Type"::Invoice, EDocumentPurchaseHeader."Purchase Order No.") then; + Vendor."Receive E-Document To"::"Purchase Order": + if PurchaseHeader.Get("Purchase Document Type"::Order, EDocumentPurchaseHeader."Purchase Order No.") then; + else + Error(VendorReceiveEDocumentErr, Vendor.TableCaption(), Vendor."No.", Vendor.FieldCaption("Receive E-Document To"), Format(Vendor."Receive E-Document To")); + end; end; local procedure GetPurchaseLineItemRef(EDocumentPurchaseLine: Record "E-Document Purchase Line"; var ItemReference: Record "Item Reference"): Boolean @@ -149,4 +161,25 @@ codeunit 6124 "E-Doc. Providers" implements IPurchaseLineProvider, IUnitOfMeasur exit(true); end; + procedure GetDefaultUnitOfMeasure(PurchaseLineType: Enum "Purchase Line Type"; PurchaseLineTypeNo: Code[20]) UnitOfMeasureCode: Code[20]; + var + Item: Record Item; + Resource: Record Resource; + begin + case PurchaseLineType of + "Purchase Line Type"::Item: + begin + if Item.Get(PurchaseLineTypeNo) then; + if Item."Purch. Unit of Measure" <> '' then + UnitOfMeasureCode := Item."Purch. Unit of Measure" + else + UnitOfMeasureCode := Item."Base Unit of Measure"; + end; + "Purchase Line Type"::Resource: + begin + if Resource.Get(PurchaseLineTypeNo) then; + UnitOfMeasureCode := Resource."Base Unit of Measure"; + end; + end; + end; } \ No newline at end of file diff --git a/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al index c4478258c1..4d62fef6a4 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al @@ -88,7 +88,7 @@ codeunit 6125 "Prepare Purchase E-Doc. Draft" implements IProcessStructuredData if EDocumentPurchaseHeader."[BC] Vendor No." <> '' then CopilotLineMatching(EDocument."Entry No"); - exit("E-Document Type"::"Purchase Invoice"); + exit(EDocumentPurchaseHeader."E-Document Type"); end; local procedure CopilotLineMatching(EDocumentEntryNo: Integer) diff --git a/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al b/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al index 93bb50f1c9..72e6f77353 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al @@ -207,6 +207,11 @@ table 6100 "E-Document Purchase Header" Caption = 'Vendor Contact Name'; DataClassification = CustomerContent; } + field(38; "E-Document Type"; Enum "E-Document Type") + { + Caption = 'E-Document Type'; + DataClassification = CustomerContent; + } #endregion Purchase fields #region Business Central Data - Validated fields [101-200] diff --git a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al index 1479acb301..5542f41b6e 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al @@ -5,11 +5,11 @@ namespace Microsoft.eServices.EDocument.Format; using Microsoft.eServices.EDocument; +using Microsoft.eServices.EDocument.Helpers; using Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument.Processing.Interfaces; using System.Utilities; -using Microsoft.Finance.GeneralLedger.Setup; codeunit 6173 "E-Document PEPPOL Handler" implements IStructuredFormatReader { @@ -30,7 +30,6 @@ codeunit 6173 "E-Document PEPPOL Handler" implements IStructuredFormatReader QualifiedDatatypesLbl: Label 'urn:oasis:names:specification:ubl:schema:xsd:QualifiedDatatypes-2'; UnqualifiedDataTypesSchemaModuleLbl: Label 'urn:un:unece:uncefact:data:specification:UnqualifiedDataTypesSchemaModule:2'; DefaultInvoiceLbl: Label 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'; - CreditNoteNotSupportedLbl: Label 'Credit notes are not supported'; begin EDocumentPurchaseHeader.InsertForEDocument(EDocument); @@ -47,11 +46,16 @@ codeunit 6173 "E-Document PEPPOL Handler" implements IStructuredFormatReader case UpperCase(XmlElement.LocalName()) of 'INVOICE': begin + EDocumentPurchaseHeader."E-Document Type" := "E-Document Type"::"Purchase Invoice"; PopulatePurchaseInvoiceHeader(PeppolXML, XmlNamespaces, EDocumentPurchaseHeader); InsertPurchaseInvoiceLines(PeppolXML, XmlNamespaces, EDocumentPurchaseHeader."E-Document Entry No."); end; 'CREDITNOTE': - Error(CreditNoteNotSupportedLbl); + begin + EDocumentPurchaseHeader."E-Document Type" := "E-Document Type"::"Purchase Credit Memo"; + PopulatePurchaseCreditMemoHeader(PeppolXML, XmlNamespaces, EDocumentPurchaseHeader); + InsertPurchaseCreditMemoLines(PeppolXML, XmlNamespaces, EDocumentPurchaseHeader."E-Document Entry No."); + end; end; EDocumentPurchaseHeader.Modify(); EDocument.Direction := EDocument.Direction::Incoming; @@ -81,126 +85,153 @@ codeunit 6173 "E-Document PEPPOL Handler" implements IStructuredFormatReader end; end; + local procedure InsertPurchaseCreditMemoLines(PeppolXML: XmlDocument; XmlNamespaces: XmlNamespaceManager; EDocumentEntryNo: Integer) + var + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + NewLineXML: XmlDocument; + LineXMLList: XmlNodeList; + LineXMLNode: XmlNode; + i: Integer; + CreditNoteLinePathLbl: Label '/cn:CreditNote/cac:CreditNoteLine'; + CreditNoteNamespaceLbl: Label 'urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2'; + begin + // Add CreditNote namespace for proper line parsing + XmlNamespaces.AddNamespace('cn', CreditNoteNamespaceLbl); + + if not PeppolXML.SelectNodes(CreditNoteLinePathLbl, XmlNamespaces, LineXMLList) then + exit; + + for i := 1 to LineXMLList.Count do begin + Clear(EDocumentPurchaseLine); + EDocumentPurchaseLine.Validate("E-Document Entry No.", EDocumentEntryNo); + EDocumentPurchaseLine."Line No." := EDocumentPurchaseLine.GetNextLineNo(EDocumentEntryNo); + LineXMLList.Get(i, LineXMLNode); + NewLineXML.ReplaceNodes(LineXMLNode); + PopulateEDocumentPurchaseCreditMemoLine(NewLineXML, XmlNamespaces, EDocumentPurchaseLine); + EDocumentPurchaseLine.Insert(); + end; + end; + #pragma warning disable AA0139 // false positive: overflow handled by SetStringValueInField local procedure PopulatePurchaseInvoiceHeader(PeppolXML: XmlDocument; XmlNamespaces: XmlNamespaceManager; var EDocumentPurchaseHeader: Record "E-Document Purchase Header") var + EDocumentXMLHelper: Codeunit "EDocument XML Helper"; XMLNode: XmlNode; begin - SetStringValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cbc:ID', MaxStrLen(EDocumentPurchaseHeader."Sales Invoice No."), EDocumentPurchaseHeader."Sales Invoice No."); - SetStringValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:OrderReference/cbc:ID', MaxStrLen(EDocumentPurchaseHeader."Purchase Order No."), EDocumentPurchaseHeader."Purchase Order No."); - SetStringValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name', MaxStrLen(EDocumentPurchaseHeader."Vendor Company Name"), EDocumentPurchaseHeader."Vendor Company Name"); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cbc:ID', MaxStrLen(EDocumentPurchaseHeader."Sales Invoice No."), EDocumentPurchaseHeader."Sales Invoice No."); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:OrderReference/cbc:ID', MaxStrLen(EDocumentPurchaseHeader."Purchase Order No."), EDocumentPurchaseHeader."Purchase Order No."); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name', MaxStrLen(EDocumentPurchaseHeader."Vendor Company Name"), EDocumentPurchaseHeader."Vendor Company Name"); // Line below, using PayeeParty, shall be used when the Payee is different from the Seller. Otherwise, it will not be shown in the XML. - SetStringValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:PayeeParty/cac:PartyName/cbc:Name', MaxStrLen(EDocumentPurchaseHeader."Vendor Company Name"), EDocumentPurchaseHeader."Vendor Company Name"); - SetNumberValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:LegalMonetaryTotal/cbc:AllowanceTotalAmount', EDocumentPurchaseHeader."Total Discount"); - SetNumberValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:LegalMonetaryTotal/cbc:PayableAmount', EDocumentPurchaseHeader."Amount Due"); - SetNumberValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:LegalMonetaryTotal/cbc:PayableAmount', EDocumentPurchaseHeader.Total); - SetStringValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:Name', MaxStrLen(EDocumentPurchaseHeader."Vendor Contact Name"), EDocumentPurchaseHeader."Vendor Contact Name"); - SetStringValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:StreetName', MaxStrLen(EDocumentPurchaseHeader."Vendor Address"), EDocumentPurchaseHeader."Vendor Address"); - SetStringValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyID', MaxStrLen(EDocumentPurchaseHeader."Customer VAT Id"), EDocumentPurchaseHeader."Customer VAT Id"); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:PayeeParty/cac:PartyName/cbc:Name', MaxStrLen(EDocumentPurchaseHeader."Vendor Company Name"), EDocumentPurchaseHeader."Vendor Company Name"); + EDocumentXMLHelper.SetNumberValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:LegalMonetaryTotal/cbc:AllowanceTotalAmount', EDocumentPurchaseHeader."Total Discount"); + EDocumentXMLHelper.SetNumberValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:LegalMonetaryTotal/cbc:PayableAmount', EDocumentPurchaseHeader."Amount Due"); + EDocumentXMLHelper.SetNumberValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:LegalMonetaryTotal/cbc:PayableAmount', EDocumentPurchaseHeader.Total); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:Name', MaxStrLen(EDocumentPurchaseHeader."Vendor Contact Name"), EDocumentPurchaseHeader."Vendor Contact Name"); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:StreetName', MaxStrLen(EDocumentPurchaseHeader."Vendor Address"), EDocumentPurchaseHeader."Vendor Address"); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyID', MaxStrLen(EDocumentPurchaseHeader."Customer VAT Id"), EDocumentPurchaseHeader."Customer VAT Id"); // Line below, using PayeeParty, shall be used when the Payee is different from the Seller. Otherwise, it will not be shown in the XML. - SetStringValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:PayeeParty/cac:PartyLegalEntity/cbc:CompanyID', MaxStrLen(EDocumentPurchaseHeader."Vendor VAT Id"), EDocumentPurchaseHeader."Vendor VAT Id"); - SetNumberValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount', EDocumentPurchaseHeader."Sub Total"); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:PayeeParty/cac:PartyLegalEntity/cbc:CompanyID', MaxStrLen(EDocumentPurchaseHeader."Vendor VAT Id"), EDocumentPurchaseHeader."Vendor VAT Id"); + EDocumentXMLHelper.SetNumberValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount', EDocumentPurchaseHeader."Sub Total"); EDocumentPurchaseHeader."Total VAT" := EDocumentPurchaseHeader."Total" - EDocumentPurchaseHeader."Sub Total" - EDocumentPurchaseHeader."Total Discount"; - SetDateValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cbc:DueDate', EDocumentPurchaseHeader."Due Date"); - SetDateValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cbc:IssueDate', EDocumentPurchaseHeader."Document Date"); - SetStringValueInField(PeppolXML, XmlNamespaces, '/inv:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID', MaxStrLen(EDocumentPurchaseHeader."Vendor VAT Id"), EDocumentPurchaseHeader."Vendor VAT Id"); - SetCurrencyValueInField(PeppolXML, XmlNamespaces, '/inv:Invoice/cbc:DocumentCurrencyCode', MaxStrLen(EDocumentPurchaseHeader."Currency Code"), EDocumentPurchaseHeader."Currency Code"); - SetStringValueInField(PeppolXML, XmlNamespaces, '/inv:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name', MaxStrLen(EDocumentPurchaseHeader."Customer Company Name"), EDocumentPurchaseHeader."Customer Company Name"); - SetStringValueInField(PeppolXML, XmlNamespaces, '/inv:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID', MaxStrLen(EDocumentPurchaseHeader."Customer VAT Id"), EDocumentPurchaseHeader."Customer VAT Id"); - SetStringValueInField(PeppolXML, XmlNamespaces, '/inv:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PostalAddress/cbc:StreetName', MaxStrLen(EDocumentPurchaseHeader."Customer Address"), EDocumentPurchaseHeader."Customer Address"); - SetStringValueInField(PeppolXML, XmlNamespaces, '/inv:Invoice/cac:AccountingCustomerParty/cac:Party/cbc:EndpointID', MaxStrLen(EDocumentPurchaseHeader."Customer GLN"), EDocumentPurchaseHeader."Customer GLN"); - SetStringValueInField(PeppolXML, XmlNamespaces, '/inv:Invoice/cac:AccountingCustomerParty/cac:Party/cbc:EndpointID/@schemeID', MaxStrLen(EDocumentPurchaseHeader."Customer Company Id"), EDocumentPurchaseHeader."Customer Company Id"); + EDocumentXMLHelper.SetDateValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cbc:DueDate', EDocumentPurchaseHeader."Due Date"); + EDocumentXMLHelper.SetDateValueInField(PeppolXML, XMLNamespaces, '/inv:Invoice/cbc:IssueDate', EDocumentPurchaseHeader."Document Date"); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XmlNamespaces, '/inv:Invoice/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID', MaxStrLen(EDocumentPurchaseHeader."Vendor VAT Id"), EDocumentPurchaseHeader."Vendor VAT Id"); + EDocumentXMLHelper.SetCurrencyValueInField(PeppolXML, XmlNamespaces, '/inv:Invoice/cbc:DocumentCurrencyCode', MaxStrLen(EDocumentPurchaseHeader."Currency Code"), EDocumentPurchaseHeader."Currency Code"); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XmlNamespaces, '/inv:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name', MaxStrLen(EDocumentPurchaseHeader."Customer Company Name"), EDocumentPurchaseHeader."Customer Company Name"); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XmlNamespaces, '/inv:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID', MaxStrLen(EDocumentPurchaseHeader."Customer VAT Id"), EDocumentPurchaseHeader."Customer VAT Id"); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XmlNamespaces, '/inv:Invoice/cac:AccountingCustomerParty/cac:Party/cac:PostalAddress/cbc:StreetName', MaxStrLen(EDocumentPurchaseHeader."Customer Address"), EDocumentPurchaseHeader."Customer Address"); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XmlNamespaces, '/inv:Invoice/cac:AccountingCustomerParty/cac:Party/cbc:EndpointID', MaxStrLen(EDocumentPurchaseHeader."Customer GLN"), EDocumentPurchaseHeader."Customer GLN"); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XmlNamespaces, '/inv:Invoice/cac:AccountingCustomerParty/cac:Party/cbc:EndpointID/@schemeID', MaxStrLen(EDocumentPurchaseHeader."Customer Company Id"), EDocumentPurchaseHeader."Customer Company Id"); if PeppolXML.SelectSingleNode('/inv:Invoice/cac:AccountingSupplierParty/cac:Party/cbc:EndpointID/@schemeID', XmlNamespaces, XMLNode) then if XMLNode.AsXmlAttribute().Value() = '0088' then // GLN - SetStringValueInField(PeppolXML, XmlNamespaces, '/inv:Invoice/cac:AccountingSupplierParty/cac:Party/cbc:EndpointID', MaxStrLen(EDocumentPurchaseHeader."Vendor GLN"), EDocumentPurchaseHeader."Vendor GLN"); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XmlNamespaces, '/inv:Invoice/cac:AccountingSupplierParty/cac:Party/cbc:EndpointID', MaxStrLen(EDocumentPurchaseHeader."Vendor GLN"), EDocumentPurchaseHeader."Vendor GLN"); end; - local procedure PopulateEDocumentPurchaseLine(LineXML: XmlDocument; XmlNamespaces: XmlNamespaceManager; var EDocumentPurchaseLine: Record "E-Document Purchase Line") - begin - SetNumberValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cbc:InvoicedQuantity', EDocumentPurchaseLine.Quantity); - SetStringValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cbc:InvoicedQuantity/@unitCode', MaxStrLen(EDocumentPurchaseLine."Unit of Measure"), EDocumentPurchaseLine."Unit of Measure"); - SetNumberValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cbc:LineExtensionAmount', EDocumentPurchaseLine."Sub Total"); - SetCurrencyValueInField(LineXML, XmlNamespaces, 'cac:InvoiceLine/cbc:LineExtensionAmount/@currencyID', MaxStrLen(EDocumentPurchaseLine."Currency Code"), EDocumentPurchaseLine."Currency Code"); - SetNumberValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cac:AllowanceCharge/cbc:Amount', EDocumentPurchaseLine."Total Discount"); - SetStringValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cbc:Note', MaxStrLen(EDocumentPurchaseLine.Description), EDocumentPurchaseLine.Description); - SetStringValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cac:Item/cbc:Name', MaxStrLen(EDocumentPurchaseLine.Description), EDocumentPurchaseLine.Description); - SetStringValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cac:Item/cbc:Description', MaxStrLen(EDocumentPurchaseLine.Description), EDocumentPurchaseLine.Description); - SetStringValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cac:Item/cac:SellersItemIdentification/cbc:ID', MaxStrLen(EDocumentPurchaseLine."Product Code"), EDocumentPurchaseLine."Product Code"); - SetStringValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cac:Item/cac:StandardItemIdentification/cbc:ID', MaxStrLen(EDocumentPurchaseLine."Product Code"), EDocumentPurchaseLine."Product Code"); - SetNumberValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cac:Item/cac:ClassifiedTaxCategory/cbc:Percent', EDocumentPurchaseLine."VAT Rate"); - SetNumberValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cac:Price/cbc:PriceAmount', EDocumentPurchaseLine."Unit Price"); - end; - - local procedure SetCurrencyValueInField(XMLDocument: XmlDocument; XMLNamespaces: XmlNamespaceManager; Path: Text; MaxLength: Integer; var CurrencyField: Code[10]) + local procedure PopulatePurchaseCreditMemoHeader(PeppolXML: XmlDocument; XmlNamespaces: XmlNamespaceManager; var EDocumentPurchaseHeader: Record "E-Document Purchase Header") var - GLSetup: Record "General Ledger Setup"; + EDocumentXMLHelper: Codeunit "EDocument XML Helper"; XMLNode: XmlNode; - CurrencyCode: Code[10]; + CreditNoteNamespaceLbl: Label 'urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2'; begin - if not XMLDocument.SelectSingleNode(Path, XMLNamespaces, XMLNode) then - exit; - - GLSetup.Get(); + // Add CreditNote namespace for proper header parsing + XmlNamespaces.AddNamespace('cn', CreditNoteNamespaceLbl); - if XMLNode.IsXmlElement() then begin - CurrencyCode := CopyStr(XMLNode.AsXmlElement().InnerText(), 1, MaxLength); - if GLSetup."LCY Code" <> CurrencyCode then - CurrencyField := CurrencyCode; - exit; - end; - - if XMLNode.IsXmlAttribute() then begin - CurrencyCode := CopyStr(XMLNode.AsXmlAttribute().Value, 1, MaxLength); - if GLSetup."LCY Code" <> CurrencyCode then - CurrencyField := CurrencyCode; - exit; - end; - end; -#pragma warning restore AA0139 - - local procedure SetStringValueInField(XMLDocument: XmlDocument; XMLNamespaces: XmlNamespaceManager; Path: Text; MaxLength: Integer; var Field: Text) - var - XMLNode: XmlNode; - begin - if not XMLDocument.SelectSingleNode(Path, XMLNamespaces, XMLNode) then - exit; - - if XMLNode.IsXmlElement() then begin - Field := CopyStr(XMLNode.AsXmlElement().InnerText(), 1, MaxLength); - exit; - end; - - if XMLNode.IsXmlAttribute() then begin - Field := CopyStr(XMLNode.AsXmlAttribute().Value(), 1, MaxLength); - exit; - end; + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cbc:ID', MaxStrLen(EDocumentPurchaseHeader."Sales Invoice No."), EDocumentPurchaseHeader."Sales Invoice No."); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:BillingReference/cac:InvoiceDocumentReference/cbc:ID', MaxStrLen(EDocumentPurchaseHeader."Purchase Order No."), EDocumentPurchaseHeader."Purchase Order No."); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name', MaxStrLen(EDocumentPurchaseHeader."Vendor Company Name"), EDocumentPurchaseHeader."Vendor Company Name"); + // Line below, using PayeeParty, shall be used when the Payee is different from the Seller. Otherwise, it will not be shown in the XML. + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:PayeeParty/cac:PartyName/cbc:Name', MaxStrLen(EDocumentPurchaseHeader."Vendor Company Name"), EDocumentPurchaseHeader."Vendor Company Name"); + EDocumentXMLHelper.SetNumberValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:LegalMonetaryTotal/cbc:AllowanceTotalAmount', EDocumentPurchaseHeader."Total Discount"); + EDocumentXMLHelper.SetNumberValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:LegalMonetaryTotal/cbc:PayableAmount', EDocumentPurchaseHeader."Amount Due"); + EDocumentXMLHelper.SetNumberValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:LegalMonetaryTotal/cbc:PayableAmount', EDocumentPurchaseHeader.Total); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:Name', MaxStrLen(EDocumentPurchaseHeader."Vendor Contact Name"), EDocumentPurchaseHeader."Vendor Contact Name"); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:StreetName', MaxStrLen(EDocumentPurchaseHeader."Vendor Address"), EDocumentPurchaseHeader."Vendor Address"); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyID', MaxStrLen(EDocumentPurchaseHeader."Customer VAT Id"), EDocumentPurchaseHeader."Customer VAT Id"); + // Line below, using PayeeParty, shall be used when the Payee is different from the Seller. Otherwise, it will not be shown in the XML. + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:PayeeParty/cac:PartyLegalEntity/cbc:CompanyID', MaxStrLen(EDocumentPurchaseHeader."Vendor VAT Id"), EDocumentPurchaseHeader."Vendor VAT Id"); + EDocumentXMLHelper.SetNumberValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount', EDocumentPurchaseHeader."Sub Total"); + EDocumentPurchaseHeader."Total VAT" := EDocumentPurchaseHeader."Total" - EDocumentPurchaseHeader."Sub Total" - EDocumentPurchaseHeader."Total Discount"; + EDocumentXMLHelper.SetDateValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:PaymentMeans/cbc:PaymentDueDate', EDocumentPurchaseHeader."Due Date"); + EDocumentXMLHelper.SetDateValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cbc:IssueDate', EDocumentPurchaseHeader."Document Date"); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XmlNamespaces, '/cn:CreditNote/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID', MaxStrLen(EDocumentPurchaseHeader."Vendor VAT Id"), EDocumentPurchaseHeader."Vendor VAT Id"); + EDocumentXMLHelper.SetCurrencyValueInField(PeppolXML, XmlNamespaces, '/cn:CreditNote/cbc:DocumentCurrencyCode', MaxStrLen(EDocumentPurchaseHeader."Currency Code"), EDocumentPurchaseHeader."Currency Code"); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XmlNamespaces, '/cn:CreditNote/cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name', MaxStrLen(EDocumentPurchaseHeader."Customer Company Name"), EDocumentPurchaseHeader."Customer Company Name"); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XmlNamespaces, '/cn:CreditNote/cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID', MaxStrLen(EDocumentPurchaseHeader."Customer VAT Id"), EDocumentPurchaseHeader."Customer VAT Id"); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XmlNamespaces, '/cn:CreditNote/cac:AccountingCustomerParty/cac:Party/cac:PostalAddress/cbc:StreetName', MaxStrLen(EDocumentPurchaseHeader."Customer Address"), EDocumentPurchaseHeader."Customer Address"); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XmlNamespaces, '/cn:CreditNote/cac:AccountingCustomerParty/cac:Party/cbc:EndpointID', MaxStrLen(EDocumentPurchaseHeader."Customer GLN"), EDocumentPurchaseHeader."Customer GLN"); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XmlNamespaces, '/cn:CreditNote/cac:AccountingCustomerParty/cac:Party/cbc:EndpointID/@schemeID', MaxStrLen(EDocumentPurchaseHeader."Customer Company Id"), EDocumentPurchaseHeader."Customer Company Id"); + + if PeppolXML.SelectSingleNode('/cn:CreditNote/cac:AccountingSupplierParty/cac:Party/cbc:EndpointID/@schemeID', XmlNamespaces, XMLNode) then + if XMLNode.AsXmlAttribute().Value() = '0088' then // GLN + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XmlNamespaces, '/cn:CreditNote/cac:AccountingSupplierParty/cac:Party/cbc:EndpointID', MaxStrLen(EDocumentPurchaseHeader."Vendor GLN"), EDocumentPurchaseHeader."Vendor GLN"); end; - local procedure SetNumberValueInField(XMLDocument: XmlDocument; XMLNamespaces: XmlNamespaceManager; Path: Text; var DecimalValue: Decimal) + local procedure PopulateEDocumentPurchaseLine(LineXML: XmlDocument; XmlNamespaces: XmlNamespaceManager; var EDocumentPurchaseLine: Record "E-Document Purchase Line") var - XMLNode: XmlNode; + EDocumentXMLHelper: Codeunit "EDocument XML Helper"; begin - if not XMLDocument.SelectSingleNode(Path, XMLNamespaces, XMLNode) then - exit; - - if XMLNode.AsXmlElement().InnerText() <> '' then - Evaluate(DecimalValue, XMLNode.AsXmlElement().InnerText(), 9); + EDocumentXMLHelper.SetNumberValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cbc:InvoicedQuantity', EDocumentPurchaseLine.Quantity); + EDocumentXMLHelper.SetStringValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cbc:InvoicedQuantity/@unitCode', MaxStrLen(EDocumentPurchaseLine."Unit of Measure"), EDocumentPurchaseLine."Unit of Measure"); + EDocumentXMLHelper.SetNumberValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cbc:LineExtensionAmount', EDocumentPurchaseLine."Sub Total"); + EDocumentXMLHelper.SetCurrencyValueInField(LineXML, XmlNamespaces, 'cac:InvoiceLine/cbc:LineExtensionAmount/@currencyID', MaxStrLen(EDocumentPurchaseLine."Currency Code"), EDocumentPurchaseLine."Currency Code"); + EDocumentXMLHelper.SetNumberValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cac:AllowanceCharge/cbc:Amount', EDocumentPurchaseLine."Total Discount"); + EDocumentXMLHelper.SetStringValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cbc:Note', MaxStrLen(EDocumentPurchaseLine.Description), EDocumentPurchaseLine.Description); + EDocumentXMLHelper.SetStringValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cac:Item/cbc:Name', MaxStrLen(EDocumentPurchaseLine.Description), EDocumentPurchaseLine.Description); + EDocumentXMLHelper.SetStringValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cac:Item/cbc:Description', MaxStrLen(EDocumentPurchaseLine.Description), EDocumentPurchaseLine.Description); + EDocumentXMLHelper.SetStringValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cac:Item/cac:SellersItemIdentification/cbc:ID', MaxStrLen(EDocumentPurchaseLine."Product Code"), EDocumentPurchaseLine."Product Code"); + EDocumentXMLHelper.SetStringValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cac:Item/cac:StandardItemIdentification/cbc:ID', MaxStrLen(EDocumentPurchaseLine."Product Code"), EDocumentPurchaseLine."Product Code"); + EDocumentXMLHelper.SetNumberValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cac:Item/cac:ClassifiedTaxCategory/cbc:Percent', EDocumentPurchaseLine."VAT Rate"); + EDocumentXMLHelper.SetNumberValueInField(LineXML, XMLNamespaces, 'cac:InvoiceLine/cac:Price/cbc:PriceAmount', EDocumentPurchaseLine."Unit Price"); end; - local procedure SetDateValueInField(XMLDocument: XmlDocument; XMLNamespaces: XmlNamespaceManager; Path: Text; var DateValue: Date) + local procedure PopulateEDocumentPurchaseCreditMemoLine(LineXML: XmlDocument; XmlNamespaces: XmlNamespaceManager; var EDocumentPurchaseLine: Record "E-Document Purchase Line") var - XMLNode: XmlNode; + EDocumentXMLHelper: Codeunit "EDocument XML Helper"; begin - if not XMLDocument.SelectSingleNode(Path, XMLNamespaces, XMLNode) then - exit; - - if XMLNode.AsXmlElement().InnerText() <> '' then - Evaluate(DateValue, XMLNode.AsXmlElement().InnerText(), 9); + EDocumentXMLHelper.SetNumberValueInField(LineXML, XMLNamespaces, 'cac:CreditNoteLine/cbc:CreditedQuantity', EDocumentPurchaseLine.Quantity); + EDocumentXMLHelper.SetStringValueInField(LineXML, XMLNamespaces, 'cac:CreditNoteLine/cbc:CreditedQuantity/@unitCode', MaxStrLen(EDocumentPurchaseLine."Unit of Measure"), EDocumentPurchaseLine."Unit of Measure"); + EDocumentXMLHelper.SetNumberValueInField(LineXML, XMLNamespaces, 'cac:CreditNoteLine/cbc:LineExtensionAmount', EDocumentPurchaseLine."Sub Total"); + EDocumentXMLHelper.SetCurrencyValueInField(LineXML, XmlNamespaces, 'cac:CreditNoteLine/cbc:LineExtensionAmount/@currencyID', MaxStrLen(EDocumentPurchaseLine."Currency Code"), EDocumentPurchaseLine."Currency Code"); + EDocumentXMLHelper.SetNumberValueInField(LineXML, XMLNamespaces, 'cac:CreditNoteLine/cac:AllowanceCharge/cbc:Amount', EDocumentPurchaseLine."Total Discount"); + EDocumentXMLHelper.SetStringValueInField(LineXML, XMLNamespaces, 'cac:CreditNoteLine/cbc:Note', MaxStrLen(EDocumentPurchaseLine.Description), EDocumentPurchaseLine.Description); + EDocumentXMLHelper.SetStringValueInField(LineXML, XMLNamespaces, 'cac:CreditNoteLine/cac:Item/cbc:Name', MaxStrLen(EDocumentPurchaseLine.Description), EDocumentPurchaseLine.Description); + EDocumentXMLHelper.SetStringValueInField(LineXML, XMLNamespaces, 'cac:CreditNoteLine/cac:Item/cbc:Description', MaxStrLen(EDocumentPurchaseLine.Description), EDocumentPurchaseLine.Description); + EDocumentXMLHelper.SetStringValueInField(LineXML, XMLNamespaces, 'cac:CreditNoteLine/cac:Item/cac:SellersItemIdentification/cbc:ID', MaxStrLen(EDocumentPurchaseLine."Product Code"), EDocumentPurchaseLine."Product Code"); + EDocumentXMLHelper.SetStringValueInField(LineXML, XMLNamespaces, 'cac:CreditNoteLine/cac:Item/cac:StandardItemIdentification/cbc:ID', MaxStrLen(EDocumentPurchaseLine."Product Code"), EDocumentPurchaseLine."Product Code"); + EDocumentXMLHelper.SetNumberValueInField(LineXML, XMLNamespaces, 'cac:CreditNoteLine/cac:Item/cac:ClassifiedTaxCategory/cbc:Percent', EDocumentPurchaseLine."VAT Rate"); + EDocumentXMLHelper.SetNumberValueInField(LineXML, XMLNamespaces, 'cac:CreditNoteLine/cac:Price/cbc:PriceAmount', EDocumentPurchaseLine."Unit Price"); end; procedure View(EDocument: Record "E-Document"; TempBlob: Codeunit "Temp Blob") + var + EDocPurchaseHeader: Record "E-Document Purchase Header"; + EDocPurchaseLine: Record "E-Document Purchase Line"; + EDocReadablePurchaseDoc: Page "E-Doc. Readable Purchase Doc."; begin - Error('A view is not implemented for this handler.'); + EDocPurchaseHeader.GetFromEDocument(EDocument); + EDocPurchaseLine.SetRange("E-Document Entry No.", EDocPurchaseHeader."E-Document Entry No."); + EDocReadablePurchaseDoc.SetBuffer(EDocPurchaseHeader, EDocPurchaseLine); + EDocReadablePurchaseDoc.Run(); end; } \ No newline at end of file diff --git a/Apps/W1/EDocument/app/src/Processing/Interfaces/IEDocumentCreatePurchaseCreditMemo.Interface.al b/Apps/W1/EDocument/app/src/Processing/Interfaces/IEDocumentCreatePurchaseCreditMemo.Interface.al new file mode 100644 index 0000000000..dc115f1b15 --- /dev/null +++ b/Apps/W1/EDocument/app/src/Processing/Interfaces/IEDocumentCreatePurchaseCreditMemo.Interface.al @@ -0,0 +1,21 @@ +// ------------------------------------------------------------------------------------------------ +// 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.Processing.Interfaces; + +using Microsoft.eServices.EDocument; +using Microsoft.Purchases.Document; + +/// +/// Interface for changing the way that purchase credit memos get created from an E-Document. +/// +interface IEDocumentCreatePurchaseCreditMemo +{ + /// + /// Creates a purchase credit memo from an E-Document with a draft ready. + /// + /// + /// + procedure CreatePurchaseCreditMemo(EDocument: Record "E-Document"): Record "Purchase Header"; +} diff --git a/Apps/W1/EDocument/app/src/Processing/Interfaces/IUnitOfMeasureProvider.Interface.al b/Apps/W1/EDocument/app/src/Processing/Interfaces/IUnitOfMeasureProvider.Interface.al index 7f99c09200..10ec2a113c 100644 --- a/Apps/W1/EDocument/app/src/Processing/Interfaces/IUnitOfMeasureProvider.Interface.al +++ b/Apps/W1/EDocument/app/src/Processing/Interfaces/IUnitOfMeasureProvider.Interface.al @@ -5,6 +5,7 @@ namespace Microsoft.eServices.EDocument.Processing.Interfaces; using Microsoft.eServices.EDocument; +using Microsoft.Purchases.Document; using Microsoft.Foundation.UOM; /// @@ -20,4 +21,12 @@ interface IUnitOfMeasureProvider /// The external unit of measure reference. /// A Unit of Measure record corresponding to the provided details. procedure GetUnitOfMeasure(EDocument: Record "E-Document"; EDocumentLineId: Integer; ExternalUnitOfMeasure: Text): Record "Unit of Measure"; + + /// + /// Retrieves the default unit of measure for a given purchase line type and number. + /// + /// The type of the purchase line. + /// The number associated with the purchase line type. + /// The default unit of measure code for the specified purchase line type and number. + procedure GetDefaultUnitOfMeasure(PurchaseLineType: Enum "Purchase Line Type"; PurchaseLineTypeNo: Code[20]) UnitOfMeasureCode: Code[20]; } \ No newline at end of file From 6207947d4649f53b737abd60c2228656515def61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Wed, 30 Jul 2025 11:36:27 +0300 Subject: [PATCH 02/28] refactor: Simplify Purchase Credit Memo creation and enhance XML processing --- .../EDocCreatePurchCrMemo.Codeunit.al | 183 ++++++++++-------- .../PrepareDraft/EDocProviders.Codeunit.al | 15 +- .../EDocumentPEPPOLHandler.Codeunit.al | 5 +- 3 files changed, 109 insertions(+), 94 deletions(-) diff --git a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al index 28ac635f1d..71fbf4a486 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al @@ -34,24 +34,11 @@ codeunit 6105 "E-Doc. Create Purch. Cr. Memo" implements IEDocumentFinishDraft, var EDocumentPurchaseHeader: Record "E-Document Purchase Header"; PurchaseHeader: Record "Purchase Header"; - DocumentAttachmentMgt: Codeunit "Document Attachment Mgmt"; IEDocumentFinishPurchaseDraft: Interface IEDocumentCreatePurchaseCreditMemo; begin EDocumentPurchaseHeader.GetFromEDocument(EDocument); IEDocumentFinishPurchaseDraft := EDocImportParameters."Processing Customizations"; PurchaseHeader := IEDocumentFinishPurchaseDraft.CreatePurchaseCreditMemo(EDocument); - PurchaseHeader.SetRecFilter(); - PurchaseHeader.FindFirst(); - PurchaseHeader."Doc. Amount Incl. VAT" := EDocumentPurchaseHeader.Total; - PurchaseHeader."Doc. Amount VAT" := EDocumentPurchaseHeader."Total VAT"; - PurchaseHeader.TestField("Document Type", "Purchase Document Type"::"Credit Memo"); - PurchaseHeader.TestField("No."); - PurchaseHeader."E-Document Link" := EDocument.SystemId; - PurchaseHeader.Modify(); - - // Post document creation - DocumentAttachmentMgt.CopyAttachments(EDocument, PurchaseHeader); - DocumentAttachmentMgt.DeleteAttachedDocuments(EDocument); // Post document validation - Silently emit telemetry if not TryValidateDocumentTotals(PurchaseHeader) then @@ -75,17 +62,9 @@ codeunit 6105 "E-Doc. Create Purch. Cr. Memo" implements IEDocumentFinishDraft, procedure CreatePurchaseCreditMemo(EDocument: Record "E-Document"): Record "Purchase Header" var PurchaseHeader: Record "Purchase Header"; - GLSetup: Record "General Ledger Setup"; - VendorLedgerEntry: Record "Vendor Ledger Entry"; EDocumentPurchaseHeader: Record "E-Document Purchase Header"; EDocumentPurchaseLine: Record "E-Document Purchase Line"; - PurchaseLine: Record "Purchase Line"; EDocumentPurchaseHistMapping: Codeunit "E-Doc. Purchase Hist. Mapping"; - DimensionManagement: Codeunit DimensionManagement; - PurchaseLineCombinedDimensions: array[10] of Integer; - StopCreatingPurchaseCreditMemo: Boolean; - VendorCreditMemoNo: Code[35]; - GlobalDim1, GlobalDim2 : Code[20]; begin EDocumentPurchaseHeader.GetFromEDocument(EDocument); if not AllDraftLinesHaveTypeAndNumberSpecificed(EDocumentPurchaseHeader) then begin @@ -93,75 +72,14 @@ codeunit 6105 "E-Doc. Create Purch. Cr. Memo" implements IEDocumentFinishDraft, Error(DraftLineDoesNotConstantTypeAndNumberErr); end; EDocumentPurchaseHeader.TestField("E-Document Entry No."); - PurchaseHeader.Validate("Document Type", "Purchase Document Type"::"Credit Memo"); - PurchaseHeader.Validate("Buy-from Vendor No.", EDocumentPurchaseHeader."[BC] Vendor No."); - - VendorCreditMemoNo := CopyStr(EDocumentPurchaseHeader."Sales Invoice No.", 1, MaxStrLen(PurchaseHeader."Vendor Cr. Memo No.")); - VendorLedgerEntry.SetLoadFields("Entry No."); - VendorLedgerEntry.ReadIsolation := VendorLedgerEntry.ReadIsolation::ReadUncommitted; - StopCreatingPurchaseCreditMemo := PurchaseHeader.FindPostedDocumentWithSameExternalDocNo(VendorLedgerEntry, VendorCreditMemoNo); - if StopCreatingPurchaseCreditMemo then begin - Telemetry.LogMessage('0000PHD', CreditMemoAlreadyExistsErr, Verbosity::Error, DataClassification::OrganizationIdentifiableInformation, TelemetryScope::All); - Error(CreditMemoAlreadyExistsErr, VendorCreditMemoNo, EDocumentPurchaseHeader."[BC] Vendor No."); - end; - - PurchaseHeader.Validate("Vendor Cr. Memo No.", VendorCreditMemoNo); - PurchaseHeader.Validate("Vendor Order No.", EDocumentPurchaseHeader."Purchase Order No."); - PurchaseHeader.Insert(true); - - if EDocumentPurchaseHeader."Document Date" <> 0D then - PurchaseHeader.Validate("Document Date", EDocumentPurchaseHeader."Document Date"); - if EDocumentPurchaseHeader."Due Date" <> 0D then - PurchaseHeader.Validate("Due Date", EDocumentPurchaseHeader."Due Date"); - PurchaseHeader.Modify(); - - // Validate of currency has to happen after insert. - GLSetup.GetRecordOnce(); - if EDocumentPurchaseHeader."Currency Code" <> GLSetup.GetCurrencyCode('') then begin - PurchaseHeader.Validate("Currency Code", EDocumentPurchaseHeader."Currency Code"); - PurchaseHeader.Modify(); - end; - - // Track changes for history - EDocumentPurchaseHistMapping.TrackRecord(EDocument, EDocumentPurchaseHeader, PurchaseHeader); + CreatePurchaseHeader(EDocument, PurchaseHeader, EDocumentPurchaseHeader, EDocumentPurchaseHistMapping); EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocument."Entry No"); if EDocumentPurchaseLine.FindSet() then repeat - PurchaseLine."Document Type" := PurchaseHeader."Document Type"; - PurchaseLine."Document No." := PurchaseHeader."No."; - PurchaseLine."Line No." += 10000; - PurchaseLine."Unit of Measure Code" := CopyStr(EDocumentPurchaseLine."[BC] Unit of Measure", 1, MaxStrLen(PurchaseLine."Unit of Measure Code")); - PurchaseLine."Variant Code" := EDocumentPurchaseLine."[BC] Variant Code"; - PurchaseLine.Type := EDocumentPurchaseLine."[BC] Purchase Line Type"; - PurchaseLine.Validate("No.", EDocumentPurchaseLine."[BC] Purchase Type No."); - PurchaseLine.Description := EDocumentPurchaseLine.Description; - - if EDocumentPurchaseLine."[BC] Item Reference No." <> '' then - PurchaseLine.Validate("Item Reference No.", EDocumentPurchaseLine."[BC] Item Reference No."); - - PurchaseLine.Validate(Quantity, EDocumentPurchaseLine.Quantity); - PurchaseLine.Validate("Direct Unit Cost", EDocumentPurchaseLine."Unit Price"); - if EDocumentPurchaseLine."Total Discount" > 0 then - PurchaseLine.Validate("Line Discount Amount", EDocumentPurchaseLine."Total Discount"); - PurchaseLine.Validate("Deferral Code", EDocumentPurchaseLine."[BC] Deferral Code"); - - Clear(PurchaseLineCombinedDimensions); - PurchaseLineCombinedDimensions[1] := PurchaseLine."Dimension Set ID"; - PurchaseLineCombinedDimensions[2] := EDocumentPurchaseLine."[BC] Dimension Set ID"; - PurchaseLine.Validate("Dimension Set ID", DimensionManagement.GetCombinedDimensionSetID(PurchaseLineCombinedDimensions, GlobalDim1, GlobalDim2)); - PurchaseLine.Validate("Shortcut Dimension 1 Code", EDocumentPurchaseLine."[BC] Shortcut Dimension 1 Code"); - PurchaseLine.Validate("Shortcut Dimension 2 Code", EDocumentPurchaseLine."[BC] Shortcut Dimension 2 Code"); - EDocumentPurchaseHistMapping.ApplyHistoryValuesToPurchaseLine(EDocumentPurchaseLine, PurchaseLine); - PurchaseLine.Insert(); - - // Track changes for history - EDocumentPurchaseHistMapping.TrackRecord(EDocument, EDocumentPurchaseLine, PurchaseLine); - + CreatePurchaseLine(EDocument, PurchaseHeader, EDocumentPurchaseLine, EDocumentPurchaseHistMapping); until EDocumentPurchaseLine.Next() = 0; - exit(PurchaseHeader); - end; [TryFunction] @@ -190,4 +108,101 @@ codeunit 6105 "E-Doc. Create Purch. Cr. Memo" implements IEDocumentFinishDraft, until EDocumentPurchaseLine.Next() = 0; exit(true); end; + + local procedure CreatePurchaseLine(EDocument: Record "E-Document"; PurchaseHeader: Record "Purchase Header"; EDocumentPurchaseLine: Record "E-Document Purchase Line"; var EDocumentPurchaseHistMapping: Codeunit "E-Doc. Purchase Hist. Mapping") + var + PurchaseLine: Record "Purchase Line"; + DimensionManagement: Codeunit DimensionManagement; + PurchaseLineCombinedDimensions: array[10] of Integer; + GlobalDim1: Code[20]; + GlobalDim2: Code[20]; + begin + PurchaseLine.SetRange("Document Type", PurchaseHeader."Document Type"); + PurchaseLine.SetRange("Document No.", PurchaseHeader."No."); + if PurchaseLine.FindFirst() then; + + PurchaseLine."Document Type" := PurchaseHeader."Document Type"; + PurchaseLine."Document No." := PurchaseHeader."No."; + PurchaseLine."Line No." += 10000; + PurchaseLine."Unit of Measure Code" := CopyStr(EDocumentPurchaseLine."[BC] Unit of Measure", 1, MaxStrLen(PurchaseLine."Unit of Measure Code")); + PurchaseLine."Variant Code" := EDocumentPurchaseLine."[BC] Variant Code"; + PurchaseLine.Type := EDocumentPurchaseLine."[BC] Purchase Line Type"; + PurchaseLine.Validate("No.", EDocumentPurchaseLine."[BC] Purchase Type No."); + PurchaseLine.Description := EDocumentPurchaseLine.Description; + + if EDocumentPurchaseLine."[BC] Item Reference No." <> '' then + PurchaseLine.Validate("Item Reference No.", EDocumentPurchaseLine."[BC] Item Reference No."); + + PurchaseLine.Validate(Quantity, EDocumentPurchaseLine.Quantity); + PurchaseLine.Validate("Direct Unit Cost", EDocumentPurchaseLine."Unit Price"); + if EDocumentPurchaseLine."Total Discount" > 0 then + PurchaseLine.Validate("Line Discount Amount", EDocumentPurchaseLine."Total Discount"); + PurchaseLine.Validate("Deferral Code", EDocumentPurchaseLine."[BC] Deferral Code"); + + Clear(PurchaseLineCombinedDimensions); + PurchaseLineCombinedDimensions[1] := PurchaseLine."Dimension Set ID"; + PurchaseLineCombinedDimensions[2] := EDocumentPurchaseLine."[BC] Dimension Set ID"; + PurchaseLine.Validate("Dimension Set ID", DimensionManagement.GetCombinedDimensionSetID(PurchaseLineCombinedDimensions, GlobalDim1, GlobalDim2)); + PurchaseLine.Validate("Shortcut Dimension 1 Code", EDocumentPurchaseLine."[BC] Shortcut Dimension 1 Code"); + PurchaseLine.Validate("Shortcut Dimension 2 Code", EDocumentPurchaseLine."[BC] Shortcut Dimension 2 Code"); + EDocumentPurchaseHistMapping.ApplyHistoryValuesToPurchaseLine(EDocumentPurchaseLine, PurchaseLine); + PurchaseLine.Insert(false); + + // Track changes for history + EDocumentPurchaseHistMapping.TrackRecord(EDocument, EDocumentPurchaseLine, PurchaseLine); + end; + + local procedure CreatePurchaseHeader(EDocument: Record "E-Document"; var PurchaseHeader: Record "Purchase Header"; EDocumentPurchaseHeader: Record "E-Document Purchase Header"; var EDocumentPurchaseHistMapping: Codeunit "E-Doc. Purchase Hist. Mapping") + var + GLSetup: Record "General Ledger Setup"; + VendorLedgerEntry: Record "Vendor Ledger Entry"; + DocumentAttachmentMgt: Codeunit "Document Attachment Mgmt"; + StopCreatingPurchaseCreditMemo: Boolean; + VendorCreditMemoNo: Code[35]; + begin + PurchaseHeader.Validate("Document Type", "Purchase Document Type"::"Credit Memo"); + PurchaseHeader.Validate("Buy-from Vendor No.", EDocumentPurchaseHeader."[BC] Vendor No."); + + VendorCreditMemoNo := CopyStr(EDocumentPurchaseHeader."Sales Invoice No.", 1, MaxStrLen(PurchaseHeader."Vendor Cr. Memo No.")); + VendorLedgerEntry.SetLoadFields("Entry No."); + VendorLedgerEntry.ReadIsolation := VendorLedgerEntry.ReadIsolation::ReadUncommitted; + StopCreatingPurchaseCreditMemo := PurchaseHeader.FindPostedDocumentWithSameExternalDocNo(VendorLedgerEntry, VendorCreditMemoNo); + if StopCreatingPurchaseCreditMemo then begin + Telemetry.LogMessage('0000PHD', CreditMemoAlreadyExistsErr, Verbosity::Error, DataClassification::OrganizationIdentifiableInformation, TelemetryScope::All); + Error(CreditMemoAlreadyExistsErr, VendorCreditMemoNo, EDocumentPurchaseHeader."[BC] Vendor No."); + end; + + PurchaseHeader.Validate("Vendor Cr. Memo No.", VendorCreditMemoNo); + PurchaseHeader.Validate("Vendor Order No.", EDocumentPurchaseHeader."Purchase Order No."); + PurchaseHeader.Insert(true); + + if EDocumentPurchaseHeader."Document Date" <> 0D then + PurchaseHeader.Validate("Document Date", EDocumentPurchaseHeader."Document Date"); + if EDocumentPurchaseHeader."Due Date" <> 0D then + PurchaseHeader.Validate("Due Date", EDocumentPurchaseHeader."Due Date"); + PurchaseHeader.Modify(false); + + // Validate of currency has to happen after insert. + GLSetup.GetRecordOnce(); + if EDocumentPurchaseHeader."Currency Code" <> GLSetup.GetCurrencyCode('') then begin + PurchaseHeader.Validate("Currency Code", EDocumentPurchaseHeader."Currency Code"); + PurchaseHeader.Modify(false); + end; + + // Track changes for history + EDocumentPurchaseHistMapping.TrackRecord(EDocument, EDocumentPurchaseHeader, PurchaseHeader); + + PurchaseHeader.SetRecFilter(); + PurchaseHeader.FindFirst(); + PurchaseHeader."Doc. Amount Incl. VAT" := EDocumentPurchaseHeader.Total; + PurchaseHeader."Doc. Amount VAT" := EDocumentPurchaseHeader."Total VAT"; + PurchaseHeader.TestField("Document Type", "Purchase Document Type"::"Credit Memo"); + PurchaseHeader.TestField("No."); + PurchaseHeader."E-Document Link" := EDocument.SystemId; + PurchaseHeader.Modify(false); + + // Post document creation + DocumentAttachmentMgt.CopyAttachments(EDocument, PurchaseHeader); + DocumentAttachmentMgt.DeleteAttachedDocuments(EDocument); + end; } diff --git a/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/EDocProviders.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/EDocProviders.Codeunit.al index a4881d7859..b37b45e636 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/EDocProviders.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/EDocProviders.Codeunit.al @@ -115,6 +115,7 @@ codeunit 6124 "E-Doc. Providers" implements IPurchaseLineProvider, IUnitOfMeasur Vendor: Record Vendor; VendorReceiveEDocumentErr: Label 'The %1 = %2 has an invalid %3 value: %4. Please correct the vendor setup.', Comment = '%1 = Vendor Table Caption, %2 = Vendor No., %3 = Receive E-Document To Field Caption, %4 = Receive E-Document To Value'; begin + Vendor.SetLoadFields("Receive E-Document To"); Vendor.Get(EDocumentPurchaseHeader."[BC] Vendor No."); case Vendor."Receive E-Document To" of Vendor."Receive E-Document To"::"Purchase Invoice": @@ -169,16 +170,16 @@ codeunit 6124 "E-Doc. Providers" implements IPurchaseLineProvider, IUnitOfMeasur case PurchaseLineType of "Purchase Line Type"::Item: begin - if Item.Get(PurchaseLineTypeNo) then; - if Item."Purch. Unit of Measure" <> '' then - UnitOfMeasureCode := Item."Purch. Unit of Measure" - else - UnitOfMeasureCode := Item."Base Unit of Measure"; + if Item.Get(PurchaseLineTypeNo) then + if Item."Purch. Unit of Measure" <> '' then + UnitOfMeasureCode := Item."Purch. Unit of Measure" + else + UnitOfMeasureCode := Item."Base Unit of Measure"; end; "Purchase Line Type"::Resource: begin - if Resource.Get(PurchaseLineTypeNo) then; - UnitOfMeasureCode := Resource."Base Unit of Measure"; + if Resource.Get(PurchaseLineTypeNo) then + UnitOfMeasureCode := Resource."Base Unit of Measure"; end; end; end; diff --git a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al index 5542f41b6e..9437c0ee9e 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al @@ -101,14 +101,13 @@ codeunit 6173 "E-Document PEPPOL Handler" implements IStructuredFormatReader if not PeppolXML.SelectNodes(CreditNoteLinePathLbl, XmlNamespaces, LineXMLList) then exit; - for i := 1 to LineXMLList.Count do begin + foreach LineXMLNode in LineXMLList do begin Clear(EDocumentPurchaseLine); EDocumentPurchaseLine.Validate("E-Document Entry No.", EDocumentEntryNo); EDocumentPurchaseLine."Line No." := EDocumentPurchaseLine.GetNextLineNo(EDocumentEntryNo); - LineXMLList.Get(i, LineXMLNode); NewLineXML.ReplaceNodes(LineXMLNode); PopulateEDocumentPurchaseCreditMemoLine(NewLineXML, XmlNamespaces, EDocumentPurchaseLine); - EDocumentPurchaseLine.Insert(); + EDocumentPurchaseLine.Insert(false); end; end; From a3f2ad74c0a0042d73be56912bbb896c7db6526e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Wed, 30 Jul 2025 16:52:34 +0300 Subject: [PATCH 03/28] Fixes purchase line numbering by using FindLast Changes the purchase line lookup from FindFirst to FindLast to ensure proper line number sequencing when creating new purchase lines. Moves the CreatePurchaseLine method to the end of the file for better code organization. --- .../EDocCreatePurchCrMemo.Codeunit.al | 86 +++++++++---------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al index 71fbf4a486..afa3086730 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al @@ -109,49 +109,6 @@ codeunit 6105 "E-Doc. Create Purch. Cr. Memo" implements IEDocumentFinishDraft, exit(true); end; - local procedure CreatePurchaseLine(EDocument: Record "E-Document"; PurchaseHeader: Record "Purchase Header"; EDocumentPurchaseLine: Record "E-Document Purchase Line"; var EDocumentPurchaseHistMapping: Codeunit "E-Doc. Purchase Hist. Mapping") - var - PurchaseLine: Record "Purchase Line"; - DimensionManagement: Codeunit DimensionManagement; - PurchaseLineCombinedDimensions: array[10] of Integer; - GlobalDim1: Code[20]; - GlobalDim2: Code[20]; - begin - PurchaseLine.SetRange("Document Type", PurchaseHeader."Document Type"); - PurchaseLine.SetRange("Document No.", PurchaseHeader."No."); - if PurchaseLine.FindFirst() then; - - PurchaseLine."Document Type" := PurchaseHeader."Document Type"; - PurchaseLine."Document No." := PurchaseHeader."No."; - PurchaseLine."Line No." += 10000; - PurchaseLine."Unit of Measure Code" := CopyStr(EDocumentPurchaseLine."[BC] Unit of Measure", 1, MaxStrLen(PurchaseLine."Unit of Measure Code")); - PurchaseLine."Variant Code" := EDocumentPurchaseLine."[BC] Variant Code"; - PurchaseLine.Type := EDocumentPurchaseLine."[BC] Purchase Line Type"; - PurchaseLine.Validate("No.", EDocumentPurchaseLine."[BC] Purchase Type No."); - PurchaseLine.Description := EDocumentPurchaseLine.Description; - - if EDocumentPurchaseLine."[BC] Item Reference No." <> '' then - PurchaseLine.Validate("Item Reference No.", EDocumentPurchaseLine."[BC] Item Reference No."); - - PurchaseLine.Validate(Quantity, EDocumentPurchaseLine.Quantity); - PurchaseLine.Validate("Direct Unit Cost", EDocumentPurchaseLine."Unit Price"); - if EDocumentPurchaseLine."Total Discount" > 0 then - PurchaseLine.Validate("Line Discount Amount", EDocumentPurchaseLine."Total Discount"); - PurchaseLine.Validate("Deferral Code", EDocumentPurchaseLine."[BC] Deferral Code"); - - Clear(PurchaseLineCombinedDimensions); - PurchaseLineCombinedDimensions[1] := PurchaseLine."Dimension Set ID"; - PurchaseLineCombinedDimensions[2] := EDocumentPurchaseLine."[BC] Dimension Set ID"; - PurchaseLine.Validate("Dimension Set ID", DimensionManagement.GetCombinedDimensionSetID(PurchaseLineCombinedDimensions, GlobalDim1, GlobalDim2)); - PurchaseLine.Validate("Shortcut Dimension 1 Code", EDocumentPurchaseLine."[BC] Shortcut Dimension 1 Code"); - PurchaseLine.Validate("Shortcut Dimension 2 Code", EDocumentPurchaseLine."[BC] Shortcut Dimension 2 Code"); - EDocumentPurchaseHistMapping.ApplyHistoryValuesToPurchaseLine(EDocumentPurchaseLine, PurchaseLine); - PurchaseLine.Insert(false); - - // Track changes for history - EDocumentPurchaseHistMapping.TrackRecord(EDocument, EDocumentPurchaseLine, PurchaseLine); - end; - local procedure CreatePurchaseHeader(EDocument: Record "E-Document"; var PurchaseHeader: Record "Purchase Header"; EDocumentPurchaseHeader: Record "E-Document Purchase Header"; var EDocumentPurchaseHistMapping: Codeunit "E-Doc. Purchase Hist. Mapping") var GLSetup: Record "General Ledger Setup"; @@ -205,4 +162,47 @@ codeunit 6105 "E-Doc. Create Purch. Cr. Memo" implements IEDocumentFinishDraft, DocumentAttachmentMgt.CopyAttachments(EDocument, PurchaseHeader); DocumentAttachmentMgt.DeleteAttachedDocuments(EDocument); end; + + local procedure CreatePurchaseLine(EDocument: Record "E-Document"; PurchaseHeader: Record "Purchase Header"; EDocumentPurchaseLine: Record "E-Document Purchase Line"; var EDocumentPurchaseHistMapping: Codeunit "E-Doc. Purchase Hist. Mapping") + var + PurchaseLine: Record "Purchase Line"; + DimensionManagement: Codeunit DimensionManagement; + PurchaseLineCombinedDimensions: array[10] of Integer; + GlobalDim1: Code[20]; + GlobalDim2: Code[20]; + begin + PurchaseLine.SetRange("Document Type", PurchaseHeader."Document Type"); + PurchaseLine.SetRange("Document No.", PurchaseHeader."No."); + if PurchaseLine.FindLast() then; + + PurchaseLine."Document Type" := PurchaseHeader."Document Type"; + PurchaseLine."Document No." := PurchaseHeader."No."; + PurchaseLine."Line No." += 10000; + PurchaseLine."Unit of Measure Code" := CopyStr(EDocumentPurchaseLine."[BC] Unit of Measure", 1, MaxStrLen(PurchaseLine."Unit of Measure Code")); + PurchaseLine."Variant Code" := EDocumentPurchaseLine."[BC] Variant Code"; + PurchaseLine.Type := EDocumentPurchaseLine."[BC] Purchase Line Type"; + PurchaseLine.Validate("No.", EDocumentPurchaseLine."[BC] Purchase Type No."); + PurchaseLine.Description := EDocumentPurchaseLine.Description; + + if EDocumentPurchaseLine."[BC] Item Reference No." <> '' then + PurchaseLine.Validate("Item Reference No.", EDocumentPurchaseLine."[BC] Item Reference No."); + + PurchaseLine.Validate(Quantity, EDocumentPurchaseLine.Quantity); + PurchaseLine.Validate("Direct Unit Cost", EDocumentPurchaseLine."Unit Price"); + if EDocumentPurchaseLine."Total Discount" > 0 then + PurchaseLine.Validate("Line Discount Amount", EDocumentPurchaseLine."Total Discount"); + PurchaseLine.Validate("Deferral Code", EDocumentPurchaseLine."[BC] Deferral Code"); + + Clear(PurchaseLineCombinedDimensions); + PurchaseLineCombinedDimensions[1] := PurchaseLine."Dimension Set ID"; + PurchaseLineCombinedDimensions[2] := EDocumentPurchaseLine."[BC] Dimension Set ID"; + PurchaseLine.Validate("Dimension Set ID", DimensionManagement.GetCombinedDimensionSetID(PurchaseLineCombinedDimensions, GlobalDim1, GlobalDim2)); + PurchaseLine.Validate("Shortcut Dimension 1 Code", EDocumentPurchaseLine."[BC] Shortcut Dimension 1 Code"); + PurchaseLine.Validate("Shortcut Dimension 2 Code", EDocumentPurchaseLine."[BC] Shortcut Dimension 2 Code"); + EDocumentPurchaseHistMapping.ApplyHistoryValuesToPurchaseLine(EDocumentPurchaseLine, PurchaseLine); + PurchaseLine.Insert(false); + + // Track changes for history + EDocumentPurchaseHistMapping.TrackRecord(EDocument, EDocumentPurchaseLine, PurchaseLine); + end; } From 8fd6e5fcde0fb1937bb05fe3405fc3c353535f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Wed, 30 Jul 2025 17:26:57 +0300 Subject: [PATCH 04/28] Adds XML documentation to E-Document helper methods Improves code maintainability by adding comprehensive XML documentation comments to helper methods in the E-Document module. Documents parameter types, descriptions, and return values for XML parsing utilities and purchase credit memo creation procedures to enhance developer experience and code understanding. --- .../Helpers/EDocumentXMLHelper.Codeunit.al | 30 +++++++++++++++++++ .../EDocCreatePurchCrMemo.Codeunit.al | 21 +++++++++++-- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al b/Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al index 647d652426..e28bc7b066 100644 --- a/Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al @@ -10,6 +10,14 @@ codeunit 6127 "EDocument XML Helper" { Access = Internal; + /// + /// Extracts currency value from XML document and sets it in the currency field if it differs from LCY. + /// + /// The XML document to search in. + /// The XML namespace manager for XPath queries. + /// The XPath expression to locate the currency value. + /// The maximum length of the currency code. + /// The currency field to update with the extracted value. internal procedure SetCurrencyValueInField(XMLDocument: XmlDocument; XMLNamespaces: XmlNamespaceManager; Path: Text; MaxLength: Integer; var CurrencyField: Code[10]) var GLSetup: Record "General Ledger Setup"; @@ -38,6 +46,14 @@ codeunit 6127 "EDocument XML Helper" #pragma warning restore AA0139 end; + /// + /// Extracts string value from XML document and sets it in the specified field. + /// + /// The XML document to search in. + /// The XML namespace manager for XPath queries. + /// The XPath expression to locate the string value. + /// The maximum length of the string value. + /// The text field to update with the extracted value. internal procedure SetStringValueInField(XMLDocument: XmlDocument; XMLNamespaces: XmlNamespaceManager; Path: Text; MaxLength: Integer; var Field: Text) var XMLNode: XmlNode; @@ -56,6 +72,13 @@ codeunit 6127 "EDocument XML Helper" end; end; + /// + /// Extracts numeric value from XML document and sets it in the specified decimal field. + /// + /// The XML document to search in. + /// The XML namespace manager for XPath queries. + /// The XPath expression to locate the numeric value. + /// The decimal field to update with the extracted value. internal procedure SetNumberValueInField(XMLDocument: XmlDocument; XMLNamespaces: XmlNamespaceManager; Path: Text; var DecimalValue: Decimal) var XMLNode: XmlNode; @@ -67,6 +90,13 @@ codeunit 6127 "EDocument XML Helper" Evaluate(DecimalValue, XMLNode.AsXmlElement().InnerText(), 9); end; + /// + /// Extracts date value from XML document and sets it in the specified date field. + /// + /// The XML document to search in. + /// The XML namespace manager for XPath queries. + /// The XPath expression to locate the date value. + /// The date field to update with the extracted value. internal procedure SetDateValueInField(XMLDocument: XmlDocument; XMLNamespaces: XmlNamespaceManager; Path: Text; var DateValue: Date) var XMLNode: XmlNode; diff --git a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al index afa3086730..0e5cc6105b 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al @@ -30,7 +30,13 @@ codeunit 6105 "E-Doc. Create Purch. Cr. Memo" implements IEDocumentFinishDraft, CreditMemoAlreadyExistsErr: Label 'A purchase credit memo with external document number %1 already exists for vendor %2.', Comment = '%1 = Vendor Credit Memo No., %2 = Vendor No.'; DraftLineDoesNotConstantTypeAndNumberErr: Label 'One of the draft lines do not contain the type and number. Please, specify these fields manually.'; - procedure ApplyDraftToBC(EDocument: Record "E-Document"; EDocImportParameters: Record "E-Doc. Import Parameters"): RecordId + /// + /// Applies the draft E-Document to Business Central by creating a purchase credit memo from the draft data. + /// + /// The E-Document record containing the draft data to be applied. + /// The import parameters containing processing customizations. + /// The RecordId of the created purchase credit memo. + internal procedure ApplyDraftToBC(EDocument: Record "E-Document"; EDocImportParameters: Record "E-Doc. Import Parameters"): RecordId var EDocumentPurchaseHeader: Record "E-Document Purchase Header"; PurchaseHeader: Record "Purchase Header"; @@ -47,7 +53,11 @@ codeunit 6105 "E-Doc. Create Purch. Cr. Memo" implements IEDocumentFinishDraft, exit(PurchaseHeader.RecordId); end; - procedure RevertDraftActions(EDocument: Record "E-Document") + /// + /// Reverts the draft actions by deleting the associated purchase credit memo document. + /// + /// The E-Document record whose draft actions should be reverted. + internal procedure RevertDraftActions(EDocument: Record "E-Document") var PurchaseHeader: Record "Purchase Header"; begin @@ -59,7 +69,12 @@ codeunit 6105 "E-Doc. Create Purch. Cr. Memo" implements IEDocumentFinishDraft, PurchaseHeader.Delete(true); end; - procedure CreatePurchaseCreditMemo(EDocument: Record "E-Document"): Record "Purchase Header" + /// + /// Creates a purchase credit memo from E-Document draft data, including header and line information. + /// + /// The E-Document record containing the draft data to create the credit memo from. + /// The created purchase header record for the credit memo. + internal procedure CreatePurchaseCreditMemo(EDocument: Record "E-Document"): Record "Purchase Header" var PurchaseHeader: Record "Purchase Header"; EDocumentPurchaseHeader: Record "E-Document Purchase Header"; From 45d806b2ee578c6ac219e325198b673f3e5af4ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= <30231314+AndriusAndrulevicius@users.noreply.github.com> Date: Thu, 31 Jul 2025 12:31:28 +0300 Subject: [PATCH 05/28] Update EDocumentXMLHelper.Codeunit.al Use next available ID (recent MSFT changes introduced new obects) --- .../EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al b/Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al index e28bc7b066..353d62e000 100644 --- a/Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al @@ -6,7 +6,7 @@ namespace Microsoft.eServices.EDocument.Helpers; using Microsoft.Finance.GeneralLedger.Setup; -codeunit 6127 "EDocument XML Helper" +codeunit 6175 "EDocument XML Helper" { Access = Internal; @@ -107,4 +107,4 @@ codeunit 6127 "EDocument XML Helper" if XMLNode.AsXmlElement().InnerText() <> '' then Evaluate(DateValue, XMLNode.AsXmlElement().InnerText(), 9); end; -} \ No newline at end of file +} From 6bac7c437012885d154cd0ba6e2f6645a027babe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Mon, 4 Aug 2025 14:10:28 +0300 Subject: [PATCH 06/28] Reorganizes imports and updates codeunit ID Changes codeunit number from 6175 to 6177 to resolve potential ID conflicts. Alphabetizes using statements across multiple files to improve code organization and maintainability. Adds missing blank lines in enum definitions for better formatting consistency. Enhances documentation by adding parameter descriptions to interface method. --- .../app/src/Helpers/EDocumentXMLHelper.Codeunit.al | 2 +- .../Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al | 8 ++++---- .../Import/FinishDraft/EDocCreatePurchCrMemo.Enum.al | 1 + .../Import/FinishDraft/EDocCreatePurchaseInvoice.Enum.al | 1 + .../IEDocumentCreatePurchaseCreditMemo.Interface.al | 4 ++-- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al b/Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al index 353d62e000..096b39501c 100644 --- a/Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al @@ -6,7 +6,7 @@ namespace Microsoft.eServices.EDocument.Helpers; using Microsoft.Finance.GeneralLedger.Setup; -codeunit 6175 "EDocument XML Helper" +codeunit 6177 "EDocument XML Helper" { Access = Internal; diff --git a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al index 0e5cc6105b..ff3bd00eb2 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al @@ -5,15 +5,15 @@ namespace Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument; -using Microsoft.Finance.Dimension; -using Microsoft.Purchases.Document; -using Microsoft.eServices.EDocument.Processing.Interfaces; using Microsoft.eServices.EDocument.Processing.Import.Purchase; +using Microsoft.eServices.EDocument.Processing.Interfaces; +using Microsoft.Finance.Dimension; using Microsoft.Finance.GeneralLedger.Setup; +using Microsoft.Foundation.Attachment; +using Microsoft.Purchases.Document; using Microsoft.Purchases.Payables; using Microsoft.Purchases.Posting; using System.Telemetry; -using Microsoft.Foundation.Attachment; /// /// Dealing with the creation of the purchase credit memo after the draft has been populated. diff --git a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Enum.al b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Enum.al index 09052e3ec5..ed880f70a9 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Enum.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Enum.al @@ -13,5 +13,6 @@ enum 6118 "E-Doc. Create Purch. Cr. Memo" implements IEDocumentCreatePurchaseCre { Extensible = true; DefaultImplementation = IEDocumentCreatePurchaseCreditMemo = "E-Doc. Create Purch. Cr. Memo"; + value(0; "Default") { } } diff --git a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Enum.al b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Enum.al index 63f1899d51..155c31ae11 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Enum.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Enum.al @@ -13,5 +13,6 @@ enum 6105 "E-Doc. Create Purchase Invoice" implements IEDocumentCreatePurchaseIn { Extensible = true; DefaultImplementation = IEDocumentCreatePurchaseInvoice = "E-Doc. Create Purchase Invoice"; + value(0; "Default") { } } \ No newline at end of file diff --git a/Apps/W1/EDocument/app/src/Processing/Interfaces/IEDocumentCreatePurchaseCreditMemo.Interface.al b/Apps/W1/EDocument/app/src/Processing/Interfaces/IEDocumentCreatePurchaseCreditMemo.Interface.al index dc115f1b15..a657e831b7 100644 --- a/Apps/W1/EDocument/app/src/Processing/Interfaces/IEDocumentCreatePurchaseCreditMemo.Interface.al +++ b/Apps/W1/EDocument/app/src/Processing/Interfaces/IEDocumentCreatePurchaseCreditMemo.Interface.al @@ -15,7 +15,7 @@ interface IEDocumentCreatePurchaseCreditMemo /// /// Creates a purchase credit memo from an E-Document with a draft ready. /// - /// - /// + /// The E-Document record to create the purchase credit memo from. + /// The created purchase header record for the credit memo. procedure CreatePurchaseCreditMemo(EDocument: Record "E-Document"): Record "Purchase Header"; } From 9235e0516667e9006046fb3c6e8e490e062f377b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Mon, 4 Aug 2025 14:12:34 +0300 Subject: [PATCH 07/28] Adds whitespace for code readability Improves code formatting by adding a blank line after the exit statement to enhance visual separation between the early return and subsequent logic flow. --- .../Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al | 1 + 1 file changed, 1 insertion(+) diff --git a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al index ff3bd00eb2..7eed98fa1a 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al @@ -64,6 +64,7 @@ codeunit 6105 "E-Doc. Create Purch. Cr. Memo" implements IEDocumentFinishDraft, PurchaseHeader.SetRange("E-Document Link", EDocument.SystemId); if not PurchaseHeader.FindFirst() then exit; + PurchaseHeader.TestField("Document Type", "Purchase Document Type"::"Credit Memo"); Clear(PurchaseHeader."E-Document Link"); PurchaseHeader.Delete(true); From 23d1e892de3d9d34e472f89548047f03a14ce737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Sat, 9 Aug 2025 04:32:28 +0300 Subject: [PATCH 08/28] Improves unit of measure handling in purchase documents Sets default unit of measure when missing during purchase line processing to prevent validation errors. Clears item reference number when purchase type changes to maintain data consistency and avoid mismatched references. --- .../PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al | 5 +++-- .../Import/Purchase/EDocPurchaseDraftSubform.Page.al | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al index fdb996ae2e..7275079d2a 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al @@ -77,11 +77,12 @@ codeunit 6125 "Prepare Purchase E-Doc. Draft" implements IProcessStructuredData if EDocPurchaseHistMapping.FindRelatedPurchaseLineInHistory(EDocumentPurchaseHeader."[BC] Vendor No.", EDocumentPurchaseLine, EDocPurchaseLineHistory) then EDocPurchaseHistMapping.UpdateMissingLineValuesFromHistory(EDocPurchaseLineHistory, EDocumentPurchaseLine); + if EDocumentPurchaseLine."[BC] Unit of Measure" = '' then + EDocumentPurchaseLine."[BC] Unit of Measure" := IUnitOfMeasureProvider.GetDefaultUnitOfMeasure(EDocumentPurchaseLine."[BC] Purchase Line Type", EDocumentPurchaseLine."[BC] Purchase Type No."); + EDocumentPurchaseLine.Modify(); LogActivitySessionChanges(EDocActivityLogSession); EDocActivityLogSession.CleanUpLogs(); - - until EDocumentPurchaseLine.Next() = 0; // Ask Copilot to try to find fields that are suited to be matched diff --git a/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al b/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al index 3668a20614..739b7e843d 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al @@ -33,6 +33,13 @@ page 6183 "E-Doc. Purchase Draft Subform" { ApplicationArea = All; Lookup = true; + trigger OnValidate() + begin + if Rec."[BC] Purchase Type No." <> xRec."[BC] Purchase Type No." then begin + Clear(Rec."[BC] Item Reference No."); + CurrPage.Update(false); + end; + end; } field("Item Reference No."; Rec."[BC] Item Reference No.") { From 78c579654266c0926e1d6b427ad60edb6edc5e8b Mon Sep 17 00:00:00 2001 From: Grasiele Matuleviciute <131970463+GMatuleviciute@users.noreply.github.com> Date: Mon, 11 Aug 2025 09:34:45 +0300 Subject: [PATCH 09/28] Apply suggestion from @GMatuleviciute --- .../Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al b/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al index 739b7e843d..ffa140f816 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al @@ -33,6 +33,8 @@ page 6183 "E-Doc. Purchase Draft Subform" { ApplicationArea = All; Lookup = true; +Lookup = true; + trigger OnValidate() begin if Rec."[BC] Purchase Type No." <> xRec."[BC] Purchase Type No." then begin From e13670c2ce2b85ec139d9454419a5c87b7b20668 Mon Sep 17 00:00:00 2001 From: Grasiele Matuleviciute <131970463+GMatuleviciute@users.noreply.github.com> Date: Mon, 11 Aug 2025 09:35:27 +0300 Subject: [PATCH 10/28] Apply suggestion from @GMatuleviciute --- .../Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al | 1 - 1 file changed, 1 deletion(-) diff --git a/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al b/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al index ffa140f816..e5ac0f4371 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al @@ -33,7 +33,6 @@ page 6183 "E-Doc. Purchase Draft Subform" { ApplicationArea = All; Lookup = true; -Lookup = true; trigger OnValidate() begin From 5ad28aa79f3c8ba754482dfba5fc56223ed80703 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Mon, 11 Aug 2025 18:49:12 +0300 Subject: [PATCH 11/28] Enhances PEPPOL e-document testing with comprehensive validation Adds three new test methods to validate PEPPOL invoice processing workflow including data extraction viewing, purchase invoice creation, and draft modification scenarios. Improves test data consistency by introducing mock currency and date variables that can be configured across test methods rather than hardcoded values. Fixes page validation trigger formatting by removing unnecessary page update call and correcting indentation. Strengthens assertion messages with detailed error information including field names, table names, expected and actual values for better debugging. --- .../Purchase/EDocPurchaseDraftSubform.Page.al | 6 +- .../EDocumentStructuredTests.Codeunit.al | 173 +++++++++++++++++- .../PEPPOLStructuredValidations.Codeunit.al | 125 +++++++++---- 3 files changed, 264 insertions(+), 40 deletions(-) diff --git a/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al b/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al index e5ac0f4371..53b1179575 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al @@ -36,11 +36,9 @@ page 6183 "E-Doc. Purchase Draft Subform" trigger OnValidate() begin - if Rec."[BC] Purchase Type No." <> xRec."[BC] Purchase Type No." then begin + if Rec."[BC] Purchase Type No." <> xRec."[BC] Purchase Type No." then Clear(Rec."[BC] Item Reference No."); - CurrPage.Update(false); - end; - end; + end; } field("Item Reference No."; Rec."[BC] Item Reference No.") { diff --git a/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al b/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al index ee7a702132..e06a0be65a 100644 --- a/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al +++ b/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al @@ -16,6 +16,8 @@ codeunit 139891 "E-Document Structured Tests" PEPPOLStructuredValidations: Codeunit "PEPPOL Structured Validations"; IsInitialized: Boolean; EDocumentStatusNotUpdatedErr: Label 'The status of the EDocument was not updated to the expected status after the step was executed.'; + MockCurrencyCode: Code[10]; + MockDate: Date; #region CAPI JSON [Test] @@ -75,11 +77,147 @@ codeunit 139891 "E-Document Structured Tests" Initialize(Enum::"Service Integration"::"Mock"); SetupPEPPOLEDocumentService(); CreateInboundEDocumentFromXML(EDocument, 'peppol/peppol-invoice-0.xml'); - if ProcessEDocumentToStep(EDocument, "Import E-Document Steps"::"Read into Draft") then - PEPPOLStructuredValidations.AssertFullEDocumentContentExtracted(EDocument."Entry No") + if ProcessEDocumentToStep(EDocument, "Import E-Document Steps"::"Read into Draft") then begin + PEPPOLStructuredValidations.SetMockCurrencyCode(MockCurrencyCode); + PEPPOLStructuredValidations.SetMockDate(MockDate); + PEPPOLStructuredValidations.AssertFullEDocumentContentExtracted(EDocument."Entry No"); + end else Assert.Fail(EDocumentStatusNotUpdatedErr); end; + + [Test] + [HandlerFunctions('EDocumentPurchaseHeaderPageHandler')] + procedure TestPEPPOLInvoice_ValidDocument_ViewExtractedData() + var + EDocument: Record "E-Document"; + EDocImport: Codeunit "E-Doc. Import"; + begin + // [FEATURE] [E-Document] [PEPPOL] [View Data] + // [SCENARIO] View extracted data from a valid PEPPOL invoice document + // --------------------------------------------------------------------------- + // [Expected Outcome] + // [1] E-Document is successfully processed to draft status + // [2] Extracted data page opens without errors + // [3] Page handler verifies the page can be closed properly + + // [GIVEN] A valid PEPPOL XML invoice document is imported and processed + Initialize(Enum::"Service Integration"::"Mock"); + SetupPEPPOLEDocumentService(); + CreateInboundEDocumentFromXML(EDocument, 'peppol/peppol-invoice-0.xml'); + + // [WHEN] The document is processed to draft status + ProcessEDocumentToStep(EDocument, "Import E-Document Steps"::"Read into Draft"); + EDocument.Get(EDocument."Entry No"); + + // [WHEN] View extracted data is called + EDocImport.ViewExtractedData(EDocument); + + // [THEN] The extracted data page opens and can be handled properly (verified by page handler) + end; + + [Test] + procedure TestPEPPOLInvoice_ValidDocument_PurchaseInvoiceCreated() + var + EDocument: Record "E-Document"; + PurchaseHeader: Record "Purchase Header"; + EDocumentProcessing: Codeunit "E-Document Processing"; + DataTypeManagement: Codeunit "Data Type Management"; + VariantRecord: Variant; + RecRef: RecordRef; + begin + // [FEATURE] [E-Document] [PEPPOL] [Purchase Invoice Creation] + // [SCENARIO] Create a purchase invoice from a valid PEPPOL invoice document + // --------------------------------------------------------------------------- + // [Expected Outcome] + // [1] E-Document is successfully processed through all import steps + // [2] A purchase header record is created and linked to the E-Document + // [3] Purchase header fields are correctly populated with PEPPOL data + // [4] All validation assertions pass for the created purchase document + + // [GIVEN] A valid PEPPOL XML invoice document is imported + Initialize(Enum::"Service Integration"::"Mock"); + SetupPEPPOLEDocumentService(); + CreateInboundEDocumentFromXML(EDocument, 'peppol/peppol-invoice-0.xml'); + + // [WHEN] The document is processed through finish draft step + ProcessEDocumentToStep(EDocument, "Import E-Document Steps"::"Finish draft"); + EDocument.Get(EDocument."Entry No"); + + // [WHEN] The created purchase record is retrieved + EDocumentProcessing.GetRecord(EDocument, VariantRecord); + DataTypeManagement.GetRecordRef(VariantRecord, RecRef); + RecRef.SetTable(PurchaseHeader); + + // [THEN] The purchase header is correctly created with PEPPOL data + PEPPOLStructuredValidations.SetMockCurrencyCode(MockCurrencyCode); + PEPPOLStructuredValidations.SetMockDate(MockDate); + PEPPOLStructuredValidations.AssertPurchaseHeader(Vendor."No.", PurchaseHeader); + end; + + [Test] + procedure TestPEPPOLInvoice_ValidDocument_UpdateDraftAndFinalize() + var + EDocument: Record "E-Document"; + PurchaseHeader: Record "Purchase Header"; + Item: Record Item; + EDocImportParameters: Record "E-Doc. Import Parameters"; + EDocImport: Codeunit "E-Doc. Import"; + EDocumentProcessing: Codeunit "E-Document Processing"; + DataTypeManagement: Codeunit "Data Type Management"; + EDocPurchaseDraftSubform: TestPage "E-Doc. Purchase Draft Subform"; + EDocPurchaseDraft: TestPage "E-Document Purchase Draft"; + VariantRecord: Variant; + RecRef: RecordRef; + begin + // [FEATURE] [E-Document] [PEPPOL] [Draft Update] [Manual Intervention] + // [SCENARIO] Update draft purchase document data and finalize processing + // --------------------------------------------------------------------------- + // [Expected Outcome] + // [1] E-Document is processed to prepare draft status + // [2] Draft document can be opened and modified through UI + // [3] Item number can be manually assigned to purchase lines + // [4] Document processing can be completed after manual updates + // [5] Final purchase header contains both imported and manually updated data + + // [GIVEN] A valid PEPPOL XML invoice document is imported and processed to draft preparation + Initialize(Enum::"Service Integration"::"Mock"); + SetupPEPPOLEDocumentService(); + CreateInboundEDocumentFromXML(EDocument, 'peppol/peppol-invoice-0.xml'); + ProcessEDocumentToStep(EDocument, "Import E-Document Steps"::"Prepare draft"); + + // [GIVEN] A generic item is created for manual assignment + LibraryEDoc.CreateGenericItem(Item); + + // [WHEN] The draft document is opened and modified through UI + EDocPurchaseDraft.OpenEdit(); + EDocPurchaseDraft.GoToRecord(EDocument); + EDocPurchaseDraft.Lines.First(); + EDocPurchaseDraft.Lines."No.".SetValue(Item."No."); + EDocPurchaseDraft.Lines.Next(); + + // [WHEN] The processing is completed to finish draft step + EDocImportParameters."Step to Run" := "Import E-Document Steps"::"Finish draft"; + EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParameters); + EDocument.Get(EDocument."Entry No"); + + // [WHEN] The final purchase record is retrieved + EDocumentProcessing.GetRecord(EDocument, VariantRecord); + DataTypeManagement.GetRecordRef(VariantRecord, RecRef); + RecRef.SetTable(PurchaseHeader); + + // [THEN] The purchase header contains both imported PEPPOL data and manual updates + PEPPOLStructuredValidations.SetMockCurrencyCode(MockCurrencyCode); + PEPPOLStructuredValidations.SetMockDate(MockDate); + PEPPOLStructuredValidations.SetItem(Item); + PEPPOLStructuredValidations.AssertPurchaseHeader(Vendor."No.", PurchaseHeader); + end; + + [PageHandler] + procedure EDocumentPurchaseHeaderPageHandler(var EDocReadablePurchaseDoc: TestPage "E-Doc. Readable Purchase Doc.") + begin + EDocReadablePurchaseDoc.Close(); + end; #endregion local procedure Initialize(Integration: Enum "Service Integration") @@ -119,9 +257,13 @@ codeunit 139891 "E-Document Structured Tests" EDocumentsSetup.InsertNewExperienceSetup(); // Set a currency that can be used across all localizations + MockCurrencyCode := 'XYZ'; Currency.Init(); - Currency.Validate(Code, 'XYZ'); + Currency.Validate(Code, MockCurrencyCode); if Currency.Insert(true) then; + CreateCurrencyExchangeRate(); + + MockDate := DMY2Date(22, 01, 2026); TransformationRule.DeleteAll(); TransformationRule.CreateDefaultTransformations(); @@ -181,6 +323,29 @@ codeunit 139891 "E-Document Structured Tests" EDocImportParameters."Step to Run" := ProcessingStep; EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParameters); EDocument.CalcFields("Import Processing Status"); - exit(EDocument."Import Processing Status" = Enum::"Import E-Doc. Proc. Status"::"Ready for draft"); + + // Update the exit condition to handle different processing steps + case ProcessingStep of + "Import E-Document Steps"::"Read into Draft": + exit(EDocument."Import Processing Status" = Enum::"Import E-Doc. Proc. Status"::"Ready for draft"); + "Import E-Document Steps"::"Finish draft": + exit(EDocument."Import Processing Status" = Enum::"Import E-Doc. Proc. Status"::Processed); + "Import E-Document Steps"::"Prepare draft": + exit(EDocument."Import Processing Status" = Enum::"Import E-Doc. Proc. Status"::"Draft Ready"); + else + exit(EDocument."Import Processing Status" = Enum::"Import E-Doc. Proc. Status"::"Ready for draft"); + end; + end; + + local procedure CreateCurrencyExchangeRate() + var + CurrencyExchangeRate: Record "Currency Exchange Rate"; + begin + CurrencyExchangeRate.Init(); + CurrencyExchangeRate."Currency Code" := MockCurrencyCode; + CurrencyExchangeRate."Starting Date" := WorkDate(); + CurrencyExchangeRate."Exchange Rate Amount" := 10; + CurrencyExchangeRate."Relational Exch. Rate Amount" := 1.23; + CurrencyExchangeRate.Insert(true); end; } diff --git a/Apps/W1/EDocument/test/src/Processing/PEPPOLStructuredValidations.Codeunit.al b/Apps/W1/EDocument/test/src/Processing/PEPPOLStructuredValidations.Codeunit.al index 32882d89ae..7154018915 100644 --- a/Apps/W1/EDocument/test/src/Processing/PEPPOLStructuredValidations.Codeunit.al +++ b/Apps/W1/EDocument/test/src/Processing/PEPPOLStructuredValidations.Codeunit.al @@ -2,52 +2,113 @@ codeunit 139896 "PEPPOL Structured Validations" { var Assert: Codeunit Assert; + Item: Record Item; + UnitOfMeasureCodeTok: Label 'PCS', Locked = true; + SalesInvoiceNoTok: Label '103033'; + PurchaseorderNoTok: Label '2'; + MockDate: Date; + MockCurrencyCode: Code[10]; + MockDataMismatchLbl: Label 'The %1 in %2 does not align with the mock data. Expected: %3, Actual: %4', Locked = true; internal procedure AssertFullEDocumentContentExtracted(EDocumentEntryNo: Integer) var - GLSetup: Record "General Ledger Setup"; EDocumentPurchaseHeader: Record "E-Document Purchase Header"; EDocumentPurchaseLine: Record "E-Document Purchase Line"; begin - GLSetup.Get(); EDocumentPurchaseHeader.Get(EDocumentEntryNo); - Assert.AreEqual('103033', EDocumentPurchaseHeader."Sales Invoice No.", 'The sales invoice number does not allign with the mock data.'); - Assert.AreEqual(DMY2Date(22, 01, 2026), EDocumentPurchaseHeader."Document Date", 'The invoice date does not allign with the mock data.'); - Assert.AreEqual(DMY2Date(22, 02, 2026), EDocumentPurchaseHeader."Due Date", 'The due date does not allign with the mock data.'); - Assert.AreEqual('XYZ', EDocumentPurchaseHeader."Currency Code", 'The currency code does not allign with the mock data.'); - Assert.AreEqual('2', EDocumentPurchaseHeader."Purchase Order No.", 'The purchase order number does not allign with the mock data.'); + Assert.AreEqual(SalesInvoiceNoTok, EDocumentPurchaseHeader."Sales Invoice No.", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Sales Invoice No."), EDocumentPurchaseHeader.TableCaption, SalesInvoiceNoTok, EDocumentPurchaseHeader."Sales Invoice No.")); + Assert.AreEqual(MockDate, EDocumentPurchaseHeader."Document Date", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Document Date"), EDocumentPurchaseHeader.TableCaption, MockDate, EDocumentPurchaseHeader."Document Date")); + Assert.AreEqual(CalcDate('<+1M>', MockDate), EDocumentPurchaseHeader."Due Date", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Due Date"), EDocumentPurchaseHeader.TableCaption, CalcDate('<+1M>', MockDate), EDocumentPurchaseHeader."Due Date")); + Assert.AreEqual(MockCurrencyCode, EDocumentPurchaseHeader."Currency Code", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Currency Code"), EDocumentPurchaseHeader.TableCaption, MockCurrencyCode, EDocumentPurchaseHeader."Currency Code")); + Assert.AreEqual(PurchaseorderNoTok, EDocumentPurchaseHeader."Purchase Order No.", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Purchase Order No."), EDocumentPurchaseHeader.TableCaption, PurchaseorderNoTok, EDocumentPurchaseHeader."Purchase Order No.")); // Assert.AreEqual('', EDocumentPurchaseHeader."Vendor GLN", 'The endpoint schema is not provided to populate the GLN.'); - Assert.AreEqual('CRONUS International', EDocumentPurchaseHeader."Vendor Company Name", 'The vendor name does not allign with the mock data.'); - Assert.AreEqual('Main Street, 14', EDocumentPurchaseHeader."Vendor Address", 'The vendor street does not allign with the mock data.'); - Assert.AreEqual('GB123456789', EDocumentPurchaseHeader."Vendor VAT Id", 'The vendor VAT id does not allign with the mock data.'); - Assert.AreEqual('Jim Olive', EDocumentPurchaseHeader."Vendor Contact Name", 'The vendor contact name does not allign with the mock data.'); - Assert.AreEqual('The Cannon Group PLC', EDocumentPurchaseHeader."Customer Company Name", 'The customer name does not allign with the mock data.'); - Assert.AreEqual('GB789456278', EDocumentPurchaseHeader."Customer VAT Id", 'The customer VAT id does not allign with the mock data.'); - Assert.AreEqual('192 Market Square', EDocumentPurchaseHeader."Customer Address", 'The customer address does not allign with the mock data.'); + Assert.AreEqual('CRONUS International', EDocumentPurchaseHeader."Vendor Company Name", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Vendor Company Name"), EDocumentPurchaseHeader.TableCaption, 'CRONUS International', EDocumentPurchaseHeader."Vendor Company Name")); + Assert.AreEqual('Main Street, 14', EDocumentPurchaseHeader."Vendor Address", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Vendor Address"), EDocumentPurchaseHeader.TableCaption, 'Main Street, 14', EDocumentPurchaseHeader."Vendor Address")); + Assert.AreEqual('GB123456789', EDocumentPurchaseHeader."Vendor VAT Id", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Vendor VAT Id"), EDocumentPurchaseHeader.TableCaption, 'GB123456789', EDocumentPurchaseHeader."Vendor VAT Id")); + Assert.AreEqual('Jim Olive', EDocumentPurchaseHeader."Vendor Contact Name", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Vendor Contact Name"), EDocumentPurchaseHeader.TableCaption, 'Jim Olive', EDocumentPurchaseHeader."Vendor Contact Name")); + Assert.AreEqual('The Cannon Group PLC', EDocumentPurchaseHeader."Customer Company Name", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Customer Company Name"), EDocumentPurchaseHeader.TableCaption, 'The Cannon Group PLC', EDocumentPurchaseHeader."Customer Company Name")); + Assert.AreEqual('GB789456278', EDocumentPurchaseHeader."Customer VAT Id", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Customer VAT Id"), EDocumentPurchaseHeader.TableCaption, 'GB789456278', EDocumentPurchaseHeader."Customer VAT Id")); + Assert.AreEqual('192 Market Square', EDocumentPurchaseHeader."Customer Address", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Customer Address"), EDocumentPurchaseHeader.TableCaption, '192 Market Square', EDocumentPurchaseHeader."Customer Address")); EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocumentEntryNo); EDocumentPurchaseLine.FindSet(); - Assert.AreEqual(1, EDocumentPurchaseLine."Quantity", 'The quantity in the purchase line does not allign with the mock data.'); - Assert.AreEqual('PCS', EDocumentPurchaseLine."Unit of Measure", 'The unit of measure in the purchase line does not allign with the mock data.'); - Assert.AreEqual(4000, EDocumentPurchaseLine."Sub Total", 'The total amount before taxes in the purchase line does not allign with the mock data.'); - Assert.AreEqual('XYZ', EDocumentPurchaseLine."Currency Code", 'The currency code in the purchase line does not allign with the mock data.'); - Assert.AreEqual(0, EDocumentPurchaseLine."Total Discount", 'The total discount in the purchase line does not allign with the mock data.'); - Assert.AreEqual('Bicycle', EDocumentPurchaseLine.Description, 'The product description in the purchase line does not allign with the mock data.'); - Assert.AreEqual('1000', EDocumentPurchaseLine."Product Code", 'The product code in the purchase line does not allign with the mock data.'); - Assert.AreEqual(25, EDocumentPurchaseLine."VAT Rate", 'The VAT rate in the purchase line does not allign with the mock data.'); - Assert.AreEqual(4000, EDocumentPurchaseLine."Unit Price", 'The unit price in the purchase line does not allign with the mock data.'); + Assert.AreEqual(1, EDocumentPurchaseLine."Quantity", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Quantity"), EDocumentPurchaseLine.TableCaption, 1, EDocumentPurchaseLine."Quantity")); + Assert.AreEqual(UnitOfMeasureCodeTok, EDocumentPurchaseLine."Unit of Measure", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Unit of Measure"), EDocumentPurchaseLine.TableCaption, UnitOfMeasureCodeTok, EDocumentPurchaseLine."Unit of Measure")); + Assert.AreEqual(4000, EDocumentPurchaseLine."Sub Total", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Sub Total"), EDocumentPurchaseLine.TableCaption, 4000, EDocumentPurchaseLine."Sub Total")); + Assert.AreEqual(MockCurrencyCode, EDocumentPurchaseLine."Currency Code", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Currency Code"), EDocumentPurchaseLine.TableCaption, MockCurrencyCode, EDocumentPurchaseLine."Currency Code")); + Assert.AreEqual(0, EDocumentPurchaseLine."Total Discount", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Total Discount"), EDocumentPurchaseLine.TableCaption, 0, EDocumentPurchaseLine."Total Discount")); + Assert.AreEqual('Bicycle', EDocumentPurchaseLine.Description, StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption(Description), EDocumentPurchaseLine.TableCaption, 'Bicycle', EDocumentPurchaseLine.Description)); + Assert.AreEqual('1000', EDocumentPurchaseLine."Product Code", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Product Code"), EDocumentPurchaseLine.TableCaption, '1000', EDocumentPurchaseLine."Product Code")); + Assert.AreEqual(25, EDocumentPurchaseLine."VAT Rate", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("VAT Rate"), EDocumentPurchaseLine.TableCaption, 25, EDocumentPurchaseLine."VAT Rate")); + Assert.AreEqual(4000, EDocumentPurchaseLine."Unit Price", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Unit Price"), EDocumentPurchaseLine.TableCaption, 4000, EDocumentPurchaseLine."Unit Price")); EDocumentPurchaseLine.Next(); - Assert.AreEqual(2, EDocumentPurchaseLine."Quantity", 'The quantity in the purchase line does not allign with the mock data.'); - Assert.AreEqual('PCS', EDocumentPurchaseLine."Unit of Measure", 'The unit of measure in the purchase line does not allign with the mock data.'); - Assert.AreEqual(10000, EDocumentPurchaseLine."Sub Total", 'The total amount before taxes in the purchase line does not allign with the mock data.'); - Assert.AreEqual('XYZ', EDocumentPurchaseLine."Currency Code", 'The currency code in the purchase line does not allign with the mock data.'); - Assert.AreEqual(0, EDocumentPurchaseLine."Total Discount", 'The total discount in the purchase line does not allign with the mock data.'); - Assert.AreEqual('Bicycle v2', EDocumentPurchaseLine.Description, 'The product description in the purchase line does not allign with the mock data.'); - Assert.AreEqual('2000', EDocumentPurchaseLine."Product Code", 'The product code in the purchase line does not allign with the mock data.'); - Assert.AreEqual(25, EDocumentPurchaseLine."VAT Rate", 'The VAT rate in the purchase line does not allign with the mock data.'); - Assert.AreEqual(5000, EDocumentPurchaseLine."Unit Price", 'The unit price in the purchase line does not allign with the mock data.'); + Assert.AreEqual(2, EDocumentPurchaseLine."Quantity", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Quantity"), EDocumentPurchaseLine.TableCaption, 2, EDocumentPurchaseLine."Quantity")); + Assert.AreEqual(UnitOfMeasureCodeTok, EDocumentPurchaseLine."Unit of Measure", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Unit of Measure"), EDocumentPurchaseLine.TableCaption, UnitOfMeasureCodeTok, EDocumentPurchaseLine."Unit of Measure")); + Assert.AreEqual(10000, EDocumentPurchaseLine."Sub Total", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Sub Total"), EDocumentPurchaseLine.TableCaption, 10000, EDocumentPurchaseLine."Sub Total")); + Assert.AreEqual(MockCurrencyCode, EDocumentPurchaseLine."Currency Code", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Currency Code"), EDocumentPurchaseLine.TableCaption, MockCurrencyCode, EDocumentPurchaseLine."Currency Code")); + Assert.AreEqual(0, EDocumentPurchaseLine."Total Discount", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Total Discount"), EDocumentPurchaseLine.TableCaption, 0, EDocumentPurchaseLine."Total Discount")); + Assert.AreEqual('Bicycle v2', EDocumentPurchaseLine.Description, StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption(Description), EDocumentPurchaseLine.TableCaption, 'Bicycle v2', EDocumentPurchaseLine.Description)); + Assert.AreEqual('2000', EDocumentPurchaseLine."Product Code", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Product Code"), EDocumentPurchaseLine.TableCaption, '2000', EDocumentPurchaseLine."Product Code")); + Assert.AreEqual(25, EDocumentPurchaseLine."VAT Rate", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("VAT Rate"), EDocumentPurchaseLine.TableCaption, 25, EDocumentPurchaseLine."VAT Rate")); + Assert.AreEqual(5000, EDocumentPurchaseLine."Unit Price", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Unit Price"), EDocumentPurchaseLine.TableCaption, 5000, EDocumentPurchaseLine."Unit Price")); + end; + + internal procedure AssertPurchaseHeader(VendorNo: Code[20]; PurchaseHeader: Record "Purchase Header") + var + PurchaseLine: Record "Purchase Line"; + Item1No: Label 'GL00000001', Locked = true; + Item2No: Label 'GL00000003', Locked = true; + begin + Assert.AreEqual(SalesInvoiceNoTok, PurchaseHeader."Vendor Invoice No.", StrSubstNo(MockDataMismatchLbl, PurchaseHeader.FieldCaption("Vendor Invoice No."), PurchaseHeader.TableCaption, SalesInvoiceNoTok, PurchaseHeader."Vendor Invoice No.")); + Assert.AreEqual(MockDate, PurchaseHeader."Document Date", StrSubstNo(MockDataMismatchLbl, PurchaseHeader.FieldCaption("Document Date"), PurchaseHeader.TableCaption, MockDate, PurchaseHeader."Document Date")); + Assert.AreEqual(CalcDate('<+1M>', MockDate), PurchaseHeader."Due Date", StrSubstNo(MockDataMismatchLbl, PurchaseHeader.FieldCaption("Due Date"), PurchaseHeader.TableCaption, CalcDate('<+1M>', MockDate), PurchaseHeader."Due Date")); + Assert.AreEqual(MockCurrencyCode, PurchaseHeader."Currency Code", StrSubstNo(MockDataMismatchLbl, PurchaseHeader.FieldCaption("Currency Code"), PurchaseHeader.TableCaption, MockCurrencyCode, PurchaseHeader."Currency Code")); + Assert.AreEqual(PurchaseorderNoTok, PurchaseHeader."Vendor Order No.", StrSubstNo(MockDataMismatchLbl, PurchaseHeader.FieldCaption("Vendor Order No."), PurchaseHeader.TableCaption, PurchaseorderNoTok, PurchaseHeader."Vendor Order No.")); + Assert.AreEqual(VendorNo, PurchaseHeader."Buy-from Vendor No.", StrSubstNo(MockDataMismatchLbl, PurchaseHeader.FieldCaption("Buy-from Vendor No."), PurchaseHeader.TableCaption, VendorNo, PurchaseHeader."Buy-from Vendor No.")); + PurchaseLine.SetRange("Document Type", PurchaseHeader."Document Type"); + PurchaseLine.SetRange("Document No.", PurchaseHeader."No."); + PurchaseLine.FindSet(); + Assert.AreEqual(1, PurchaseLine.Quantity, StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption(Quantity), PurchaseLine.TableCaption, 1, PurchaseLine.Quantity)); + Assert.AreEqual(4000, PurchaseLine."Direct Unit Cost", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("Direct Unit Cost"), PurchaseLine.TableCaption, 4000, PurchaseLine."Direct Unit Cost")); + Assert.AreEqual(MockCurrencyCode, PurchaseLine."Currency Code", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("Currency Code"), PurchaseLine.TableCaption, MockCurrencyCode, PurchaseLine."Currency Code")); + Assert.AreEqual(0, PurchaseLine."Line Discount Amount", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("Line Discount Amount"), PurchaseLine.TableCaption, 0, PurchaseLine."Line Discount Amount")); + // In the import file we have a name 'Bicycle' but because of Item Cross Reference validation Item description is being used + if Item."No." <> '' then begin + Assert.AreEqual('Bicycle', PurchaseLine.Description, StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption(Description), PurchaseLine.TableCaption, Item."No.", PurchaseLine.Description)); + Assert.AreEqual(Item."No.", PurchaseLine."No.", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("No."), PurchaseLine.TableCaption, Item."No.", PurchaseLine."No.")); + Assert.AreEqual(Item."Purch. Unit of Measure", PurchaseLine."Unit of Measure Code", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("Unit of Measure Code"), PurchaseLine.TableCaption, UnitOfMeasureCodeTok, PurchaseLine."Unit of Measure Code")); + end + else begin + Assert.AreEqual(Item1No, PurchaseLine.Description, StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption(Description), PurchaseLine.TableCaption, Item1No, PurchaseLine.Description)); + Assert.AreEqual(Item1No, PurchaseLine."No.", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("No."), PurchaseLine.TableCaption, Item1No, PurchaseLine."No.")); + Assert.AreEqual(UnitOfMeasureCodeTok, PurchaseLine."Unit of Measure Code", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("Unit of Measure Code"), PurchaseLine.TableCaption, UnitOfMeasureCodeTok, PurchaseLine."Unit of Measure Code")); + end; + + PurchaseLine.Next(); + Assert.AreEqual(2, PurchaseLine.Quantity, StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption(Quantity), PurchaseLine.TableCaption, 2, PurchaseLine.Quantity)); + Assert.AreEqual(UnitOfMeasureCodeTok, PurchaseLine."Unit of Measure Code", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("Unit of Measure Code"), PurchaseLine.TableCaption, UnitOfMeasureCodeTok, PurchaseLine."Unit of Measure Code")); + Assert.AreEqual(5000, PurchaseLine."Direct Unit Cost", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("Direct Unit Cost"), PurchaseLine.TableCaption, 5000, PurchaseLine."Direct Unit Cost")); + Assert.AreEqual(MockCurrencyCode, PurchaseLine."Currency Code", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("Currency Code"), PurchaseLine.TableCaption, MockCurrencyCode, PurchaseLine."Currency Code")); + Assert.AreEqual(0, PurchaseLine."Line Discount Amount", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("Line Discount Amount"), PurchaseLine.TableCaption, 0, PurchaseLine."Line Discount Amount")); + // In the import file we have a name 'Bicycle v2' but because of Item Cross Reference validation Item description is being used + Assert.AreEqual(Item2No, PurchaseLine.Description, StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption(Description), PurchaseLine.TableCaption, Item2No, PurchaseLine.Description)); + Assert.AreEqual(Item2No, PurchaseLine."No.", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("No."), PurchaseLine.TableCaption, Item2No, PurchaseLine."No.")); end; + internal procedure SetItem(Item: Record Item) + begin + this.Item := Item; + end; + + procedure SetMockDate(MockDate: Date) + begin + this.MockDate := MockDate; + end; + + procedure SetMockCurrencyCode(MockCurrencyCode: Code[10]) + begin + this.MockCurrencyCode := MockCurrencyCode; + end; } \ No newline at end of file From 91847770b5a4595a171fa305751fb7752cde5d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Wed, 13 Aug 2025 16:29:57 +0300 Subject: [PATCH 12/28] Refactors purchase document creation logic Consolidates duplicate code between invoice and credit memo creation into a shared helper class to improve maintainability and consistency. Adds ResetDraft method to structured format readers for proper cleanup when undoing processing steps. Removes obsolete enum and simplifies unit of measure handling by moving default logic to item reference processing. Enhances credit memo creation with applies-to document support for better document linking. --- .../Import/EDocEmptyDraft.Codeunit.al | 20 ++ .../Import/EDocUnspecifiedImpl.Codeunit.al | 5 + .../EDocCreatePurchCrMemo.Codeunit.al | 158 +---------- .../FinishDraft/EDocCreatePurchCrMemo.Enum.al | 18 -- .../EDocCreatePurchaseInvoice.Codeunit.al | 161 ++---------- .../EDocCreatePurchaseInvoice.Enum.al | 4 +- .../EDocPurchaseDocumentHelper.Codeunit.al | 245 ++++++++++++++++++ .../Import/ImportEDocumentProcess.Codeunit.al | 6 +- .../PrepareDraft/EDocProviders.Codeunit.al | 29 +-- .../PreparePurchaseEDocDraft.Codeunit.al | 3 - .../Purchase/EDocumentPurchaseHeader.Table.al | 5 + .../EDocumentADIHandler.Codeunit.al | 20 ++ .../EDocumentPEPPOLHandler.Codeunit.al | 26 +- .../IStructuredFormatReader.Interface.al | 7 +- .../IUnitOfMeasureProvider.Interface.al | 8 - 15 files changed, 367 insertions(+), 348 deletions(-) delete mode 100644 Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Enum.al create mode 100644 Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocPurchaseDocumentHelper.Codeunit.al diff --git a/Apps/W1/EDocument/app/src/Processing/Import/EDocEmptyDraft.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/EDocEmptyDraft.Codeunit.al index 415229e663..109bbabbc9 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/EDocEmptyDraft.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/EDocEmptyDraft.Codeunit.al @@ -50,4 +50,24 @@ codeunit 6193 "E-Doc. Empty Draft" implements IStructureReceivedEDocument, IStru begin Error(NoDataErr); end; + + procedure ResetDraft(EDocument: Record "E-Document") + var + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + EDocumentHeaderMapping: Record "E-Document Header Mapping"; + EDocumentLineField: Record "E-Document Line - Field"; + begin + EDocumentPurchaseHeader.GetFromEDocument(EDocument); + EDocumentPurchaseHeader.Delete(true); + + EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocument."Entry No"); + EDocumentPurchaseLine.DeleteAll(true); + + EDocumentHeaderMapping.SetRange("E-Document Entry No.", EDocument."Entry No"); + EDocumentHeaderMapping.DeleteAll(true); + + EDocumentLineField.SetRange("E-Document Entry No.", EDocument."Entry No"); + EDocumentLineField.DeleteAll(true); + end; } \ No newline at end of file diff --git a/Apps/W1/EDocument/app/src/Processing/Import/EDocUnspecifiedImpl.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/EDocUnspecifiedImpl.Codeunit.al index a47c0c0896..1828308c1f 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/EDocUnspecifiedImpl.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/EDocUnspecifiedImpl.Codeunit.al @@ -61,6 +61,11 @@ codeunit 6116 "E-Doc. Unspecified Impl." implements IStructureReceivedEDocument, begin end; + procedure ResetDraft(EDocument: Record "E-Document") + begin + // No actions to undo + end; + var EDocumentNoReadSpecifiedErr: Label 'No method to read the e-document has been provided.'; } \ No newline at end of file diff --git a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al index 7eed98fa1a..c83e29b1fb 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al @@ -8,12 +8,7 @@ using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument.Processing.Interfaces; using Microsoft.Finance.Dimension; -using Microsoft.Finance.GeneralLedger.Setup; -using Microsoft.Foundation.Attachment; using Microsoft.Purchases.Document; -using Microsoft.Purchases.Payables; -using Microsoft.Purchases.Posting; -using System.Telemetry; /// /// Dealing with the creation of the purchase credit memo after the draft has been populated. @@ -25,10 +20,7 @@ codeunit 6105 "E-Doc. Create Purch. Cr. Memo" implements IEDocumentFinishDraft, tabledata "Dimension Set Entry" = im; var - Telemetry: Codeunit "Telemetry"; EDocImpSessionTelemetry: Codeunit "E-Doc. Imp. Session Telemetry"; - CreditMemoAlreadyExistsErr: Label 'A purchase credit memo with external document number %1 already exists for vendor %2.', Comment = '%1 = Vendor Credit Memo No., %2 = Vendor No.'; - DraftLineDoesNotConstantTypeAndNumberErr: Label 'One of the draft lines do not contain the type and number. Please, specify these fields manually.'; /// /// Applies the draft E-Document to Business Central by creating a purchase credit memo from the draft data. @@ -40,6 +32,7 @@ codeunit 6105 "E-Doc. Create Purch. Cr. Memo" implements IEDocumentFinishDraft, var EDocumentPurchaseHeader: Record "E-Document Purchase Header"; PurchaseHeader: Record "Purchase Header"; + EDocPurchaseDocumentHelper: Codeunit "E-Doc Purchase Document Helper"; IEDocumentFinishPurchaseDraft: Interface IEDocumentCreatePurchaseCreditMemo; begin EDocumentPurchaseHeader.GetFromEDocument(EDocument); @@ -47,7 +40,7 @@ codeunit 6105 "E-Doc. Create Purch. Cr. Memo" implements IEDocumentFinishDraft, PurchaseHeader := IEDocumentFinishPurchaseDraft.CreatePurchaseCreditMemo(EDocument); // Post document validation - Silently emit telemetry - if not TryValidateDocumentTotals(PurchaseHeader) then + if not EDocPurchaseDocumentHelper.TryValidateDocumentTotals(PurchaseHeader) then EDocImpSessionTelemetry.SetBool('Totals Validation Failed', true); exit(PurchaseHeader.RecordId); @@ -64,7 +57,7 @@ codeunit 6105 "E-Doc. Create Purch. Cr. Memo" implements IEDocumentFinishDraft, PurchaseHeader.SetRange("E-Document Link", EDocument.SystemId); if not PurchaseHeader.FindFirst() then exit; - + PurchaseHeader.TestField("Document Type", "Purchase Document Type"::"Credit Memo"); Clear(PurchaseHeader."E-Document Link"); PurchaseHeader.Delete(true); @@ -75,150 +68,11 @@ codeunit 6105 "E-Doc. Create Purch. Cr. Memo" implements IEDocumentFinishDraft, /// /// The E-Document record containing the draft data to create the credit memo from. /// The created purchase header record for the credit memo. - internal procedure CreatePurchaseCreditMemo(EDocument: Record "E-Document"): Record "Purchase Header" + internal procedure CreatePurchaseCreditMemo(EDocument: Record "E-Document") PurchaseHeader: Record "Purchase Header" var - PurchaseHeader: Record "Purchase Header"; - EDocumentPurchaseHeader: Record "E-Document Purchase Header"; - EDocumentPurchaseLine: Record "E-Document Purchase Line"; - EDocumentPurchaseHistMapping: Codeunit "E-Doc. Purchase Hist. Mapping"; + EDocPurchaseDocumentHelper: Codeunit "E-Doc Purchase Document Helper"; begin - EDocumentPurchaseHeader.GetFromEDocument(EDocument); - if not AllDraftLinesHaveTypeAndNumberSpecificed(EDocumentPurchaseHeader) then begin - Telemetry.LogMessage('0000PLZ', 'Draft line does not contain type or number', Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::All); - Error(DraftLineDoesNotConstantTypeAndNumberErr); - end; - EDocumentPurchaseHeader.TestField("E-Document Entry No."); - CreatePurchaseHeader(EDocument, PurchaseHeader, EDocumentPurchaseHeader, EDocumentPurchaseHistMapping); - - EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocument."Entry No"); - if EDocumentPurchaseLine.FindSet() then - repeat - CreatePurchaseLine(EDocument, PurchaseHeader, EDocumentPurchaseLine, EDocumentPurchaseHistMapping); - until EDocumentPurchaseLine.Next() = 0; + PurchaseHeader := EDocPurchaseDocumentHelper.CreatePurchaseDocumentHeader(EDocument, "Purchase Document Type"::"Credit Memo"); exit(PurchaseHeader); end; - - [TryFunction] - local procedure TryValidateDocumentTotals(PurchaseHeader: Record "Purchase Header") - var - PurchPost: Codeunit "Purch.-Post"; - begin - // If document totals are setup, we just run the validation - PurchPost.CheckDocumentTotalAmounts(PurchaseHeader); - end; - - local procedure AllDraftLinesHaveTypeAndNumberSpecificed(EDocumentPurchaseHeader: Record "E-Document Purchase Header"): Boolean - var - EDocumentPurchaseLine: Record "E-Document Purchase Line"; - begin - EDocumentPurchaseLine.SetLoadFields("[BC] Purchase Line Type", "[BC] Purchase Type No."); - EDocumentPurchaseLine.ReadIsolation(IsolationLevel::ReadCommitted); - EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocumentPurchaseHeader."E-Document Entry No."); - if not EDocumentPurchaseLine.FindSet() then - exit(true); - repeat - if EDocumentPurchaseLine."[BC] Purchase Line Type" = EDocumentPurchaseLine."[BC] Purchase Line Type"::" " then - exit(false); - if EDocumentPurchaseLine."[BC] Purchase Type No." = '' then - exit(false); - until EDocumentPurchaseLine.Next() = 0; - exit(true); - end; - - local procedure CreatePurchaseHeader(EDocument: Record "E-Document"; var PurchaseHeader: Record "Purchase Header"; EDocumentPurchaseHeader: Record "E-Document Purchase Header"; var EDocumentPurchaseHistMapping: Codeunit "E-Doc. Purchase Hist. Mapping") - var - GLSetup: Record "General Ledger Setup"; - VendorLedgerEntry: Record "Vendor Ledger Entry"; - DocumentAttachmentMgt: Codeunit "Document Attachment Mgmt"; - StopCreatingPurchaseCreditMemo: Boolean; - VendorCreditMemoNo: Code[35]; - begin - PurchaseHeader.Validate("Document Type", "Purchase Document Type"::"Credit Memo"); - PurchaseHeader.Validate("Buy-from Vendor No.", EDocumentPurchaseHeader."[BC] Vendor No."); - - VendorCreditMemoNo := CopyStr(EDocumentPurchaseHeader."Sales Invoice No.", 1, MaxStrLen(PurchaseHeader."Vendor Cr. Memo No.")); - VendorLedgerEntry.SetLoadFields("Entry No."); - VendorLedgerEntry.ReadIsolation := VendorLedgerEntry.ReadIsolation::ReadUncommitted; - StopCreatingPurchaseCreditMemo := PurchaseHeader.FindPostedDocumentWithSameExternalDocNo(VendorLedgerEntry, VendorCreditMemoNo); - if StopCreatingPurchaseCreditMemo then begin - Telemetry.LogMessage('0000PHD', CreditMemoAlreadyExistsErr, Verbosity::Error, DataClassification::OrganizationIdentifiableInformation, TelemetryScope::All); - Error(CreditMemoAlreadyExistsErr, VendorCreditMemoNo, EDocumentPurchaseHeader."[BC] Vendor No."); - end; - - PurchaseHeader.Validate("Vendor Cr. Memo No.", VendorCreditMemoNo); - PurchaseHeader.Validate("Vendor Order No.", EDocumentPurchaseHeader."Purchase Order No."); - PurchaseHeader.Insert(true); - - if EDocumentPurchaseHeader."Document Date" <> 0D then - PurchaseHeader.Validate("Document Date", EDocumentPurchaseHeader."Document Date"); - if EDocumentPurchaseHeader."Due Date" <> 0D then - PurchaseHeader.Validate("Due Date", EDocumentPurchaseHeader."Due Date"); - PurchaseHeader.Modify(false); - - // Validate of currency has to happen after insert. - GLSetup.GetRecordOnce(); - if EDocumentPurchaseHeader."Currency Code" <> GLSetup.GetCurrencyCode('') then begin - PurchaseHeader.Validate("Currency Code", EDocumentPurchaseHeader."Currency Code"); - PurchaseHeader.Modify(false); - end; - - // Track changes for history - EDocumentPurchaseHistMapping.TrackRecord(EDocument, EDocumentPurchaseHeader, PurchaseHeader); - - PurchaseHeader.SetRecFilter(); - PurchaseHeader.FindFirst(); - PurchaseHeader."Doc. Amount Incl. VAT" := EDocumentPurchaseHeader.Total; - PurchaseHeader."Doc. Amount VAT" := EDocumentPurchaseHeader."Total VAT"; - PurchaseHeader.TestField("Document Type", "Purchase Document Type"::"Credit Memo"); - PurchaseHeader.TestField("No."); - PurchaseHeader."E-Document Link" := EDocument.SystemId; - PurchaseHeader.Modify(false); - - // Post document creation - DocumentAttachmentMgt.CopyAttachments(EDocument, PurchaseHeader); - DocumentAttachmentMgt.DeleteAttachedDocuments(EDocument); - end; - - local procedure CreatePurchaseLine(EDocument: Record "E-Document"; PurchaseHeader: Record "Purchase Header"; EDocumentPurchaseLine: Record "E-Document Purchase Line"; var EDocumentPurchaseHistMapping: Codeunit "E-Doc. Purchase Hist. Mapping") - var - PurchaseLine: Record "Purchase Line"; - DimensionManagement: Codeunit DimensionManagement; - PurchaseLineCombinedDimensions: array[10] of Integer; - GlobalDim1: Code[20]; - GlobalDim2: Code[20]; - begin - PurchaseLine.SetRange("Document Type", PurchaseHeader."Document Type"); - PurchaseLine.SetRange("Document No.", PurchaseHeader."No."); - if PurchaseLine.FindLast() then; - - PurchaseLine."Document Type" := PurchaseHeader."Document Type"; - PurchaseLine."Document No." := PurchaseHeader."No."; - PurchaseLine."Line No." += 10000; - PurchaseLine."Unit of Measure Code" := CopyStr(EDocumentPurchaseLine."[BC] Unit of Measure", 1, MaxStrLen(PurchaseLine."Unit of Measure Code")); - PurchaseLine."Variant Code" := EDocumentPurchaseLine."[BC] Variant Code"; - PurchaseLine.Type := EDocumentPurchaseLine."[BC] Purchase Line Type"; - PurchaseLine.Validate("No.", EDocumentPurchaseLine."[BC] Purchase Type No."); - PurchaseLine.Description := EDocumentPurchaseLine.Description; - - if EDocumentPurchaseLine."[BC] Item Reference No." <> '' then - PurchaseLine.Validate("Item Reference No.", EDocumentPurchaseLine."[BC] Item Reference No."); - - PurchaseLine.Validate(Quantity, EDocumentPurchaseLine.Quantity); - PurchaseLine.Validate("Direct Unit Cost", EDocumentPurchaseLine."Unit Price"); - if EDocumentPurchaseLine."Total Discount" > 0 then - PurchaseLine.Validate("Line Discount Amount", EDocumentPurchaseLine."Total Discount"); - PurchaseLine.Validate("Deferral Code", EDocumentPurchaseLine."[BC] Deferral Code"); - - Clear(PurchaseLineCombinedDimensions); - PurchaseLineCombinedDimensions[1] := PurchaseLine."Dimension Set ID"; - PurchaseLineCombinedDimensions[2] := EDocumentPurchaseLine."[BC] Dimension Set ID"; - PurchaseLine.Validate("Dimension Set ID", DimensionManagement.GetCombinedDimensionSetID(PurchaseLineCombinedDimensions, GlobalDim1, GlobalDim2)); - PurchaseLine.Validate("Shortcut Dimension 1 Code", EDocumentPurchaseLine."[BC] Shortcut Dimension 1 Code"); - PurchaseLine.Validate("Shortcut Dimension 2 Code", EDocumentPurchaseLine."[BC] Shortcut Dimension 2 Code"); - EDocumentPurchaseHistMapping.ApplyHistoryValuesToPurchaseLine(EDocumentPurchaseLine, PurchaseLine); - PurchaseLine.Insert(false); - - // Track changes for history - EDocumentPurchaseHistMapping.TrackRecord(EDocument, EDocumentPurchaseLine, PurchaseLine); - end; } diff --git a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Enum.al b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Enum.al deleted file mode 100644 index ed880f70a9..0000000000 --- a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Enum.al +++ /dev/null @@ -1,18 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// 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.Processing.Import; - -using Microsoft.eServices.EDocument.Processing.Interfaces; - -/// -/// Enum for the implementations of the E-Doc. Create Purchase Credit Memo interface. -/// -enum 6118 "E-Doc. Create Purch. Cr. Memo" implements IEDocumentCreatePurchaseCreditMemo -{ - Extensible = true; - DefaultImplementation = IEDocumentCreatePurchaseCreditMemo = "E-Doc. Create Purch. Cr. Memo"; - - value(0; "Default") { } -} diff --git a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al index 3810a92c7c..54bcb516af 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al @@ -5,15 +5,10 @@ namespace Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument; +using Microsoft.eServices.EDocument.Processing.Import.Purchase; +using Microsoft.eServices.EDocument.Processing.Interfaces; using Microsoft.Finance.Dimension; using Microsoft.Purchases.Document; -using Microsoft.eServices.EDocument.Processing.Interfaces; -using Microsoft.eServices.EDocument.Processing.Import.Purchase; -using Microsoft.Finance.GeneralLedger.Setup; -using Microsoft.Purchases.Payables; -using Microsoft.Purchases.Posting; -using System.Telemetry; -using Microsoft.Foundation.Attachment; /// /// Dealing with the creation of the purchase invoice after the draft has been populated. @@ -25,40 +20,35 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft, tabledata "Dimension Set Entry" = im; var - Telemetry: Codeunit "Telemetry"; EDocImpSessionTelemetry: Codeunit "E-Doc. Imp. Session Telemetry"; - InvoiceAlreadyExistsErr: Label 'A purchase invoice with external document number %1 already exists for vendor %2.', Comment = '%1 = Vendor Invoice No., %2 = Vendor No.'; - DraftLineDoesNotConstantTypeAndNumberErr: Label 'One of the draft lines do not contain the type and number. Please, specify these fields manually.'; + /// + /// Applies the draft E-Document to Business Central by creating a purchase invoice from the draft data. + /// + /// The E-Document record containing the draft data to be applied. + /// The import parameters containing processing customizations. + /// The RecordId of the created purchase invoice. procedure ApplyDraftToBC(EDocument: Record "E-Document"; EDocImportParameters: Record "E-Doc. Import Parameters"): RecordId var EDocumentPurchaseHeader: Record "E-Document Purchase Header"; PurchaseHeader: Record "Purchase Header"; - DocumentAttachmentMgt: Codeunit "Document Attachment Mgmt"; + EDocPurchaseDocumentHelper: Codeunit "E-Doc Purchase Document Helper"; IEDocumentFinishPurchaseDraft: Interface IEDocumentCreatePurchaseInvoice; begin EDocumentPurchaseHeader.GetFromEDocument(EDocument); IEDocumentFinishPurchaseDraft := EDocImportParameters."Processing Customizations"; PurchaseHeader := IEDocumentFinishPurchaseDraft.CreatePurchaseInvoice(EDocument); - PurchaseHeader.SetRecFilter(); - PurchaseHeader.FindFirst(); - PurchaseHeader."Doc. Amount Incl. VAT" := EDocumentPurchaseHeader.Total; - PurchaseHeader."Doc. Amount VAT" := EDocumentPurchaseHeader."Total VAT"; - PurchaseHeader.TestField("Document Type", "Purchase Document Type"::Invoice); - PurchaseHeader.TestField("No."); - PurchaseHeader."E-Document Link" := EDocument.SystemId; - PurchaseHeader.Modify(); - - // Post document creation - DocumentAttachmentMgt.CopyAttachments(EDocument, PurchaseHeader); - DocumentAttachmentMgt.DeleteAttachedDocuments(EDocument); // Post document validation - Silently emit telemetry - EDocImpSessionTelemetry.SetBool('Totals Validation', TryValidateDocumentTotals(PurchaseHeader)); + EDocImpSessionTelemetry.SetBool('Totals Validation', EDocPurchaseDocumentHelper.TryValidateDocumentTotals(PurchaseHeader)); exit(PurchaseHeader.RecordId); end; + /// + /// Reverts the draft actions by deleting the associated purchase invoice document. + /// + /// The E-Document record whose draft actions should be reverted. procedure RevertDraftActions(EDocument: Record "E-Document") var PurchaseHeader: Record "Purchase Header"; @@ -71,123 +61,16 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft, PurchaseHeader.Delete(true); end; - procedure CreatePurchaseInvoice(EDocument: Record "E-Document"): Record "Purchase Header" + /// + /// Creates a purchase invoice from E-Document draft data, including header and line information. + /// + /// The E-Document record containing the draft data to create the invoice from. + /// The created purchase header record for the invoice. + procedure CreatePurchaseInvoice(EDocument: Record "E-Document") PurchaseHeader: Record "Purchase Header" var - PurchaseHeader: Record "Purchase Header"; - GLSetup: Record "General Ledger Setup"; - VendorLedgerEntry: Record "Vendor Ledger Entry"; - EDocumentPurchaseHeader: Record "E-Document Purchase Header"; - EDocumentPurchaseLine: Record "E-Document Purchase Line"; - PurchaseLine: Record "Purchase Line"; - EDocumentPurchaseHistMapping: Codeunit "E-Doc. Purchase Hist. Mapping"; - DimensionManagement: Codeunit DimensionManagement; - PurchaseLineCombinedDimensions: array[10] of Integer; - StopCreatingPurchaseInvoice: Boolean; - VendorInvoiceNo: Code[35]; - GlobalDim1, GlobalDim2 : Code[20]; + EDocPurchaseDocumentHelper: Codeunit "E-Doc Purchase Document Helper"; begin - EDocumentPurchaseHeader.GetFromEDocument(EDocument); - if not AllDraftLinesHaveTypeAndNumberSpecificed(EDocumentPurchaseHeader) then begin - Telemetry.LogMessage('0000PLY', 'Draft line does not contain type or number', Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::All); - Error(DraftLineDoesNotConstantTypeAndNumberErr); - end; - EDocumentPurchaseHeader.TestField("E-Document Entry No."); - PurchaseHeader.Validate("Document Type", "Purchase Document Type"::Invoice); - PurchaseHeader.Validate("Buy-from Vendor No.", EDocumentPurchaseHeader."[BC] Vendor No."); - - VendorInvoiceNo := CopyStr(EDocumentPurchaseHeader."Sales Invoice No.", 1, MaxStrLen(PurchaseHeader."Vendor Invoice No.")); - VendorLedgerEntry.SetLoadFields("Entry No."); - VendorLedgerEntry.ReadIsolation := VendorLedgerEntry.ReadIsolation::ReadUncommitted; - StopCreatingPurchaseInvoice := PurchaseHeader.FindPostedDocumentWithSameExternalDocNo(VendorLedgerEntry, VendorInvoiceNo); - if StopCreatingPurchaseInvoice then begin - Telemetry.LogMessage('0000PHC', InvoiceAlreadyExistsErr, Verbosity::Error, DataClassification::OrganizationIdentifiableInformation, TelemetryScope::All); - Error(InvoiceAlreadyExistsErr, VendorInvoiceNo, EDocumentPurchaseHeader."[BC] Vendor No."); - end; - - PurchaseHeader.Validate("Vendor Invoice No.", VendorInvoiceNo); - PurchaseHeader.Validate("Vendor Order No.", EDocumentPurchaseHeader."Purchase Order No."); - PurchaseHeader.Insert(true); - - if EDocumentPurchaseHeader."Document Date" <> 0D then - PurchaseHeader.Validate("Document Date", EDocumentPurchaseHeader."Document Date"); - if EDocumentPurchaseHeader."Due Date" <> 0D then - PurchaseHeader.Validate("Due Date", EDocumentPurchaseHeader."Due Date"); - PurchaseHeader."Invoice Received Date" := PurchaseHeader."Document Date"; - PurchaseHeader.Modify(); - - // Validate of currency has to happen after insert. - GLSetup.GetRecordOnce(); - if EDocumentPurchaseHeader."Currency Code" <> GLSetup.GetCurrencyCode('') then begin - PurchaseHeader.Validate("Currency Code", EDocumentPurchaseHeader."Currency Code"); - PurchaseHeader.Modify(); - end; - - // Track changes for history - EDocumentPurchaseHistMapping.TrackRecord(EDocument, EDocumentPurchaseHeader, PurchaseHeader); - - EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocument."Entry No"); - if EDocumentPurchaseLine.FindSet() then - repeat - PurchaseLine."Document Type" := PurchaseHeader."Document Type"; - PurchaseLine."Document No." := PurchaseHeader."No."; - PurchaseLine."Line No." += 10000; - PurchaseLine."Unit of Measure Code" := CopyStr(EDocumentPurchaseLine."[BC] Unit of Measure", 1, MaxStrLen(PurchaseLine."Unit of Measure Code")); - PurchaseLine."Variant Code" := EDocumentPurchaseLine."[BC] Variant Code"; - PurchaseLine.Type := EDocumentPurchaseLine."[BC] Purchase Line Type"; - PurchaseLine.Validate("No.", EDocumentPurchaseLine."[BC] Purchase Type No."); - PurchaseLine.Description := EDocumentPurchaseLine.Description; - - if EDocumentPurchaseLine."[BC] Item Reference No." <> '' then - PurchaseLine.Validate("Item Reference No.", EDocumentPurchaseLine."[BC] Item Reference No."); - - PurchaseLine.Validate(Quantity, EDocumentPurchaseLine.Quantity); - PurchaseLine.Validate("Direct Unit Cost", EDocumentPurchaseLine."Unit Price"); - if EDocumentPurchaseLine."Total Discount" > 0 then - PurchaseLine.Validate("Line Discount Amount", EDocumentPurchaseLine."Total Discount"); - PurchaseLine.Validate("Deferral Code", EDocumentPurchaseLine."[BC] Deferral Code"); - - Clear(PurchaseLineCombinedDimensions); - PurchaseLineCombinedDimensions[1] := PurchaseLine."Dimension Set ID"; - PurchaseLineCombinedDimensions[2] := EDocumentPurchaseLine."[BC] Dimension Set ID"; - PurchaseLine.Validate("Dimension Set ID", DimensionManagement.GetCombinedDimensionSetID(PurchaseLineCombinedDimensions, GlobalDim1, GlobalDim2)); - PurchaseLine.Validate("Shortcut Dimension 1 Code", EDocumentPurchaseLine."[BC] Shortcut Dimension 1 Code"); - PurchaseLine.Validate("Shortcut Dimension 2 Code", EDocumentPurchaseLine."[BC] Shortcut Dimension 2 Code"); - EDocumentPurchaseHistMapping.ApplyHistoryValuesToPurchaseLine(EDocumentPurchaseLine, PurchaseLine); - PurchaseLine.Insert(); - - // Track changes for history - EDocumentPurchaseHistMapping.TrackRecord(EDocument, EDocumentPurchaseLine, PurchaseLine); - - until EDocumentPurchaseLine.Next() = 0; - + PurchaseHeader := EDocPurchaseDocumentHelper.CreatePurchaseDocumentHeader(EDocument, "Purchase Document Type"::Invoice); exit(PurchaseHeader); - - end; - - [TryFunction] - local procedure TryValidateDocumentTotals(PurchaseHeader: Record "Purchase Header") - var - PurchPost: Codeunit "Purch.-Post"; - begin - // If document totals are setup, we just run the validation - PurchPost.CheckDocumentTotalAmounts(PurchaseHeader); - end; - - local procedure AllDraftLinesHaveTypeAndNumberSpecificed(EDocumentPurchaseHeader: Record "E-Document Purchase Header"): Boolean - var - EDocumentPurchaseLine: Record "E-Document Purchase Line"; - begin - EDocumentPurchaseLine.SetLoadFields("[BC] Purchase Line Type", "[BC] Purchase Type No."); - EDocumentPurchaseLine.ReadIsolation(IsolationLevel::ReadCommitted); - EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocumentPurchaseHeader."E-Document Entry No."); - if not EDocumentPurchaseLine.FindSet() then - exit(true); - repeat - if EDocumentPurchaseLine."[BC] Purchase Line Type" = EDocumentPurchaseLine."[BC] Purchase Line Type"::" " then - exit(false); - if EDocumentPurchaseLine."[BC] Purchase Type No." = '' then - exit(false); - until EDocumentPurchaseLine.Next() = 0; - exit(true); end; } \ No newline at end of file diff --git a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Enum.al b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Enum.al index 155c31ae11..21093a4f5b 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Enum.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Enum.al @@ -13,6 +13,8 @@ enum 6105 "E-Doc. Create Purchase Invoice" implements IEDocumentCreatePurchaseIn { Extensible = true; DefaultImplementation = IEDocumentCreatePurchaseInvoice = "E-Doc. Create Purchase Invoice"; - + ObsoleteState = Pending; + ObsoleteReason = 'Replaced by enum 6110 "E-Doc. Proc. Customizations'; + value(0; "Default") { } } \ No newline at end of file diff --git a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocPurchaseDocumentHelper.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocPurchaseDocumentHelper.Codeunit.al new file mode 100644 index 0000000000..de5718fe69 --- /dev/null +++ b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocPurchaseDocumentHelper.Codeunit.al @@ -0,0 +1,245 @@ +// ------------------------------------------------------------------------------------------------ +// 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.Processing.Import; + +using Microsoft.eServices.EDocument; +using Microsoft.eServices.EDocument.Processing.Import.Purchase; +using Microsoft.Finance.Dimension; +using Microsoft.Finance.GeneralLedger.Setup; +using Microsoft.Foundation.Attachment; +using Microsoft.Purchases.Document; +using Microsoft.Purchases.Payables; +using Microsoft.Purchases.Posting; +using System.Telemetry; + +/// +/// Common functionality shared between purchase invoice and credit memo creation from E-Document drafts. +/// This codeunit contains reusable procedures to eliminate code duplication. +/// +codeunit 6185 "E-Doc Purchase Document Helper" +{ + Access = Internal; + Permissions = tabledata "Dimension Set Tree Node" = im, + tabledata "Dimension Set Entry" = im; + + var + Telemetry: Codeunit "Telemetry"; + + /// + /// Creates a purchase document header from E-Document draft data, including all lines. + /// + /// The E-Document record containing the draft data. + /// The purchase document type (Invoice or Credit Memo). + /// The created purchase header record. + internal procedure CreatePurchaseDocumentHeader(EDocument: Record "E-Document"; + DocumentType: Enum "Purchase Document Type" + ) PurchaseHeader: Record "Purchase Header" + var + GLSetup: Record "General Ledger Setup"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + DocumentAttachmentMgt: Codeunit "Document Attachment Mgmt"; + EDocumentPurchaseHistMapping: Codeunit "E-Doc. Purchase Hist. Mapping"; + TelemetryEventId: Text; + PurchaseDocumentExistsErr: Label 'A purchase %1 with external document number %2 already exists for vendor %3.', Comment = '%1 = Purchase Document Type, %2 = External Document No., %3 = Vendor No.'; + ExternalDocumentNo: Code[35]; + begin + case DocumentType of + "Purchase Document Type"::Invoice: + begin + TelemetryEventId := '0000PLY'; + ExternalDocumentNo := CopyStr(EDocumentPurchaseHeader."Sales Invoice No.", 1, MaxStrLen(PurchaseHeader."Vendor Invoice No.")); + end; + "Purchase Document Type"::"Credit Memo": + begin + TelemetryEventId := '0000PLZ'; + ExternalDocumentNo := CopyStr(EDocumentPurchaseHeader."Sales Invoice No.", 1, MaxStrLen(PurchaseHeader."Vendor Cr. Memo No.")); + end; + end; + EDocumentPurchaseHeader := ValidateEDocumentDraft(EDocument, TelemetryEventId); + EDocumentPurchaseHeader.TestField("E-Document Entry No."); + PurchaseHeader.Validate("Document Type", DocumentType); + PurchaseHeader.Validate("Buy-from Vendor No.", EDocumentPurchaseHeader."[BC] Vendor No."); + + // Validate external document number for duplicates + if ValidateExternalDocumentNumber(PurchaseHeader, ExternalDocumentNo) then + Error(PurchaseDocumentExistsErr, PurchaseHeader."Document Type", ExternalDocumentNo, PurchaseHeader."Buy-from Vendor No."); + + case PurchaseHeader."Document Type" of + "Purchase Document Type"::Invoice: + PurchaseHeader.Validate("Vendor Invoice No.", ExternalDocumentNo); + "Purchase Document Type"::"Credit Memo": + PurchaseHeader.Validate("Vendor Cr. Memo No.", ExternalDocumentNo); + end; + PurchaseHeader.Validate("Vendor Order No.", EDocumentPurchaseHeader."Purchase Order No."); + PurchaseHeader.Insert(true); + + if EDocumentPurchaseHeader."Document Date" <> 0D then + PurchaseHeader.Validate("Document Date", EDocumentPurchaseHeader."Document Date"); + if EDocumentPurchaseHeader."Due Date" <> 0D then + PurchaseHeader.Validate("Due Date", EDocumentPurchaseHeader."Due Date"); + if DocumentType = "Purchase Document Type"::Invoice then + PurchaseHeader."Invoice Received Date" := PurchaseHeader."Document Date"; + if (DocumentType = "Purchase Document Type"::"Credit Memo") and (EDocumentPurchaseHeader."Applies-to Doc. No." <> '') then begin + PurchaseHeader.Validate("Applies-to Doc. Type", PurchaseHeader."Applies-to Doc. Type"::Invoice); + PurchaseHeader.Validate("Applies-to Doc. No.", EDocumentPurchaseHeader."Applies-to Doc. No."); + end; + + PurchaseHeader.Modify(false); + + // Validate of currency has to happen after insert. + GLSetup.GetRecordOnce(); + if EDocumentPurchaseHeader."Currency Code" <> GLSetup.GetCurrencyCode('') then begin + PurchaseHeader.Validate("Currency Code", EDocumentPurchaseHeader."Currency Code"); + PurchaseHeader.Modify(false); + end; + + // Track changes for history + EDocumentPurchaseHistMapping.TrackRecord(EDocument, EDocumentPurchaseHeader, PurchaseHeader); + + PurchaseHeader.SetRecFilter(); + PurchaseHeader.FindFirst(); + PurchaseHeader."Doc. Amount Incl. VAT" := EDocumentPurchaseHeader.Total; + PurchaseHeader."Doc. Amount VAT" := EDocumentPurchaseHeader."Total VAT"; + case DocumentType of + "Purchase Document Type"::Invoice: + PurchaseHeader.TestField("Document Type", "Purchase Document Type"::Invoice); + "Purchase Document Type"::"Credit Memo": + PurchaseHeader.TestField("Document Type", "Purchase Document Type"::"Credit Memo"); + end; + PurchaseHeader.TestField("No."); + PurchaseHeader."E-Document Link" := EDocument.SystemId; + PurchaseHeader.Modify(false); + + // Post document creation + DocumentAttachmentMgt.CopyAttachments(EDocument, PurchaseHeader); + DocumentAttachmentMgt.DeleteAttachedDocuments(EDocument); + + CreatePurchaseDocumentLines(EDocument, PurchaseHeader, EDocumentPurchaseHistMapping); + exit(PurchaseHeader); + end; + + /// + /// Creates all purchase document lines from E-Document draft data. + /// + /// The E-Document record containing the draft data. + /// The purchase header record that the lines belong to. + /// The history mapping codeunit for tracking changes. + local procedure CreatePurchaseDocumentLines(EDocument: Record "E-Document"; PurchaseHeader: Record "Purchase Header"; var EDocumentPurchaseHistMapping: Codeunit "E-Doc. Purchase Hist. Mapping") + var + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + PurchaseLine: Record "Purchase Line"; + DimensionManagement: Codeunit DimensionManagement; + PurchaseLineCombinedDimensions: array[10] of Integer; + GlobalDim1: Code[20]; + GlobalDim2: Code[20]; + begin + EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocument."Entry No"); + if EDocumentPurchaseLine.FindSet() then + repeat + PurchaseLine.SetRange("Document Type", PurchaseHeader."Document Type"); + PurchaseLine.SetRange("Document No.", PurchaseHeader."No."); + if PurchaseLine.FindLast() then; + + PurchaseLine."Document Type" := PurchaseHeader."Document Type"; + PurchaseLine."Document No." := PurchaseHeader."No."; + PurchaseLine."Line No." += 10000; + PurchaseLine."Unit of Measure Code" := CopyStr(EDocumentPurchaseLine."[BC] Unit of Measure", 1, MaxStrLen(PurchaseLine."Unit of Measure Code")); + PurchaseLine."Variant Code" := EDocumentPurchaseLine."[BC] Variant Code"; + PurchaseLine.Type := EDocumentPurchaseLine."[BC] Purchase Line Type"; + PurchaseLine.Validate("No.", EDocumentPurchaseLine."[BC] Purchase Type No."); + PurchaseLine.Description := EDocumentPurchaseLine.Description; + + if EDocumentPurchaseLine."[BC] Item Reference No." <> '' then + PurchaseLine.Validate("Item Reference No.", EDocumentPurchaseLine."[BC] Item Reference No."); + + PurchaseLine.Validate(Quantity, EDocumentPurchaseLine.Quantity); + PurchaseLine.Validate("Direct Unit Cost", EDocumentPurchaseLine."Unit Price"); + if EDocumentPurchaseLine."Total Discount" > 0 then + PurchaseLine.Validate("Line Discount Amount", EDocumentPurchaseLine."Total Discount"); + PurchaseLine.Validate("Deferral Code", EDocumentPurchaseLine."[BC] Deferral Code"); + + Clear(PurchaseLineCombinedDimensions); + PurchaseLineCombinedDimensions[1] := PurchaseLine."Dimension Set ID"; + PurchaseLineCombinedDimensions[2] := EDocumentPurchaseLine."[BC] Dimension Set ID"; + PurchaseLine.Validate("Dimension Set ID", DimensionManagement.GetCombinedDimensionSetID(PurchaseLineCombinedDimensions, GlobalDim1, GlobalDim2)); + PurchaseLine.Validate("Shortcut Dimension 1 Code", EDocumentPurchaseLine."[BC] Shortcut Dimension 1 Code"); + PurchaseLine.Validate("Shortcut Dimension 2 Code", EDocumentPurchaseLine."[BC] Shortcut Dimension 2 Code"); + + EDocumentPurchaseHistMapping.ApplyHistoryValuesToPurchaseLine(EDocumentPurchaseLine, PurchaseLine); + PurchaseLine.Insert(false); + + // Track changes for history + EDocumentPurchaseHistMapping.TrackRecord(EDocument, EDocumentPurchaseLine, PurchaseLine); + until EDocumentPurchaseLine.Next() = 0; + end; + + local procedure ValidateAllDraftLinesHaveTypeAndNumber(EDocumentPurchaseHeader: Record "E-Document Purchase Header"): Boolean + var + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + begin + EDocumentPurchaseLine.SetLoadFields("[BC] Purchase Line Type", "[BC] Purchase Type No."); + EDocumentPurchaseLine.ReadIsolation(IsolationLevel::ReadCommitted); + EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocumentPurchaseHeader."E-Document Entry No."); + if not EDocumentPurchaseLine.FindSet() then + exit(true); + repeat + if EDocumentPurchaseLine."[BC] Purchase Line Type" = EDocumentPurchaseLine."[BC] Purchase Line Type"::" " then + exit(false); + if EDocumentPurchaseLine."[BC] Purchase Type No." = '' then + exit(false); + until EDocumentPurchaseLine.Next() = 0; + exit(true); + end; + + local procedure ValidateEDocumentDraft(EDocument: Record "E-Document"; TelemetryEventId: Text) EDocumentPurchaseHeader: Record "E-Document Purchase Header" + var + EmptyDraftLineErr: Label 'Draft line does not contain type or number'; + DraftLineDoesNotConstantTypeAndNumberErr: Label 'One of the draft lines do not contain the type and number. Please, specify these fields manually.'; + begin + EDocumentPurchaseHeader.GetFromEDocument(EDocument); + if not ValidateAllDraftLinesHaveTypeAndNumber(EDocumentPurchaseHeader) then begin + Telemetry.LogMessage(TelemetryEventId, EmptyDraftLineErr, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::All); + Error(DraftLineDoesNotConstantTypeAndNumberErr); + end; + exit(EDocumentPurchaseHeader); + end; + + /// + /// Validates document totals using purchase posting validation. + /// + /// The purchase header to validate totals for. + /// True if validation passes, false otherwise. + [TryFunction] + internal procedure TryValidateDocumentTotals(PurchaseHeader: Record "Purchase Header") + var + PurchPost: Codeunit "Purch.-Post"; + begin + // If document totals are setup, we just run the validation + PurchPost.CheckDocumentTotalAmounts(PurchaseHeader); + end; + + local procedure ValidateExternalDocumentNumber(PurchaseHeader: Record "Purchase Header"; ExternalDocumentNo: Code[35]): Boolean + var + VendorLedgerEntry: Record "Vendor Ledger Entry"; + StopCreatingPurchaseDocument: Boolean; + InvoiceAlreadyExistsErr: Label 'A purchase invoice with external document number %1 already exists for vendor %2.', Comment = '%1 = Vendor Invoice No., %2 = Vendor No.'; + TelemetryEventId: Text; + begin + case PurchaseHeader."Document Type" of + "Purchase Document Type"::Invoice: + TelemetryEventId := '0000PHC'; + "Purchase Document Type"::"Credit Memo": + TelemetryEventId := '0000PHD'; + end; + VendorLedgerEntry.SetLoadFields("Entry No."); + VendorLedgerEntry.ReadIsolation := VendorLedgerEntry.ReadIsolation::ReadUncommitted; + StopCreatingPurchaseDocument := PurchaseHeader.FindPostedDocumentWithSameExternalDocNo(VendorLedgerEntry, ExternalDocumentNo); + if StopCreatingPurchaseDocument then begin + Telemetry.LogMessage(TelemetryEventId, InvoiceAlreadyExistsErr, Verbosity::Error, DataClassification::OrganizationIdentifiableInformation, TelemetryScope::All); + exit(true); + end; + exit(false); + end; +} diff --git a/Apps/W1/EDocument/app/src/Processing/Import/ImportEDocumentProcess.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/ImportEDocumentProcess.Codeunit.al index a711ab027d..9c1b6658ba 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/ImportEDocumentProcess.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/ImportEDocumentProcess.Codeunit.al @@ -174,8 +174,8 @@ codeunit 6104 "Import E-Document Process" local procedure UndoProcessingStep(EDocument: Record "E-Document"; Step: Enum "Import E-Document Steps") var EDocumentHeaderMapping: Record "E-Document Header Mapping"; - EDocumentPurchaseLine: Record "E-Document Purchase Line"; IEDocumentFinishDraft: Interface IEDocumentFinishDraft; + IStructuredFormatReader: Interface IStructuredFormatReader; begin case Step of Step::"Finish draft": @@ -187,8 +187,8 @@ codeunit 6104 "Import E-Document Process" end; Step::"Read into Draft": begin - EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocument."Entry No"); - EDocumentPurchaseLine.DeleteAll(false); + IStructuredFormatReader := EDocument."Read into Draft Impl."; + IStructuredFormatReader.ResetDraft(EDocument); end; Step::"Prepare draft": begin diff --git a/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/EDocProviders.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/EDocProviders.Codeunit.al index 8449bf31ca..5cbc4810b6 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/EDocProviders.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/EDocProviders.Codeunit.al @@ -12,7 +12,6 @@ using Microsoft.Bank.Reconciliation; using Microsoft.eServices.EDocument.Service.Participant; using Microsoft.Inventory.Item; using Microsoft.Purchases.Document; -using Microsoft.Projects.Resources.Resource; using Microsoft.eServices.EDocument.Processing.Interfaces; using Microsoft.eServices.EDocument.Processing.Import.Purchase; using System.Log; @@ -109,7 +108,10 @@ codeunit 6124 "E-Doc. Providers" implements IPurchaseLineProvider, IUnitOfMeasur if GetPurchaseLineItemRef(EDocumentPurchaseLine, ItemReference) then begin EDocumentPurchaseLine."[BC] Purchase Line Type" := "Purchase Line Type"::Item; EDocumentPurchaseLine.Validate("[BC] Purchase Type No.", ItemReference."Item No."); - EDocumentPurchaseLine.Validate("[BC] Unit of Measure", ItemReference."Unit of Measure"); + if ItemReference."Unit of Measure" <> '' then + EDocumentPurchaseLine.Validate("[BC] Unit of Measure", ItemReference."Unit of Measure") + else + EDocumentPurchaseLine.Validate("[BC] Unit of Measure", GetDefaultUnitOfMeasure(ItemReference."Item No.")); EDocumentPurchaseLine.Validate("[BC] Variant Code", ItemReference."Variant Code"); EDocumentPurchaseLine.Validate("[BC] Item Reference No.", ItemReference."Reference No."); EDocImpSessionTelemetry.SetLineBool(EDocumentPurchaseLine.SystemId, 'Item Reference ', true); @@ -184,25 +186,14 @@ codeunit 6124 "E-Doc. Providers" implements IPurchaseLineProvider, IUnitOfMeasur exit(true); end; - procedure GetDefaultUnitOfMeasure(PurchaseLineType: Enum "Purchase Line Type"; PurchaseLineTypeNo: Code[20]) UnitOfMeasureCode: Code[20]; + local procedure GetDefaultUnitOfMeasure(ItemNo: Code[20]) UnitOfMeasureCode: Code[20]; var Item: Record Item; - Resource: Record Resource; begin - case PurchaseLineType of - "Purchase Line Type"::Item: - begin - if Item.Get(PurchaseLineTypeNo) then - if Item."Purch. Unit of Measure" <> '' then - UnitOfMeasureCode := Item."Purch. Unit of Measure" - else - UnitOfMeasureCode := Item."Base Unit of Measure"; - end; - "Purchase Line Type"::Resource: - begin - if Resource.Get(PurchaseLineTypeNo) then - UnitOfMeasureCode := Resource."Base Unit of Measure"; - end; - end; + if Item.Get(ItemNo) then + if Item."Purch. Unit of Measure" <> '' then + UnitOfMeasureCode := Item."Purch. Unit of Measure" + else + UnitOfMeasureCode := Item."Base Unit of Measure"; end; } \ No newline at end of file diff --git a/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al index 7275079d2a..4ab56b4a02 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al @@ -77,9 +77,6 @@ codeunit 6125 "Prepare Purchase E-Doc. Draft" implements IProcessStructuredData if EDocPurchaseHistMapping.FindRelatedPurchaseLineInHistory(EDocumentPurchaseHeader."[BC] Vendor No.", EDocumentPurchaseLine, EDocPurchaseLineHistory) then EDocPurchaseHistMapping.UpdateMissingLineValuesFromHistory(EDocPurchaseLineHistory, EDocumentPurchaseLine); - if EDocumentPurchaseLine."[BC] Unit of Measure" = '' then - EDocumentPurchaseLine."[BC] Unit of Measure" := IUnitOfMeasureProvider.GetDefaultUnitOfMeasure(EDocumentPurchaseLine."[BC] Purchase Line Type", EDocumentPurchaseLine."[BC] Purchase Type No."); - EDocumentPurchaseLine.Modify(); LogActivitySessionChanges(EDocActivityLogSession); EDocActivityLogSession.CleanUpLogs(); diff --git a/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al b/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al index 72e6f77353..9025973338 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al @@ -212,6 +212,11 @@ table 6100 "E-Document Purchase Header" Caption = 'E-Document Type'; DataClassification = CustomerContent; } + field(39; "Applies-to Doc. No."; Text[20]) + { + Caption = 'Applies-to Doc. No.'; + DataClassification = CustomerContent; + } #endregion Purchase fields #region Business Central Data - Validated fields [101-200] diff --git a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al index 7dc7cffc71..1c5ec1cbb4 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al @@ -131,6 +131,26 @@ codeunit 6174 "E-Document ADI Handler" implements IStructureReceivedEDocument, I EDocReadablePurchaseDoc.Run(); end; + procedure ResetDraft(EDocument: Record "E-Document") + var + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + EDocumentHeaderMapping: Record "E-Document Header Mapping"; + EDocumentLineField: Record "E-Document Line - Field"; + begin + EDocumentPurchaseHeader.GetFromEDocument(EDocument); + EDocumentPurchaseHeader.Delete(true); + + EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocument."Entry No"); + EDocumentPurchaseLine.DeleteAll(true); + + EDocumentHeaderMapping.SetRange("E-Document Entry No.", EDocument."Entry No"); + EDocumentHeaderMapping.DeleteAll(true); + + EDocumentLineField.SetRange("E-Document Entry No.", EDocument."Entry No"); + EDocumentLineField.DeleteAll(true); + end; + local procedure PopulateEDocumentPurchaseLines(ItemsArray: JsonArray; EDocumentEntryNo: Integer; var TempEDocPurchaseLine: Record "E-Document Purchase Line" temporary) var JsonTokenTemp, ItemToken : JsonToken; diff --git a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al index 9437c0ee9e..1540348eef 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al @@ -4,10 +4,10 @@ // ------------------------------------------------------------------------------------------------ namespace Microsoft.eServices.EDocument.Format; +using Microsoft.EServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Helpers; using Microsoft.eServices.EDocument.Processing.Import; -using Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument.Processing.Interfaces; using System.Utilities; @@ -91,7 +91,6 @@ codeunit 6173 "E-Document PEPPOL Handler" implements IStructuredFormatReader NewLineXML: XmlDocument; LineXMLList: XmlNodeList; LineXMLNode: XmlNode; - i: Integer; CreditNoteLinePathLbl: Label '/cn:CreditNote/cac:CreditNoteLine'; CreditNoteNamespaceLbl: Label 'urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2'; begin @@ -157,7 +156,8 @@ codeunit 6173 "E-Document PEPPOL Handler" implements IStructuredFormatReader XmlNamespaces.AddNamespace('cn', CreditNoteNamespaceLbl); EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cbc:ID', MaxStrLen(EDocumentPurchaseHeader."Sales Invoice No."), EDocumentPurchaseHeader."Sales Invoice No."); - EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:BillingReference/cac:InvoiceDocumentReference/cbc:ID', MaxStrLen(EDocumentPurchaseHeader."Purchase Order No."), EDocumentPurchaseHeader."Purchase Order No."); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:OrderReference/cbc:ID', MaxStrLen(EDocumentPurchaseHeader."Purchase Order No."), EDocumentPurchaseHeader."Applies-to Doc. No."); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:BillingReference/cac:InvoiceDocumentReference/cbc:ID', MaxStrLen(EDocumentPurchaseHeader."Applies-to Doc. No."), EDocumentPurchaseHeader."Purchase Order No."); EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name', MaxStrLen(EDocumentPurchaseHeader."Vendor Company Name"), EDocumentPurchaseHeader."Vendor Company Name"); // Line below, using PayeeParty, shall be used when the Payee is different from the Seller. Otherwise, it will not be shown in the XML. EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:PayeeParty/cac:PartyName/cbc:Name', MaxStrLen(EDocumentPurchaseHeader."Vendor Company Name"), EDocumentPurchaseHeader."Vendor Company Name"); @@ -233,4 +233,24 @@ codeunit 6173 "E-Document PEPPOL Handler" implements IStructuredFormatReader EDocReadablePurchaseDoc.SetBuffer(EDocPurchaseHeader, EDocPurchaseLine); EDocReadablePurchaseDoc.Run(); end; + + procedure ResetDraft(EDocument: Record "E-Document") + var + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + EDocumentHeaderMapping: Record "E-Document Header Mapping"; + EDocumentLineField: Record "E-Document Line - Field"; + begin + EDocumentPurchaseHeader.GetFromEDocument(EDocument); + EDocumentPurchaseHeader.Delete(true); + + EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocument."Entry No"); + EDocumentPurchaseLine.DeleteAll(true); + + EDocumentHeaderMapping.SetRange("E-Document Entry No.", EDocument."Entry No"); + EDocumentHeaderMapping.DeleteAll(true); + + EDocumentLineField.SetRange("E-Document Entry No.", EDocument."Entry No"); + EDocumentLineField.DeleteAll(true); + end; } \ No newline at end of file diff --git a/Apps/W1/EDocument/app/src/Processing/Interfaces/IStructuredFormatReader.Interface.al b/Apps/W1/EDocument/app/src/Processing/Interfaces/IStructuredFormatReader.Interface.al index 219c281c6f..2001cbe86b 100644 --- a/Apps/W1/EDocument/app/src/Processing/Interfaces/IStructuredFormatReader.Interface.al +++ b/Apps/W1/EDocument/app/src/Processing/Interfaces/IStructuredFormatReader.Interface.al @@ -14,7 +14,6 @@ using System.Utilities; /// interface IStructuredFormatReader { - /// /// Read the data into the E-Document data structures. /// @@ -23,7 +22,6 @@ interface IStructuredFormatReader /// The data process to run on the structured data. procedure ReadIntoDraft(EDocument: Record "E-Document"; TempBlob: Codeunit "Temp Blob"): Enum "E-Doc. Process Draft"; - /// /// Presents a view of the data /// @@ -31,4 +29,9 @@ interface IStructuredFormatReader /// The temporary blob that contains the data to read procedure View(EDocument: Record "E-Document"; TempBlob: Codeunit "Temp Blob"); + /// + /// Resets draft by removing all the created lines associated with the E-Document. + /// + /// The E-Document record for which to remove the created draft lines. + procedure ResetDraft(EDocument: Record "E-Document"); } \ No newline at end of file diff --git a/Apps/W1/EDocument/app/src/Processing/Interfaces/IUnitOfMeasureProvider.Interface.al b/Apps/W1/EDocument/app/src/Processing/Interfaces/IUnitOfMeasureProvider.Interface.al index 10ec2a113c..96e3342d00 100644 --- a/Apps/W1/EDocument/app/src/Processing/Interfaces/IUnitOfMeasureProvider.Interface.al +++ b/Apps/W1/EDocument/app/src/Processing/Interfaces/IUnitOfMeasureProvider.Interface.al @@ -21,12 +21,4 @@ interface IUnitOfMeasureProvider /// The external unit of measure reference. /// A Unit of Measure record corresponding to the provided details. procedure GetUnitOfMeasure(EDocument: Record "E-Document"; EDocumentLineId: Integer; ExternalUnitOfMeasure: Text): Record "Unit of Measure"; - - /// - /// Retrieves the default unit of measure for a given purchase line type and number. - /// - /// The type of the purchase line. - /// The number associated with the purchase line type. - /// The default unit of measure code for the specified purchase line type and number. - procedure GetDefaultUnitOfMeasure(PurchaseLineType: Enum "Purchase Line Type"; PurchaseLineTypeNo: Code[20]) UnitOfMeasureCode: Code[20]; } \ No newline at end of file From 7f48abdd3d4557d3867fe21d1ac958e9c02f9ef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Wed, 13 Aug 2025 17:04:22 +0300 Subject: [PATCH 13/28] Adds XML node value retrieval helper method Introduces a utility method that simplifies extracting text content from XML nodes using XPath expressions. The method handles node selection and safely returns the inner text value or an empty string if the node is not found, reducing boilerplate code for XML parsing operations. --- .../src/Helpers/EDocumentXMLHelper.Codeunit.al | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al b/Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al index 096b39501c..691adeec45 100644 --- a/Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al @@ -107,4 +107,20 @@ codeunit 6177 "EDocument XML Helper" if XMLNode.AsXmlElement().InnerText() <> '' then Evaluate(DateValue, XMLNode.AsXmlElement().InnerText(), 9); end; + + /// + /// Retrieves the inner text value of an XML node using XPath expression. + /// + /// The XML document to search in. + /// The XML namespace manager for XPath queries. + /// The XPath expression to locate the node. + /// The inner text value of the found node, or empty string if node not found. + internal procedure GetNodeValue(XmlDoc: XmlDocument; XmlNamespaces: XmlNamespaceManager; XPath: Text): Text + var + XMLNode: XmlNode; + begin + if XmlDoc.SelectSingleNode(XPath, XmlNamespaces, XMLNode) then + exit(XMLNode.AsXmlElement().InnerText()); + exit(''); + end; } From bf2dabe120276801618454c61787fa44b82c5308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Wed, 13 Aug 2025 18:40:47 +0300 Subject: [PATCH 14/28] Refactors draft reset logic into reusable method Consolidates duplicate draft reset code by moving the implementation to a central ResetDraft method on the E-Document table. Multiple codeunits previously contained identical logic for deleting purchase headers, lines, mappings, and line fields. Improves maintainability by eliminating code duplication and providing a single source of truth for draft cleanup operations. --- .../app/src/Document/EDocument.Table.al | 26 +++++++++++++++++++ .../Import/EDocEmptyDraft.Codeunit.al | 17 +----------- .../Purchase/EDocumentPurchaseDraft.Page.al | 2 +- .../EDocumentADIHandler.Codeunit.al | 17 +----------- .../EDocumentPEPPOLHandler.Codeunit.al | 17 +----------- 5 files changed, 30 insertions(+), 49 deletions(-) diff --git a/Apps/W1/EDocument/app/src/Document/EDocument.Table.al b/Apps/W1/EDocument/app/src/Document/EDocument.Table.al index 3a9f72288a..51765bf272 100644 --- a/Apps/W1/EDocument/app/src/Document/EDocument.Table.al +++ b/Apps/W1/EDocument/app/src/Document/EDocument.Table.al @@ -479,6 +479,32 @@ table 6121 "E-Document" exit(GetEDocumentServiceStatus()."Import Processing Status"); end; #endif + + /// + /// Resets the draft data associated with the E-Document by removing all created lines and mappings. + /// This procedure cleans up all purchase-related draft data including purchase header, lines, + /// header mappings, and additional line fields that were created during the import process. + /// + procedure ResetDraft() + var + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + EDocumentHeaderMapping: Record "E-Document Header Mapping"; + EDocumentLineField: Record "E-Document Line - Field"; + begin + EDocumentPurchaseHeader.GetFromEDocument(Rec); + EDocumentPurchaseHeader.Delete(true); + + EDocumentPurchaseLine.SetRange("E-Document Entry No.", Rec."Entry No"); + EDocumentPurchaseLine.DeleteAll(true); + + EDocumentHeaderMapping.SetRange("E-Document Entry No.", Rec."Entry No"); + EDocumentHeaderMapping.DeleteAll(true); + + EDocumentLineField.SetRange("E-Document Entry No.", Rec."Entry No"); + EDocumentLineField.DeleteAll(true); + end; + internal procedure ToString(): Text begin exit(StrSubstNo(ToStringLbl, SystemId, "Document Record ID", "Workflow Step Instance ID", "Job Queue Entry ID")); diff --git a/Apps/W1/EDocument/app/src/Processing/Import/EDocEmptyDraft.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/EDocEmptyDraft.Codeunit.al index 109bbabbc9..581315f71d 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/EDocEmptyDraft.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/EDocEmptyDraft.Codeunit.al @@ -52,22 +52,7 @@ codeunit 6193 "E-Doc. Empty Draft" implements IStructureReceivedEDocument, IStru end; procedure ResetDraft(EDocument: Record "E-Document") - var - EDocumentPurchaseHeader: Record "E-Document Purchase Header"; - EDocumentPurchaseLine: Record "E-Document Purchase Line"; - EDocumentHeaderMapping: Record "E-Document Header Mapping"; - EDocumentLineField: Record "E-Document Line - Field"; begin - EDocumentPurchaseHeader.GetFromEDocument(EDocument); - EDocumentPurchaseHeader.Delete(true); - - EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocument."Entry No"); - EDocumentPurchaseLine.DeleteAll(true); - - EDocumentHeaderMapping.SetRange("E-Document Entry No.", EDocument."Entry No"); - EDocumentHeaderMapping.DeleteAll(true); - - EDocumentLineField.SetRange("E-Document Entry No.", EDocument."Entry No"); - EDocumentLineField.DeleteAll(true); + EDocument.ResetDraft(); end; } \ No newline at end of file diff --git a/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al b/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al index fd1e1a7034..10fe137336 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al @@ -248,7 +248,7 @@ page 6181 "E-Document Purchase Draft" Visible = true; trigger OnAction() begin - ResetDraft(); + this.ResetDraft(); end; } action(AnalyzeDocument) diff --git a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al index 1c5ec1cbb4..34dd3ac4e4 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al @@ -132,23 +132,8 @@ codeunit 6174 "E-Document ADI Handler" implements IStructureReceivedEDocument, I end; procedure ResetDraft(EDocument: Record "E-Document") - var - EDocumentPurchaseHeader: Record "E-Document Purchase Header"; - EDocumentPurchaseLine: Record "E-Document Purchase Line"; - EDocumentHeaderMapping: Record "E-Document Header Mapping"; - EDocumentLineField: Record "E-Document Line - Field"; begin - EDocumentPurchaseHeader.GetFromEDocument(EDocument); - EDocumentPurchaseHeader.Delete(true); - - EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocument."Entry No"); - EDocumentPurchaseLine.DeleteAll(true); - - EDocumentHeaderMapping.SetRange("E-Document Entry No.", EDocument."Entry No"); - EDocumentHeaderMapping.DeleteAll(true); - - EDocumentLineField.SetRange("E-Document Entry No.", EDocument."Entry No"); - EDocumentLineField.DeleteAll(true); + EDocument.ResetDraft(); end; local procedure PopulateEDocumentPurchaseLines(ItemsArray: JsonArray; EDocumentEntryNo: Integer; var TempEDocPurchaseLine: Record "E-Document Purchase Line" temporary) diff --git a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al index 1540348eef..ed8a932fb3 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al @@ -235,22 +235,7 @@ codeunit 6173 "E-Document PEPPOL Handler" implements IStructuredFormatReader end; procedure ResetDraft(EDocument: Record "E-Document") - var - EDocumentPurchaseHeader: Record "E-Document Purchase Header"; - EDocumentPurchaseLine: Record "E-Document Purchase Line"; - EDocumentHeaderMapping: Record "E-Document Header Mapping"; - EDocumentLineField: Record "E-Document Line - Field"; begin - EDocumentPurchaseHeader.GetFromEDocument(EDocument); - EDocumentPurchaseHeader.Delete(true); - - EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocument."Entry No"); - EDocumentPurchaseLine.DeleteAll(true); - - EDocumentHeaderMapping.SetRange("E-Document Entry No.", EDocument."Entry No"); - EDocumentHeaderMapping.DeleteAll(true); - - EDocumentLineField.SetRange("E-Document Entry No.", EDocument."Entry No"); - EDocumentLineField.DeleteAll(true); + EDocument.ResetDraft(); end; } \ No newline at end of file From c04a0898d8b888569aab13a0f3d75a4f72f13ffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Wed, 13 Aug 2025 19:03:28 +0300 Subject: [PATCH 15/28] Fixes field mapping in PEPPOL credit note XML generation Corrects the assignment of purchase order number and applies-to document number fields in credit note XML structure. Previously, the values were swapped between OrderReference and BillingReference sections, causing incorrect document references in generated PEPPOL credit notes. --- .../EDocumentPEPPOLHandler.Codeunit.al | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al index ed8a932fb3..308e2da6f6 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al @@ -156,8 +156,8 @@ codeunit 6173 "E-Document PEPPOL Handler" implements IStructuredFormatReader XmlNamespaces.AddNamespace('cn', CreditNoteNamespaceLbl); EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cbc:ID', MaxStrLen(EDocumentPurchaseHeader."Sales Invoice No."), EDocumentPurchaseHeader."Sales Invoice No."); - EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:OrderReference/cbc:ID', MaxStrLen(EDocumentPurchaseHeader."Purchase Order No."), EDocumentPurchaseHeader."Applies-to Doc. No."); - EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:BillingReference/cac:InvoiceDocumentReference/cbc:ID', MaxStrLen(EDocumentPurchaseHeader."Applies-to Doc. No."), EDocumentPurchaseHeader."Purchase Order No."); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:OrderReference/cbc:ID', MaxStrLen(EDocumentPurchaseHeader."Purchase Order No."), EDocumentPurchaseHeader."Purchase Order No."); + EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:BillingReference/cac:InvoiceDocumentReference/cbc:ID', MaxStrLen(EDocumentPurchaseHeader."Applies-to Doc. No."), EDocumentPurchaseHeader."Applies-to Doc. No."); EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name', MaxStrLen(EDocumentPurchaseHeader."Vendor Company Name"), EDocumentPurchaseHeader."Vendor Company Name"); // Line below, using PayeeParty, shall be used when the Payee is different from the Seller. Otherwise, it will not be shown in the XML. EDocumentXMLHelper.SetStringValueInField(PeppolXML, XMLNamespaces, '/cn:CreditNote/cac:PayeeParty/cac:PartyName/cbc:Name', MaxStrLen(EDocumentPurchaseHeader."Vendor Company Name"), EDocumentPurchaseHeader."Vendor Company Name"); From 6a96f3e7cad4895a20749d5c1c19aba254a279ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Thu, 14 Aug 2025 10:04:49 +0300 Subject: [PATCH 16/28] Prevents errors when deleting empty record sets Adds empty checks before calling delete operations to avoid potential runtime errors when attempting to delete records that don't exist. Improves robustness by ensuring delete operations are only performed when there are actual records to delete. --- .../W1/EDocument/app/src/Document/EDocument.Table.al | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Apps/W1/EDocument/app/src/Document/EDocument.Table.al b/Apps/W1/EDocument/app/src/Document/EDocument.Table.al index 51765bf272..e89346c052 100644 --- a/Apps/W1/EDocument/app/src/Document/EDocument.Table.al +++ b/Apps/W1/EDocument/app/src/Document/EDocument.Table.al @@ -493,16 +493,20 @@ table 6121 "E-Document" EDocumentLineField: Record "E-Document Line - Field"; begin EDocumentPurchaseHeader.GetFromEDocument(Rec); - EDocumentPurchaseHeader.Delete(true); + if not EDocumentPurchaseHeader.IsEmpty() then + EDocumentPurchaseHeader.Delete(true); EDocumentPurchaseLine.SetRange("E-Document Entry No.", Rec."Entry No"); - EDocumentPurchaseLine.DeleteAll(true); + if not EDocumentPurchaseLine.IsEmpty() then + EDocumentPurchaseLine.DeleteAll(true); EDocumentHeaderMapping.SetRange("E-Document Entry No.", Rec."Entry No"); - EDocumentHeaderMapping.DeleteAll(true); + if not EDocumentHeaderMapping.IsEmpty() then + EDocumentHeaderMapping.DeleteAll(true); EDocumentLineField.SetRange("E-Document Entry No.", Rec."Entry No"); - EDocumentLineField.DeleteAll(true); + if not EDocumentLineField.IsEmpty() then + EDocumentLineField.DeleteAll(true); end; internal procedure ToString(): Text From 24fae9926bae632522ae7ba3d158895475edaeb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= <30231314+AndriusAndrulevicius@users.noreply.github.com> Date: Thu, 14 Aug 2025 17:25:53 +0300 Subject: [PATCH 17/28] Update Apps/W1/EDocument/test/src/Processing/PEPPOLStructuredValidations.Codeunit.al Co-authored-by: Grasiele Matuleviciute <131970463+GMatuleviciute@users.noreply.github.com> --- .../test/src/Processing/PEPPOLStructuredValidations.Codeunit.al | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Apps/W1/EDocument/test/src/Processing/PEPPOLStructuredValidations.Codeunit.al b/Apps/W1/EDocument/test/src/Processing/PEPPOLStructuredValidations.Codeunit.al index 7154018915..0c0357d669 100644 --- a/Apps/W1/EDocument/test/src/Processing/PEPPOLStructuredValidations.Codeunit.al +++ b/Apps/W1/EDocument/test/src/Processing/PEPPOLStructuredValidations.Codeunit.al @@ -8,7 +8,7 @@ codeunit 139896 "PEPPOL Structured Validations" PurchaseorderNoTok: Label '2'; MockDate: Date; MockCurrencyCode: Code[10]; - MockDataMismatchLbl: Label 'The %1 in %2 does not align with the mock data. Expected: %3, Actual: %4', Locked = true; + MockDataMismatchErr: Label 'The %1 in %2 does not align with the mock data. Expected: %3, Actual: %4'; internal procedure AssertFullEDocumentContentExtracted(EDocumentEntryNo: Integer) var From f34e79e248dea66d95eab6136746321819f015df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= <30231314+AndriusAndrulevicius@users.noreply.github.com> Date: Thu, 14 Aug 2025 17:26:33 +0300 Subject: [PATCH 18/28] Update Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al Co-authored-by: Grasiele Matuleviciute <131970463+GMatuleviciute@users.noreply.github.com> --- .../test/src/Processing/EDocumentStructuredTests.Codeunit.al | 1 + 1 file changed, 1 insertion(+) diff --git a/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al b/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al index e06a0be65a..ddb10f84d5 100644 --- a/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al +++ b/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al @@ -114,6 +114,7 @@ codeunit 139891 "E-Document Structured Tests" EDocImport.ViewExtractedData(EDocument); // [THEN] The extracted data page opens and can be handled properly (verified by page handler) + // EDocumentPurchaseHeaderPageHandler end; [Test] From 5f300e0cc7e346705b8c79433fae09ac680e1f1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= <30231314+AndriusAndrulevicius@users.noreply.github.com> Date: Thu, 14 Aug 2025 17:27:01 +0300 Subject: [PATCH 19/28] Update Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al Co-authored-by: Grasiele Matuleviciute <131970463+GMatuleviciute@users.noreply.github.com> --- .../test/src/Processing/EDocumentStructuredTests.Codeunit.al | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al b/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al index ddb10f84d5..2dbbc5184d 100644 --- a/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al +++ b/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al @@ -95,11 +95,6 @@ codeunit 139891 "E-Document Structured Tests" begin // [FEATURE] [E-Document] [PEPPOL] [View Data] // [SCENARIO] View extracted data from a valid PEPPOL invoice document - // --------------------------------------------------------------------------- - // [Expected Outcome] - // [1] E-Document is successfully processed to draft status - // [2] Extracted data page opens without errors - // [3] Page handler verifies the page can be closed properly // [GIVEN] A valid PEPPOL XML invoice document is imported and processed Initialize(Enum::"Service Integration"::"Mock"); From 6435c88497d6726e498d841f64968fb2874c509a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= <30231314+AndriusAndrulevicius@users.noreply.github.com> Date: Thu, 14 Aug 2025 17:27:15 +0300 Subject: [PATCH 20/28] Update Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al Co-authored-by: Grasiele Matuleviciute <131970463+GMatuleviciute@users.noreply.github.com> --- .../test/src/Processing/EDocumentStructuredTests.Codeunit.al | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al b/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al index 2dbbc5184d..d280bc00f8 100644 --- a/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al +++ b/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al @@ -96,7 +96,7 @@ codeunit 139891 "E-Document Structured Tests" // [FEATURE] [E-Document] [PEPPOL] [View Data] // [SCENARIO] View extracted data from a valid PEPPOL invoice document - // [GIVEN] A valid PEPPOL XML invoice document is imported and processed + // [GIVEN] A valid PEPPOL XML invoice document is imported Initialize(Enum::"Service Integration"::"Mock"); SetupPEPPOLEDocumentService(); CreateInboundEDocumentFromXML(EDocument, 'peppol/peppol-invoice-0.xml'); From 345a48c87e61927159e0bd2cca6d5cea2e56b359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= <30231314+AndriusAndrulevicius@users.noreply.github.com> Date: Thu, 14 Aug 2025 17:27:24 +0300 Subject: [PATCH 21/28] Update Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al Co-authored-by: Grasiele Matuleviciute <131970463+GMatuleviciute@users.noreply.github.com> --- .../src/Processing/EDocumentStructuredTests.Codeunit.al | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al b/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al index d280bc00f8..59d131c476 100644 --- a/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al +++ b/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al @@ -168,13 +168,6 @@ codeunit 139891 "E-Document Structured Tests" begin // [FEATURE] [E-Document] [PEPPOL] [Draft Update] [Manual Intervention] // [SCENARIO] Update draft purchase document data and finalize processing - // --------------------------------------------------------------------------- - // [Expected Outcome] - // [1] E-Document is processed to prepare draft status - // [2] Draft document can be opened and modified through UI - // [3] Item number can be manually assigned to purchase lines - // [4] Document processing can be completed after manual updates - // [5] Final purchase header contains both imported and manually updated data // [GIVEN] A valid PEPPOL XML invoice document is imported and processed to draft preparation Initialize(Enum::"Service Integration"::"Mock"); From ae9ff0eb8479df66fdbd930817ea4fcbf618ff58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= <30231314+AndriusAndrulevicius@users.noreply.github.com> Date: Thu, 14 Aug 2025 17:27:29 +0300 Subject: [PATCH 22/28] Update Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al Co-authored-by: Grasiele Matuleviciute <131970463+GMatuleviciute@users.noreply.github.com> --- .../test/src/Processing/EDocumentStructuredTests.Codeunit.al | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al b/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al index 59d131c476..9277c42a37 100644 --- a/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al +++ b/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al @@ -186,8 +186,7 @@ codeunit 139891 "E-Document Structured Tests" EDocPurchaseDraft.Lines.Next(); // [WHEN] The processing is completed to finish draft step - EDocImportParameters."Step to Run" := "Import E-Document Steps"::"Finish draft"; - EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParameters); + EDocImport.ProcessIncomingEDocument(EDocument, "Import E-Document Steps"::"Finish draft"); EDocument.Get(EDocument."Entry No"); // [WHEN] The final purchase record is retrieved From 1ed84e8b3808e74b34cf9b7d803e7366379cc885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= <30231314+AndriusAndrulevicius@users.noreply.github.com> Date: Thu, 14 Aug 2025 17:27:38 +0300 Subject: [PATCH 23/28] Update Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al Co-authored-by: Grasiele Matuleviciute <131970463+GMatuleviciute@users.noreply.github.com> --- .../test/src/Processing/EDocumentStructuredTests.Codeunit.al | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al b/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al index 9277c42a37..bb1e86b1db 100644 --- a/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al +++ b/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al @@ -166,7 +166,7 @@ codeunit 139891 "E-Document Structured Tests" VariantRecord: Variant; RecRef: RecordRef; begin - // [FEATURE] [E-Document] [PEPPOL] [Draft Update] [Manual Intervention] + // [FEATURE] [E-Document] [PEPPOL] [Draft Update] // [SCENARIO] Update draft purchase document data and finalize processing // [GIVEN] A valid PEPPOL XML invoice document is imported and processed to draft preparation From bc164223fe8e597da4b13516ee9811bcf902e435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= <30231314+AndriusAndrulevicius@users.noreply.github.com> Date: Thu, 14 Aug 2025 17:27:45 +0300 Subject: [PATCH 24/28] Update Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al Co-authored-by: Grasiele Matuleviciute <131970463+GMatuleviciute@users.noreply.github.com> --- .../src/Processing/EDocumentStructuredTests.Codeunit.al | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al b/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al index bb1e86b1db..90c145bd75 100644 --- a/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al +++ b/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al @@ -124,12 +124,6 @@ codeunit 139891 "E-Document Structured Tests" begin // [FEATURE] [E-Document] [PEPPOL] [Purchase Invoice Creation] // [SCENARIO] Create a purchase invoice from a valid PEPPOL invoice document - // --------------------------------------------------------------------------- - // [Expected Outcome] - // [1] E-Document is successfully processed through all import steps - // [2] A purchase header record is created and linked to the E-Document - // [3] Purchase header fields are correctly populated with PEPPOL data - // [4] All validation assertions pass for the created purchase document // [GIVEN] A valid PEPPOL XML invoice document is imported Initialize(Enum::"Service Integration"::"Mock"); From 1dbdc157b7902ec2beb2c4b9c873abcfc4b99a85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Mon, 18 Aug 2025 14:38:39 +0300 Subject: [PATCH 25/28] Makes PEPPOL validations stateless and consistent Refactors validation helpers to avoid shared state by passing required data explicitly, reducing test coupling and flakiness. Unifies and locks label tokens, standardizes error messages, and fixes API usage for captions. Cleans up unused variables and minor formatting. Updates tests to use the new helper signature. --- .../EDocumentStructuredTests.Codeunit.al | 14 +- .../PEPPOLStructuredValidations.Codeunit.al | 129 +++++++++--------- 2 files changed, 67 insertions(+), 76 deletions(-) diff --git a/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al b/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al index 90c145bd75..fddf9ba775 100644 --- a/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al +++ b/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al @@ -109,7 +109,7 @@ codeunit 139891 "E-Document Structured Tests" EDocImport.ViewExtractedData(EDocument); // [THEN] The extracted data page opens and can be handled properly (verified by page handler) - // EDocumentPurchaseHeaderPageHandler + // EDocumentPurchaseHeaderPageHandler end; [Test] @@ -117,10 +117,11 @@ codeunit 139891 "E-Document Structured Tests" var EDocument: Record "E-Document"; PurchaseHeader: Record "Purchase Header"; + DummyItem: Record Item; EDocumentProcessing: Codeunit "E-Document Processing"; DataTypeManagement: Codeunit "Data Type Management"; - VariantRecord: Variant; RecRef: RecordRef; + VariantRecord: Variant; begin // [FEATURE] [E-Document] [PEPPOL] [Purchase Invoice Creation] // [SCENARIO] Create a purchase invoice from a valid PEPPOL invoice document @@ -142,7 +143,7 @@ codeunit 139891 "E-Document Structured Tests" // [THEN] The purchase header is correctly created with PEPPOL data PEPPOLStructuredValidations.SetMockCurrencyCode(MockCurrencyCode); PEPPOLStructuredValidations.SetMockDate(MockDate); - PEPPOLStructuredValidations.AssertPurchaseHeader(Vendor."No.", PurchaseHeader); + PEPPOLStructuredValidations.AssertPurchaseDocument(Vendor."No.", PurchaseHeader, DummyItem); end; [Test] @@ -151,14 +152,12 @@ codeunit 139891 "E-Document Structured Tests" EDocument: Record "E-Document"; PurchaseHeader: Record "Purchase Header"; Item: Record Item; - EDocImportParameters: Record "E-Doc. Import Parameters"; EDocImport: Codeunit "E-Doc. Import"; EDocumentProcessing: Codeunit "E-Document Processing"; DataTypeManagement: Codeunit "Data Type Management"; - EDocPurchaseDraftSubform: TestPage "E-Doc. Purchase Draft Subform"; + RecRef: RecordRef; EDocPurchaseDraft: TestPage "E-Document Purchase Draft"; VariantRecord: Variant; - RecRef: RecordRef; begin // [FEATURE] [E-Document] [PEPPOL] [Draft Update] // [SCENARIO] Update draft purchase document data and finalize processing @@ -191,8 +190,7 @@ codeunit 139891 "E-Document Structured Tests" // [THEN] The purchase header contains both imported PEPPOL data and manual updates PEPPOLStructuredValidations.SetMockCurrencyCode(MockCurrencyCode); PEPPOLStructuredValidations.SetMockDate(MockDate); - PEPPOLStructuredValidations.SetItem(Item); - PEPPOLStructuredValidations.AssertPurchaseHeader(Vendor."No.", PurchaseHeader); + PEPPOLStructuredValidations.AssertPurchaseDocument(Vendor."No.", PurchaseHeader, Item); end; [PageHandler] diff --git a/Apps/W1/EDocument/test/src/Processing/PEPPOLStructuredValidations.Codeunit.al b/Apps/W1/EDocument/test/src/Processing/PEPPOLStructuredValidations.Codeunit.al index 0c0357d669..a4866eacf6 100644 --- a/Apps/W1/EDocument/test/src/Processing/PEPPOLStructuredValidations.Codeunit.al +++ b/Apps/W1/EDocument/test/src/Processing/PEPPOLStructuredValidations.Codeunit.al @@ -2,13 +2,13 @@ codeunit 139896 "PEPPOL Structured Validations" { var Assert: Codeunit Assert; - Item: Record Item; UnitOfMeasureCodeTok: Label 'PCS', Locked = true; - SalesInvoiceNoTok: Label '103033'; - PurchaseorderNoTok: Label '2'; + SalesInvoiceNoTok: Label '103033', Locked = true; + PurchaseorderNoTok: Label '2', Locked = true; MockDate: Date; MockCurrencyCode: Code[10]; - MockDataMismatchErr: Label 'The %1 in %2 does not align with the mock data. Expected: %3, Actual: %4'; + MockDataMismatchErr: Label 'The %1 in %2 does not align with the mock data. Expected: %3, Actual: %4', Locked = true, Comment = '%1 = Field caption, %2 = Table caption, %3 = Expected value, %4 = Actual value'; + internal procedure AssertFullEDocumentContentExtracted(EDocumentEntryNo: Integer) var @@ -16,90 +16,83 @@ codeunit 139896 "PEPPOL Structured Validations" EDocumentPurchaseLine: Record "E-Document Purchase Line"; begin EDocumentPurchaseHeader.Get(EDocumentEntryNo); - Assert.AreEqual(SalesInvoiceNoTok, EDocumentPurchaseHeader."Sales Invoice No.", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Sales Invoice No."), EDocumentPurchaseHeader.TableCaption, SalesInvoiceNoTok, EDocumentPurchaseHeader."Sales Invoice No.")); - Assert.AreEqual(MockDate, EDocumentPurchaseHeader."Document Date", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Document Date"), EDocumentPurchaseHeader.TableCaption, MockDate, EDocumentPurchaseHeader."Document Date")); - Assert.AreEqual(CalcDate('<+1M>', MockDate), EDocumentPurchaseHeader."Due Date", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Due Date"), EDocumentPurchaseHeader.TableCaption, CalcDate('<+1M>', MockDate), EDocumentPurchaseHeader."Due Date")); - Assert.AreEqual(MockCurrencyCode, EDocumentPurchaseHeader."Currency Code", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Currency Code"), EDocumentPurchaseHeader.TableCaption, MockCurrencyCode, EDocumentPurchaseHeader."Currency Code")); - Assert.AreEqual(PurchaseorderNoTok, EDocumentPurchaseHeader."Purchase Order No.", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Purchase Order No."), EDocumentPurchaseHeader.TableCaption, PurchaseorderNoTok, EDocumentPurchaseHeader."Purchase Order No.")); - // Assert.AreEqual('', EDocumentPurchaseHeader."Vendor GLN", 'The endpoint schema is not provided to populate the GLN.'); - Assert.AreEqual('CRONUS International', EDocumentPurchaseHeader."Vendor Company Name", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Vendor Company Name"), EDocumentPurchaseHeader.TableCaption, 'CRONUS International', EDocumentPurchaseHeader."Vendor Company Name")); - Assert.AreEqual('Main Street, 14', EDocumentPurchaseHeader."Vendor Address", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Vendor Address"), EDocumentPurchaseHeader.TableCaption, 'Main Street, 14', EDocumentPurchaseHeader."Vendor Address")); - Assert.AreEqual('GB123456789', EDocumentPurchaseHeader."Vendor VAT Id", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Vendor VAT Id"), EDocumentPurchaseHeader.TableCaption, 'GB123456789', EDocumentPurchaseHeader."Vendor VAT Id")); - Assert.AreEqual('Jim Olive', EDocumentPurchaseHeader."Vendor Contact Name", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Vendor Contact Name"), EDocumentPurchaseHeader.TableCaption, 'Jim Olive', EDocumentPurchaseHeader."Vendor Contact Name")); - Assert.AreEqual('The Cannon Group PLC', EDocumentPurchaseHeader."Customer Company Name", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Customer Company Name"), EDocumentPurchaseHeader.TableCaption, 'The Cannon Group PLC', EDocumentPurchaseHeader."Customer Company Name")); - Assert.AreEqual('GB789456278', EDocumentPurchaseHeader."Customer VAT Id", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Customer VAT Id"), EDocumentPurchaseHeader.TableCaption, 'GB789456278', EDocumentPurchaseHeader."Customer VAT Id")); - Assert.AreEqual('192 Market Square', EDocumentPurchaseHeader."Customer Address", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseHeader.FieldCaption("Customer Address"), EDocumentPurchaseHeader.TableCaption, '192 Market Square', EDocumentPurchaseHeader."Customer Address")); + Assert.AreEqual(SalesInvoiceNoTok, EDocumentPurchaseHeader."Sales Invoice No.", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Sales Invoice No."), EDocumentPurchaseHeader.TableCaption(), SalesInvoiceNoTok, EDocumentPurchaseHeader."Sales Invoice No.")); + Assert.AreEqual(MockDate, EDocumentPurchaseHeader."Document Date", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Document Date"), EDocumentPurchaseHeader.TableCaption(), MockDate, EDocumentPurchaseHeader."Document Date")); + Assert.AreEqual(CalcDate('<+1M>', MockDate), EDocumentPurchaseHeader."Due Date", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Due Date"), EDocumentPurchaseHeader.TableCaption(), CalcDate('<+1M>', MockDate), EDocumentPurchaseHeader."Due Date")); + Assert.AreEqual(MockCurrencyCode, EDocumentPurchaseHeader."Currency Code", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Currency Code"), EDocumentPurchaseHeader.TableCaption(), MockCurrencyCode, EDocumentPurchaseHeader."Currency Code")); + Assert.AreEqual(PurchaseorderNoTok, EDocumentPurchaseHeader."Purchase Order No.", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Purchase Order No."), EDocumentPurchaseHeader.TableCaption(), PurchaseorderNoTok, EDocumentPurchaseHeader."Purchase Order No.")); + Assert.AreEqual('CRONUS International', EDocumentPurchaseHeader."Vendor Company Name", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Vendor Company Name"), EDocumentPurchaseHeader.TableCaption(), 'CRONUS International', EDocumentPurchaseHeader."Vendor Company Name")); + Assert.AreEqual('Main Street, 14', EDocumentPurchaseHeader."Vendor Address", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Vendor Address"), EDocumentPurchaseHeader.TableCaption(), 'Main Street, 14', EDocumentPurchaseHeader."Vendor Address")); + Assert.AreEqual('GB123456789', EDocumentPurchaseHeader."Vendor VAT Id", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Vendor VAT Id"), EDocumentPurchaseHeader.TableCaption(), 'GB123456789', EDocumentPurchaseHeader."Vendor VAT Id")); + Assert.AreEqual('Jim Olive', EDocumentPurchaseHeader."Vendor Contact Name", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Vendor Contact Name"), EDocumentPurchaseHeader.TableCaption(), 'Jim Olive', EDocumentPurchaseHeader."Vendor Contact Name")); + Assert.AreEqual('The Cannon Group PLC', EDocumentPurchaseHeader."Customer Company Name", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Customer Company Name"), EDocumentPurchaseHeader.TableCaption(), 'The Cannon Group PLC', EDocumentPurchaseHeader."Customer Company Name")); + Assert.AreEqual('GB789456278', EDocumentPurchaseHeader."Customer VAT Id", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Customer VAT Id"), EDocumentPurchaseHeader.TableCaption(), 'GB789456278', EDocumentPurchaseHeader."Customer VAT Id")); + Assert.AreEqual('192 Market Square', EDocumentPurchaseHeader."Customer Address", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Customer Address"), EDocumentPurchaseHeader.TableCaption(), '192 Market Square', EDocumentPurchaseHeader."Customer Address")); EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocumentEntryNo); EDocumentPurchaseLine.FindSet(); - Assert.AreEqual(1, EDocumentPurchaseLine."Quantity", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Quantity"), EDocumentPurchaseLine.TableCaption, 1, EDocumentPurchaseLine."Quantity")); - Assert.AreEqual(UnitOfMeasureCodeTok, EDocumentPurchaseLine."Unit of Measure", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Unit of Measure"), EDocumentPurchaseLine.TableCaption, UnitOfMeasureCodeTok, EDocumentPurchaseLine."Unit of Measure")); - Assert.AreEqual(4000, EDocumentPurchaseLine."Sub Total", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Sub Total"), EDocumentPurchaseLine.TableCaption, 4000, EDocumentPurchaseLine."Sub Total")); - Assert.AreEqual(MockCurrencyCode, EDocumentPurchaseLine."Currency Code", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Currency Code"), EDocumentPurchaseLine.TableCaption, MockCurrencyCode, EDocumentPurchaseLine."Currency Code")); - Assert.AreEqual(0, EDocumentPurchaseLine."Total Discount", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Total Discount"), EDocumentPurchaseLine.TableCaption, 0, EDocumentPurchaseLine."Total Discount")); - Assert.AreEqual('Bicycle', EDocumentPurchaseLine.Description, StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption(Description), EDocumentPurchaseLine.TableCaption, 'Bicycle', EDocumentPurchaseLine.Description)); - Assert.AreEqual('1000', EDocumentPurchaseLine."Product Code", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Product Code"), EDocumentPurchaseLine.TableCaption, '1000', EDocumentPurchaseLine."Product Code")); - Assert.AreEqual(25, EDocumentPurchaseLine."VAT Rate", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("VAT Rate"), EDocumentPurchaseLine.TableCaption, 25, EDocumentPurchaseLine."VAT Rate")); - Assert.AreEqual(4000, EDocumentPurchaseLine."Unit Price", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Unit Price"), EDocumentPurchaseLine.TableCaption, 4000, EDocumentPurchaseLine."Unit Price")); + Assert.AreEqual(1, EDocumentPurchaseLine."Quantity", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Quantity"), EDocumentPurchaseLine.TableCaption(), 1, EDocumentPurchaseLine."Quantity")); + Assert.AreEqual(UnitOfMeasureCodeTok, EDocumentPurchaseLine."Unit of Measure", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Unit of Measure"), EDocumentPurchaseLine.TableCaption(), UnitOfMeasureCodeTok, EDocumentPurchaseLine."Unit of Measure")); + Assert.AreEqual(4000, EDocumentPurchaseLine."Sub Total", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Sub Total"), EDocumentPurchaseLine.TableCaption(), 4000, EDocumentPurchaseLine."Sub Total")); + Assert.AreEqual(MockCurrencyCode, EDocumentPurchaseLine."Currency Code", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Currency Code"), EDocumentPurchaseLine.TableCaption(), MockCurrencyCode, EDocumentPurchaseLine."Currency Code")); + Assert.AreEqual(0, EDocumentPurchaseLine."Total Discount", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Total Discount"), EDocumentPurchaseLine.TableCaption(), 0, EDocumentPurchaseLine."Total Discount")); + Assert.AreEqual('Bicycle', EDocumentPurchaseLine.Description, StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption(Description), EDocumentPurchaseLine.TableCaption(), 'Bicycle', EDocumentPurchaseLine.Description)); + Assert.AreEqual('1000', EDocumentPurchaseLine."Product Code", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Product Code"), EDocumentPurchaseLine.TableCaption(), '1000', EDocumentPurchaseLine."Product Code")); + Assert.AreEqual(25, EDocumentPurchaseLine."VAT Rate", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("VAT Rate"), EDocumentPurchaseLine.TableCaption(), 25, EDocumentPurchaseLine."VAT Rate")); + Assert.AreEqual(4000, EDocumentPurchaseLine."Unit Price", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Unit Price"), EDocumentPurchaseLine.TableCaption(), 4000, EDocumentPurchaseLine."Unit Price")); EDocumentPurchaseLine.Next(); - Assert.AreEqual(2, EDocumentPurchaseLine."Quantity", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Quantity"), EDocumentPurchaseLine.TableCaption, 2, EDocumentPurchaseLine."Quantity")); - Assert.AreEqual(UnitOfMeasureCodeTok, EDocumentPurchaseLine."Unit of Measure", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Unit of Measure"), EDocumentPurchaseLine.TableCaption, UnitOfMeasureCodeTok, EDocumentPurchaseLine."Unit of Measure")); - Assert.AreEqual(10000, EDocumentPurchaseLine."Sub Total", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Sub Total"), EDocumentPurchaseLine.TableCaption, 10000, EDocumentPurchaseLine."Sub Total")); - Assert.AreEqual(MockCurrencyCode, EDocumentPurchaseLine."Currency Code", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Currency Code"), EDocumentPurchaseLine.TableCaption, MockCurrencyCode, EDocumentPurchaseLine."Currency Code")); - Assert.AreEqual(0, EDocumentPurchaseLine."Total Discount", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Total Discount"), EDocumentPurchaseLine.TableCaption, 0, EDocumentPurchaseLine."Total Discount")); - Assert.AreEqual('Bicycle v2', EDocumentPurchaseLine.Description, StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption(Description), EDocumentPurchaseLine.TableCaption, 'Bicycle v2', EDocumentPurchaseLine.Description)); - Assert.AreEqual('2000', EDocumentPurchaseLine."Product Code", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Product Code"), EDocumentPurchaseLine.TableCaption, '2000', EDocumentPurchaseLine."Product Code")); - Assert.AreEqual(25, EDocumentPurchaseLine."VAT Rate", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("VAT Rate"), EDocumentPurchaseLine.TableCaption, 25, EDocumentPurchaseLine."VAT Rate")); - Assert.AreEqual(5000, EDocumentPurchaseLine."Unit Price", StrSubstNo(MockDataMismatchLbl, EDocumentPurchaseLine.FieldCaption("Unit Price"), EDocumentPurchaseLine.TableCaption, 5000, EDocumentPurchaseLine."Unit Price")); + Assert.AreEqual(2, EDocumentPurchaseLine."Quantity", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Quantity"), EDocumentPurchaseLine.TableCaption(), 2, EDocumentPurchaseLine."Quantity")); + Assert.AreEqual(UnitOfMeasureCodeTok, EDocumentPurchaseLine."Unit of Measure", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Unit of Measure"), EDocumentPurchaseLine.TableCaption(), UnitOfMeasureCodeTok, EDocumentPurchaseLine."Unit of Measure")); + Assert.AreEqual(10000, EDocumentPurchaseLine."Sub Total", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Sub Total"), EDocumentPurchaseLine.TableCaption(), 10000, EDocumentPurchaseLine."Sub Total")); + Assert.AreEqual(MockCurrencyCode, EDocumentPurchaseLine."Currency Code", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Currency Code"), EDocumentPurchaseLine.TableCaption(), MockCurrencyCode, EDocumentPurchaseLine."Currency Code")); + Assert.AreEqual(0, EDocumentPurchaseLine."Total Discount", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Total Discount"), EDocumentPurchaseLine.TableCaption(), 0, EDocumentPurchaseLine."Total Discount")); + Assert.AreEqual('Bicycle v2', EDocumentPurchaseLine.Description, StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption(Description), EDocumentPurchaseLine.TableCaption(), 'Bicycle v2', EDocumentPurchaseLine.Description)); + Assert.AreEqual('2000', EDocumentPurchaseLine."Product Code", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Product Code"), EDocumentPurchaseLine.TableCaption(), '2000', EDocumentPurchaseLine."Product Code")); + Assert.AreEqual(25, EDocumentPurchaseLine."VAT Rate", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("VAT Rate"), EDocumentPurchaseLine.TableCaption(), 25, EDocumentPurchaseLine."VAT Rate")); + Assert.AreEqual(5000, EDocumentPurchaseLine."Unit Price", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Unit Price"), EDocumentPurchaseLine.TableCaption(), 5000, EDocumentPurchaseLine."Unit Price")); end; - internal procedure AssertPurchaseHeader(VendorNo: Code[20]; PurchaseHeader: Record "Purchase Header") + internal procedure AssertPurchaseDocument(VendorNo: Code[20]; PurchaseHeader: Record "Purchase Header"; Item: Record Item) var PurchaseLine: Record "Purchase Line"; - Item1No: Label 'GL00000001', Locked = true; - Item2No: Label 'GL00000003', Locked = true; + Item1NoTok: Label 'GL00000001', Locked = true; + Item2NoTok: Label 'GL00000003', Locked = true; begin - Assert.AreEqual(SalesInvoiceNoTok, PurchaseHeader."Vendor Invoice No.", StrSubstNo(MockDataMismatchLbl, PurchaseHeader.FieldCaption("Vendor Invoice No."), PurchaseHeader.TableCaption, SalesInvoiceNoTok, PurchaseHeader."Vendor Invoice No.")); - Assert.AreEqual(MockDate, PurchaseHeader."Document Date", StrSubstNo(MockDataMismatchLbl, PurchaseHeader.FieldCaption("Document Date"), PurchaseHeader.TableCaption, MockDate, PurchaseHeader."Document Date")); - Assert.AreEqual(CalcDate('<+1M>', MockDate), PurchaseHeader."Due Date", StrSubstNo(MockDataMismatchLbl, PurchaseHeader.FieldCaption("Due Date"), PurchaseHeader.TableCaption, CalcDate('<+1M>', MockDate), PurchaseHeader."Due Date")); - Assert.AreEqual(MockCurrencyCode, PurchaseHeader."Currency Code", StrSubstNo(MockDataMismatchLbl, PurchaseHeader.FieldCaption("Currency Code"), PurchaseHeader.TableCaption, MockCurrencyCode, PurchaseHeader."Currency Code")); - Assert.AreEqual(PurchaseorderNoTok, PurchaseHeader."Vendor Order No.", StrSubstNo(MockDataMismatchLbl, PurchaseHeader.FieldCaption("Vendor Order No."), PurchaseHeader.TableCaption, PurchaseorderNoTok, PurchaseHeader."Vendor Order No.")); - Assert.AreEqual(VendorNo, PurchaseHeader."Buy-from Vendor No.", StrSubstNo(MockDataMismatchLbl, PurchaseHeader.FieldCaption("Buy-from Vendor No."), PurchaseHeader.TableCaption, VendorNo, PurchaseHeader."Buy-from Vendor No.")); + Assert.AreEqual(SalesInvoiceNoTok, PurchaseHeader."Vendor Invoice No.", StrSubstNo(MockDataMismatchErr, PurchaseHeader.FieldCaption("Vendor Invoice No."), PurchaseHeader.TableCaption(), SalesInvoiceNoTok, PurchaseHeader."Vendor Invoice No.")); + Assert.AreEqual(MockDate, PurchaseHeader."Document Date", StrSubstNo(MockDataMismatchErr, PurchaseHeader.FieldCaption("Document Date"), PurchaseHeader.TableCaption(), MockDate, PurchaseHeader."Document Date")); + Assert.AreEqual(CalcDate('<+1M>', MockDate), PurchaseHeader."Due Date", StrSubstNo(MockDataMismatchErr, PurchaseHeader.FieldCaption("Due Date"), PurchaseHeader.TableCaption(), CalcDate('<+1M>', MockDate), PurchaseHeader."Due Date")); + Assert.AreEqual(MockCurrencyCode, PurchaseHeader."Currency Code", StrSubstNo(MockDataMismatchErr, PurchaseHeader.FieldCaption("Currency Code"), PurchaseHeader.TableCaption(), MockCurrencyCode, PurchaseHeader."Currency Code")); + Assert.AreEqual(PurchaseorderNoTok, PurchaseHeader."Vendor Order No.", StrSubstNo(MockDataMismatchErr, PurchaseHeader.FieldCaption("Vendor Order No."), PurchaseHeader.TableCaption(), PurchaseorderNoTok, PurchaseHeader."Vendor Order No.")); + Assert.AreEqual(VendorNo, PurchaseHeader."Buy-from Vendor No.", StrSubstNo(MockDataMismatchErr, PurchaseHeader.FieldCaption("Buy-from Vendor No."), PurchaseHeader.TableCaption(), VendorNo, PurchaseHeader."Buy-from Vendor No.")); PurchaseLine.SetRange("Document Type", PurchaseHeader."Document Type"); PurchaseLine.SetRange("Document No.", PurchaseHeader."No."); PurchaseLine.FindSet(); - Assert.AreEqual(1, PurchaseLine.Quantity, StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption(Quantity), PurchaseLine.TableCaption, 1, PurchaseLine.Quantity)); - Assert.AreEqual(4000, PurchaseLine."Direct Unit Cost", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("Direct Unit Cost"), PurchaseLine.TableCaption, 4000, PurchaseLine."Direct Unit Cost")); - Assert.AreEqual(MockCurrencyCode, PurchaseLine."Currency Code", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("Currency Code"), PurchaseLine.TableCaption, MockCurrencyCode, PurchaseLine."Currency Code")); - Assert.AreEqual(0, PurchaseLine."Line Discount Amount", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("Line Discount Amount"), PurchaseLine.TableCaption, 0, PurchaseLine."Line Discount Amount")); + Assert.AreEqual(1, PurchaseLine.Quantity, StrSubstNo(MockDataMismatchErr, PurchaseLine.FieldCaption(Quantity), PurchaseLine.TableCaption(), 1, PurchaseLine.Quantity)); + Assert.AreEqual(4000, PurchaseLine."Direct Unit Cost", StrSubstNo(MockDataMismatchErr, PurchaseLine.FieldCaption("Direct Unit Cost"), PurchaseLine.TableCaption(), 4000, PurchaseLine."Direct Unit Cost")); + Assert.AreEqual(MockCurrencyCode, PurchaseLine."Currency Code", StrSubstNo(MockDataMismatchErr, PurchaseLine.FieldCaption("Currency Code"), PurchaseLine.TableCaption(), MockCurrencyCode, PurchaseLine."Currency Code")); + Assert.AreEqual(0, PurchaseLine."Line Discount Amount", StrSubstNo(MockDataMismatchErr, PurchaseLine.FieldCaption("Line Discount Amount"), PurchaseLine.TableCaption(), 0, PurchaseLine."Line Discount Amount")); // In the import file we have a name 'Bicycle' but because of Item Cross Reference validation Item description is being used if Item."No." <> '' then begin - Assert.AreEqual('Bicycle', PurchaseLine.Description, StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption(Description), PurchaseLine.TableCaption, Item."No.", PurchaseLine.Description)); - Assert.AreEqual(Item."No.", PurchaseLine."No.", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("No."), PurchaseLine.TableCaption, Item."No.", PurchaseLine."No.")); - Assert.AreEqual(Item."Purch. Unit of Measure", PurchaseLine."Unit of Measure Code", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("Unit of Measure Code"), PurchaseLine.TableCaption, UnitOfMeasureCodeTok, PurchaseLine."Unit of Measure Code")); - end - else begin - Assert.AreEqual(Item1No, PurchaseLine.Description, StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption(Description), PurchaseLine.TableCaption, Item1No, PurchaseLine.Description)); - Assert.AreEqual(Item1No, PurchaseLine."No.", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("No."), PurchaseLine.TableCaption, Item1No, PurchaseLine."No.")); - Assert.AreEqual(UnitOfMeasureCodeTok, PurchaseLine."Unit of Measure Code", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("Unit of Measure Code"), PurchaseLine.TableCaption, UnitOfMeasureCodeTok, PurchaseLine."Unit of Measure Code")); + Assert.AreEqual('Bicycle', PurchaseLine.Description, StrSubstNo(MockDataMismatchErr, PurchaseLine.FieldCaption(Description), PurchaseLine.TableCaption(), Item."No.", PurchaseLine.Description)); + Assert.AreEqual(Item."No.", PurchaseLine."No.", StrSubstNo(MockDataMismatchErr, PurchaseLine.FieldCaption("No."), PurchaseLine.TableCaption(), Item."No.", PurchaseLine."No.")); + Assert.AreEqual(Item."Purch. Unit of Measure", PurchaseLine."Unit of Measure Code", StrSubstNo(MockDataMismatchErr, PurchaseLine.FieldCaption("Unit of Measure Code"), PurchaseLine.TableCaption(), UnitOfMeasureCodeTok, PurchaseLine."Unit of Measure Code")); + end else begin + Assert.AreEqual(Item1NoTok, PurchaseLine.Description, StrSubstNo(MockDataMismatchErr, PurchaseLine.FieldCaption(Description), PurchaseLine.TableCaption(), Item1NoTok, PurchaseLine.Description)); + Assert.AreEqual(Item1NoTok, PurchaseLine."No.", StrSubstNo(MockDataMismatchErr, PurchaseLine.FieldCaption("No."), PurchaseLine.TableCaption(), Item1NoTok, PurchaseLine."No.")); + Assert.AreEqual(UnitOfMeasureCodeTok, PurchaseLine."Unit of Measure Code", StrSubstNo(MockDataMismatchErr, PurchaseLine.FieldCaption("Unit of Measure Code"), PurchaseLine.TableCaption(), UnitOfMeasureCodeTok, PurchaseLine."Unit of Measure Code")); end; PurchaseLine.Next(); - Assert.AreEqual(2, PurchaseLine.Quantity, StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption(Quantity), PurchaseLine.TableCaption, 2, PurchaseLine.Quantity)); - Assert.AreEqual(UnitOfMeasureCodeTok, PurchaseLine."Unit of Measure Code", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("Unit of Measure Code"), PurchaseLine.TableCaption, UnitOfMeasureCodeTok, PurchaseLine."Unit of Measure Code")); - Assert.AreEqual(5000, PurchaseLine."Direct Unit Cost", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("Direct Unit Cost"), PurchaseLine.TableCaption, 5000, PurchaseLine."Direct Unit Cost")); - Assert.AreEqual(MockCurrencyCode, PurchaseLine."Currency Code", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("Currency Code"), PurchaseLine.TableCaption, MockCurrencyCode, PurchaseLine."Currency Code")); - Assert.AreEqual(0, PurchaseLine."Line Discount Amount", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("Line Discount Amount"), PurchaseLine.TableCaption, 0, PurchaseLine."Line Discount Amount")); + Assert.AreEqual(2, PurchaseLine.Quantity, StrSubstNo(MockDataMismatchErr, PurchaseLine.FieldCaption(Quantity), PurchaseLine.TableCaption(), 2, PurchaseLine.Quantity)); + Assert.AreEqual(UnitOfMeasureCodeTok, PurchaseLine."Unit of Measure Code", StrSubstNo(MockDataMismatchErr, PurchaseLine.FieldCaption("Unit of Measure Code"), PurchaseLine.TableCaption(), UnitOfMeasureCodeTok, PurchaseLine."Unit of Measure Code")); + Assert.AreEqual(5000, PurchaseLine."Direct Unit Cost", StrSubstNo(MockDataMismatchErr, PurchaseLine.FieldCaption("Direct Unit Cost"), PurchaseLine.TableCaption(), 5000, PurchaseLine."Direct Unit Cost")); + Assert.AreEqual(MockCurrencyCode, PurchaseLine."Currency Code", StrSubstNo(MockDataMismatchErr, PurchaseLine.FieldCaption("Currency Code"), PurchaseLine.TableCaption(), MockCurrencyCode, PurchaseLine."Currency Code")); + Assert.AreEqual(0, PurchaseLine."Line Discount Amount", StrSubstNo(MockDataMismatchErr, PurchaseLine.FieldCaption("Line Discount Amount"), PurchaseLine.TableCaption(), 0, PurchaseLine."Line Discount Amount")); // In the import file we have a name 'Bicycle v2' but because of Item Cross Reference validation Item description is being used - Assert.AreEqual(Item2No, PurchaseLine.Description, StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption(Description), PurchaseLine.TableCaption, Item2No, PurchaseLine.Description)); - Assert.AreEqual(Item2No, PurchaseLine."No.", StrSubstNo(MockDataMismatchLbl, PurchaseLine.FieldCaption("No."), PurchaseLine.TableCaption, Item2No, PurchaseLine."No.")); - end; - - internal procedure SetItem(Item: Record Item) - begin - this.Item := Item; + Assert.AreEqual(Item2NoTok, PurchaseLine.Description, StrSubstNo(MockDataMismatchErr, PurchaseLine.FieldCaption(Description), PurchaseLine.TableCaption(), Item2NoTok, PurchaseLine.Description)); + Assert.AreEqual(Item2NoTok, PurchaseLine."No.", StrSubstNo(MockDataMismatchErr, PurchaseLine.FieldCaption("No."), PurchaseLine.TableCaption(), Item2NoTok, PurchaseLine."No.")); end; procedure SetMockDate(MockDate: Date) From 7080ef075f77da4e43cba6e414f6e73393bde4f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Mon, 18 Aug 2025 14:58:13 +0300 Subject: [PATCH 26/28] Centralizes draft reset on purchase header Moves draft cleanup from the document level to the purchase header, deleting header, lines, header mappings, and additional line fields in one place. Updates import handlers to fetch the purchase header from the document and invoke the new reset, improving encapsulation and avoiding partial cleanup. Aligns a history-mapping call with the updated API naming for clarity. --- .../app/src/Document/EDocument.Table.al | 29 ------------------- .../Import/EDocEmptyDraft.Codeunit.al | 5 +++- .../EDocPurchaseDocumentHelper.Codeunit.al | 2 +- .../Purchase/EDocumentPurchaseHeader.Table.al | 26 ++++++++++++++++- .../EDocumentADIHandler.Codeunit.al | 5 +++- .../EDocumentPEPPOLHandler.Codeunit.al | 5 +++- 6 files changed, 38 insertions(+), 34 deletions(-) diff --git a/Apps/W1/EDocument/app/src/Document/EDocument.Table.al b/Apps/W1/EDocument/app/src/Document/EDocument.Table.al index e89346c052..2e9537acb3 100644 --- a/Apps/W1/EDocument/app/src/Document/EDocument.Table.al +++ b/Apps/W1/EDocument/app/src/Document/EDocument.Table.al @@ -480,35 +480,6 @@ table 6121 "E-Document" end; #endif - /// - /// Resets the draft data associated with the E-Document by removing all created lines and mappings. - /// This procedure cleans up all purchase-related draft data including purchase header, lines, - /// header mappings, and additional line fields that were created during the import process. - /// - procedure ResetDraft() - var - EDocumentPurchaseHeader: Record "E-Document Purchase Header"; - EDocumentPurchaseLine: Record "E-Document Purchase Line"; - EDocumentHeaderMapping: Record "E-Document Header Mapping"; - EDocumentLineField: Record "E-Document Line - Field"; - begin - EDocumentPurchaseHeader.GetFromEDocument(Rec); - if not EDocumentPurchaseHeader.IsEmpty() then - EDocumentPurchaseHeader.Delete(true); - - EDocumentPurchaseLine.SetRange("E-Document Entry No.", Rec."Entry No"); - if not EDocumentPurchaseLine.IsEmpty() then - EDocumentPurchaseLine.DeleteAll(true); - - EDocumentHeaderMapping.SetRange("E-Document Entry No.", Rec."Entry No"); - if not EDocumentHeaderMapping.IsEmpty() then - EDocumentHeaderMapping.DeleteAll(true); - - EDocumentLineField.SetRange("E-Document Entry No.", Rec."Entry No"); - if not EDocumentLineField.IsEmpty() then - EDocumentLineField.DeleteAll(true); - end; - internal procedure ToString(): Text begin exit(StrSubstNo(ToStringLbl, SystemId, "Document Record ID", "Workflow Step Instance ID", "Job Queue Entry ID")); diff --git a/Apps/W1/EDocument/app/src/Processing/Import/EDocEmptyDraft.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/EDocEmptyDraft.Codeunit.al index 581315f71d..6a3d3c3713 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/EDocEmptyDraft.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/EDocEmptyDraft.Codeunit.al @@ -52,7 +52,10 @@ codeunit 6193 "E-Doc. Empty Draft" implements IStructureReceivedEDocument, IStru end; procedure ResetDraft(EDocument: Record "E-Document") + var + EDocPurchaseHeader: Record "E-Document Purchase Header"; begin - EDocument.ResetDraft(); + EDocPurchaseHeader.GetFromEDocument(EDocument); + EDocPurchaseHeader.ResetDraft(); end; } \ No newline at end of file diff --git a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocPurchaseDocumentHelper.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocPurchaseDocumentHelper.Codeunit.al index de5718fe69..a38cea5f8c 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocPurchaseDocumentHelper.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocPurchaseDocumentHelper.Codeunit.al @@ -167,7 +167,7 @@ codeunit 6185 "E-Doc Purchase Document Helper" PurchaseLine.Validate("Shortcut Dimension 1 Code", EDocumentPurchaseLine."[BC] Shortcut Dimension 1 Code"); PurchaseLine.Validate("Shortcut Dimension 2 Code", EDocumentPurchaseLine."[BC] Shortcut Dimension 2 Code"); - EDocumentPurchaseHistMapping.ApplyHistoryValuesToPurchaseLine(EDocumentPurchaseLine, PurchaseLine); + EDocumentPurchaseHistMapping.ApplyAdditionalFieldsFromHistoryToPurchaseLine(EDocumentPurchaseLine, PurchaseLine); PurchaseLine.Insert(false); // Track changes for history diff --git a/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al b/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al index 9025973338..2497fbdda8 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al @@ -6,9 +6,10 @@ namespace Microsoft.EServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.OrderMatch.Copilot; -using System.Telemetry; +using Microsoft.eServices.EDocument.Processing.Import; using Microsoft.Purchases.Document; using Microsoft.Purchases.Vendor; +using System.Telemetry; table 6100 "E-Document Purchase Header" { @@ -262,6 +263,29 @@ table 6100 "E-Document Purchase Header" end; end; + /// + /// Resets the draft data associated with the E-Document Purchase Header by removing all created lines and mappings. + /// This procedure cleans up all purchase-related draft data including purchase header, lines, + /// header mappings, and additional line fields that were created during the import process. + /// + procedure ResetDraft() + var + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + EDocumentHeaderMapping: Record "E-Document Header Mapping"; + EDocumentLineField: Record "E-Document Line - Field"; + begin + Rec.Delete(true); + + EDocumentPurchaseLine.SetRange("E-Document Entry No.", Rec."E-Document Entry No."); + EDocumentPurchaseLine.DeleteAll(true); + + EDocumentHeaderMapping.SetRange("E-Document Entry No.", Rec."E-Document Entry No."); + EDocumentHeaderMapping.DeleteAll(true); + + EDocumentLineField.SetRange("E-Document Entry No.", Rec."E-Document Entry No."); + EDocumentLineField.DeleteAll(true); + end; + var EDocPOCopilotMatching: Codeunit "E-Doc. PO Copilot Matching"; FeatureTelemetry: Codeunit "Feature Telemetry"; diff --git a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al index 34dd3ac4e4..3bd2d25965 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al @@ -132,8 +132,11 @@ codeunit 6174 "E-Document ADI Handler" implements IStructureReceivedEDocument, I end; procedure ResetDraft(EDocument: Record "E-Document") + var + EDocPurchaseHeader: Record "E-Document Purchase Header"; begin - EDocument.ResetDraft(); + EDocPurchaseHeader.GetFromEDocument(EDocument); + EDocPurchaseHeader.ResetDraft(); end; local procedure PopulateEDocumentPurchaseLines(ItemsArray: JsonArray; EDocumentEntryNo: Integer; var TempEDocPurchaseLine: Record "E-Document Purchase Line" temporary) diff --git a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al index 308e2da6f6..8974a43601 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al @@ -235,7 +235,10 @@ codeunit 6173 "E-Document PEPPOL Handler" implements IStructuredFormatReader end; procedure ResetDraft(EDocument: Record "E-Document") + var + EDocPurchaseHeader: Record "E-Document Purchase Header"; begin - EDocument.ResetDraft(); + EDocPurchaseHeader.GetFromEDocument(EDocument); + EDocPurchaseHeader.ResetDraft(); end; } \ No newline at end of file From 4071f21928868c19e6745a498266695230b11d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Tue, 19 Aug 2025 16:42:53 +0300 Subject: [PATCH 27/28] Centralizes draft cleanup; adds invoice retrieval Moves draft cleanup to the record delete trigger to always remove related lines and mappings when discarding a draft, removing the custom reset method and updating callers to delete the header directly. Splits purchase document lookup by exposing separate methods for order and invoice. The caller now chooses based on vendor setup, decoupling provider logic from vendor configuration and avoiding provider-level validation errors. Minor refactor in draft preparation to handle the new lookup and streamline flow. --- .../Import/EDocEmptyDraft.Codeunit.al | 2 +- .../PrepareDraft/EDocProviders.Codeunit.al | 19 ++++------ .../PreparePurchaseEDocDraft.Codeunit.al | 15 +++++--- .../Purchase/EDocumentPurchaseHeader.Table.al | 36 +++++++------------ .../EDocumentADIHandler.Codeunit.al | 2 +- .../EDocumentPEPPOLHandler.Codeunit.al | 2 +- .../IPurchaseOrderProvider.Interface.al | 7 ++++ 7 files changed, 39 insertions(+), 44 deletions(-) diff --git a/Apps/W1/EDocument/app/src/Processing/Import/EDocEmptyDraft.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/EDocEmptyDraft.Codeunit.al index 6a3d3c3713..5bc2afa1e7 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/EDocEmptyDraft.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/EDocEmptyDraft.Codeunit.al @@ -56,6 +56,6 @@ codeunit 6193 "E-Doc. Empty Draft" implements IStructureReceivedEDocument, IStru EDocPurchaseHeader: Record "E-Document Purchase Header"; begin EDocPurchaseHeader.GetFromEDocument(EDocument); - EDocPurchaseHeader.ResetDraft(); + EDocPurchaseHeader.Delete(true); end; } \ No newline at end of file diff --git a/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/EDocProviders.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/EDocProviders.Codeunit.al index 5cbc4810b6..5ef7a6bdf9 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/EDocProviders.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/EDocProviders.Codeunit.al @@ -135,20 +135,13 @@ codeunit 6124 "E-Doc. Providers" implements IPurchaseLineProvider, IUnitOfMeasur end; procedure GetPurchaseOrder(EDocumentPurchaseHeader: Record "E-Document Purchase Header") PurchaseHeader: Record "Purchase Header" - var - Vendor: Record Vendor; - VendorReceiveEDocumentErr: Label 'The %1 = %2 has an invalid %3 value: %4. Please correct the vendor setup.', Comment = '%1 = Vendor Table Caption, %2 = Vendor No., %3 = Receive E-Document To Field Caption, %4 = Receive E-Document To Value'; begin - Vendor.SetLoadFields("Receive E-Document To"); - Vendor.Get(EDocumentPurchaseHeader."[BC] Vendor No."); - case Vendor."Receive E-Document To" of - Vendor."Receive E-Document To"::"Purchase Invoice": - if PurchaseHeader.Get("Purchase Document Type"::Invoice, EDocumentPurchaseHeader."Purchase Order No.") then; - Vendor."Receive E-Document To"::"Purchase Order": - if PurchaseHeader.Get("Purchase Document Type"::Order, EDocumentPurchaseHeader."Purchase Order No.") then; - else - Error(VendorReceiveEDocumentErr, Vendor.TableCaption(), Vendor."No.", Vendor.FieldCaption("Receive E-Document To"), Format(Vendor."Receive E-Document To")); - end; + if PurchaseHeader.Get("Purchase Document Type"::Order, EDocumentPurchaseHeader."Purchase Order No.") then; + end; + + procedure GetPurchaseInvoice(EDocumentPurchaseHeader: Record "E-Document Purchase Header") PurchaseHeader: Record "Purchase Header" + begin + if PurchaseHeader.Get("Purchase Document Type"::Invoice, EDocumentPurchaseHeader."Purchase Order No.") then; end; local procedure GetPurchaseLineItemRef(EDocumentPurchaseLine: Record "E-Document Purchase Line"; var ItemReference: Record "Item Reference"): Boolean diff --git a/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al index 4ab56b4a02..86f33067b7 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al @@ -26,7 +26,7 @@ codeunit 6125 "Prepare Purchase E-Doc. Draft" implements IProcessStructuredData EDocumentPurchaseLine: Record "E-Document Purchase Line"; UnitOfMeasure: Record "Unit of Measure"; Vendor: Record Vendor; - PurchaseOrder: Record "Purchase Header"; + PurchaseHeader: Record "Purchase Header"; EDocVendorAssignmentHistory: Record "E-Doc. Vendor Assign. History"; EDocPurchaseLineHistory: Record "E-Doc. Purchase Line History"; EDocPurchaseHistMapping: Codeunit "E-Doc. Purchase Hist. Mapping"; @@ -48,11 +48,16 @@ codeunit 6125 "Prepare Purchase E-Doc. Draft" implements IProcessStructuredData EDocumentPurchaseHeader."[BC] Vendor No." := Vendor."No."; end; - PurchaseOrder := IPurchaseOrderProvider.GetPurchaseOrder(EDocumentPurchaseHeader); + case Vendor."Receive E-Document To" of + Vendor."Receive E-Document To"::"Purchase Invoice": + PurchaseHeader := IPurchaseOrderProvider.GetPurchaseInvoice(EDocumentPurchaseHeader); + Vendor."Receive E-Document To"::"Purchase Order": + PurchaseHeader := IPurchaseOrderProvider.GetPurchaseOrder(EDocumentPurchaseHeader); + end; - if PurchaseOrder."No." <> '' then begin - PurchaseOrder.TestField("Document Type", "Purchase Document Type"::Order); - EDocumentPurchaseHeader."[BC] Purchase Order No." := PurchaseOrder."No."; + if PurchaseHeader."No." <> '' then begin + PurchaseHeader.TestField("Document Type", "Purchase Document Type"::Order); + EDocumentPurchaseHeader."[BC] Purchase Order No." := PurchaseHeader."No."; EDocumentPurchaseHeader.Modify(); exit("E-Document Type"::"Purchase Order"); end; diff --git a/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al b/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al index 2497fbdda8..85403f3458 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al @@ -244,9 +244,22 @@ table 6100 "E-Document Purchase Header" } trigger OnDelete() + var + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + EDocumentHeaderMapping: Record "E-Document Header Mapping"; + EDocumentLineField: Record "E-Document Line - Field"; begin Session.LogMessage('0000PCQ', DeleteDraftPerformedTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::All, 'Category', EDocPOCopilotMatching.FeatureName()); FeatureTelemetry.LogUsage('0000PCV', EDocPOCopilotMatching.FeatureName(), 'Discard draft'); + + EDocumentPurchaseLine.SetRange("E-Document Entry No.", Rec."E-Document Entry No."); + EDocumentPurchaseLine.DeleteAll(true); + + EDocumentHeaderMapping.SetRange("E-Document Entry No.", Rec."E-Document Entry No."); + EDocumentHeaderMapping.DeleteAll(true); + + EDocumentLineField.SetRange("E-Document Entry No.", Rec."E-Document Entry No."); + EDocumentLineField.DeleteAll(true); end; procedure GetFromEDocument(EDocument: Record "E-Document") @@ -263,29 +276,6 @@ table 6100 "E-Document Purchase Header" end; end; - /// - /// Resets the draft data associated with the E-Document Purchase Header by removing all created lines and mappings. - /// This procedure cleans up all purchase-related draft data including purchase header, lines, - /// header mappings, and additional line fields that were created during the import process. - /// - procedure ResetDraft() - var - EDocumentPurchaseLine: Record "E-Document Purchase Line"; - EDocumentHeaderMapping: Record "E-Document Header Mapping"; - EDocumentLineField: Record "E-Document Line - Field"; - begin - Rec.Delete(true); - - EDocumentPurchaseLine.SetRange("E-Document Entry No.", Rec."E-Document Entry No."); - EDocumentPurchaseLine.DeleteAll(true); - - EDocumentHeaderMapping.SetRange("E-Document Entry No.", Rec."E-Document Entry No."); - EDocumentHeaderMapping.DeleteAll(true); - - EDocumentLineField.SetRange("E-Document Entry No.", Rec."E-Document Entry No."); - EDocumentLineField.DeleteAll(true); - end; - var EDocPOCopilotMatching: Codeunit "E-Doc. PO Copilot Matching"; FeatureTelemetry: Codeunit "Feature Telemetry"; diff --git a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al index 3bd2d25965..03378188f3 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al @@ -136,7 +136,7 @@ codeunit 6174 "E-Document ADI Handler" implements IStructureReceivedEDocument, I EDocPurchaseHeader: Record "E-Document Purchase Header"; begin EDocPurchaseHeader.GetFromEDocument(EDocument); - EDocPurchaseHeader.ResetDraft(); + EDocPurchaseHeader.Delete(true); end; local procedure PopulateEDocumentPurchaseLines(ItemsArray: JsonArray; EDocumentEntryNo: Integer; var TempEDocPurchaseLine: Record "E-Document Purchase Line" temporary) diff --git a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al index 8974a43601..132d442c1e 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al @@ -239,6 +239,6 @@ codeunit 6173 "E-Document PEPPOL Handler" implements IStructuredFormatReader EDocPurchaseHeader: Record "E-Document Purchase Header"; begin EDocPurchaseHeader.GetFromEDocument(EDocument); - EDocPurchaseHeader.ResetDraft(); + EDocPurchaseHeader.Delete(true); end; } \ No newline at end of file diff --git a/Apps/W1/EDocument/app/src/Processing/Interfaces/IPurchaseOrderProvider.Interface.al b/Apps/W1/EDocument/app/src/Processing/Interfaces/IPurchaseOrderProvider.Interface.al index 0d8745628c..8d996ba1c5 100644 --- a/Apps/W1/EDocument/app/src/Processing/Interfaces/IPurchaseOrderProvider.Interface.al +++ b/Apps/W1/EDocument/app/src/Processing/Interfaces/IPurchaseOrderProvider.Interface.al @@ -18,4 +18,11 @@ interface IPurchaseOrderProvider /// The E-Document purchase header record containing order details. /// A Purchase Header record matching the provided E-Document purchase header. procedure GetPurchaseOrder(EDocumentPurchaseHeader: Record "E-Document Purchase Header"): Record "Purchase Header"; + + /// + /// Retrieves the corresponding purchase invoice for a given E-Document purchase header. + /// + /// The E-Document purchase header record containing invoice details. + /// A Purchase Header record matching the provided E-Document purchase header. + procedure GetPurchaseInvoice(EDocumentPurchaseHeader: Record "E-Document Purchase Header"): Record "Purchase Header"; } \ No newline at end of file From f958008c7feb23e43eef001f99d258190b0d6631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Thu, 21 Aug 2025 16:04:29 +0300 Subject: [PATCH 28/28] Respects vendor target: order vs invoice in drafts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updates draft preparation to honor the vendor’s target (order or invoice), validating document type and returning the corresponding e-document type instead of always using order. Ensures purchase header data is initialized from the e-document before processing to prevent missing header values. Adapts tests to the parameter-based import call and updates helpers/mocks accordingly. Adds attachment dependency for purchase invoice creation. --- .../FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al | 1 + .../FinishDraft/EDocPurchaseDocumentHelper.Codeunit.al | 1 + .../PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al | 9 +++++++-- .../test/src/Processing/EDocPDFMock.Codeunit.al | 6 ++++-- .../src/Processing/EDocumentStructuredTests.Codeunit.al | 6 ++++-- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al index f6a07368fe..0cda9a65a7 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al @@ -8,6 +8,7 @@ using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument.Processing.Interfaces; using Microsoft.Finance.Dimension; +using Microsoft.Foundation.Attachment; using Microsoft.Purchases.Document; /// diff --git a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocPurchaseDocumentHelper.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocPurchaseDocumentHelper.Codeunit.al index a38cea5f8c..07b26c8a72 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocPurchaseDocumentHelper.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocPurchaseDocumentHelper.Codeunit.al @@ -45,6 +45,7 @@ codeunit 6185 "E-Doc Purchase Document Helper" PurchaseDocumentExistsErr: Label 'A purchase %1 with external document number %2 already exists for vendor %3.', Comment = '%1 = Purchase Document Type, %2 = External Document No., %3 = Vendor No.'; ExternalDocumentNo: Code[35]; begin + EDocumentPurchaseHeader.GetFromEDocument(EDocument); case DocumentType of "Purchase Document Type"::Invoice: begin diff --git a/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al index 86f33067b7..dd82cbd833 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al @@ -56,10 +56,15 @@ codeunit 6125 "Prepare Purchase E-Doc. Draft" implements IProcessStructuredData end; if PurchaseHeader."No." <> '' then begin - PurchaseHeader.TestField("Document Type", "Purchase Document Type"::Order); + case Vendor."Receive E-Document To" of + Vendor."Receive E-Document To"::"Purchase Invoice": + PurchaseHeader.TestField("Document Type", "Purchase Document Type"::Invoice); + Vendor."Receive E-Document To"::"Purchase Order": + PurchaseHeader.TestField("Document Type", "Purchase Document Type"::Order); + end; EDocumentPurchaseHeader."[BC] Purchase Order No." := PurchaseHeader."No."; EDocumentPurchaseHeader.Modify(); - exit("E-Document Type"::"Purchase Order"); + exit(PurchaseHeader."Document Type" = "Purchase Document Type"::Order ? "E-Document Type"::"Purchase Order" : "E-Document Type"::"Purchase Invoice"); end; if EDocPurchaseHistMapping.FindRelatedPurchaseHeaderInHistory(EDocument, EDocVendorAssignmentHistory) then EDocPurchaseHistMapping.UpdateMissingHeaderValuesFromHistory(EDocVendorAssignmentHistory, EDocumentPurchaseHeader); diff --git a/Apps/W1/EDocument/test/src/Processing/EDocPDFMock.Codeunit.al b/Apps/W1/EDocument/test/src/Processing/EDocPDFMock.Codeunit.al index 34a7851b3e..fd76b624c1 100644 --- a/Apps/W1/EDocument/test/src/Processing/EDocPDFMock.Codeunit.al +++ b/Apps/W1/EDocument/test/src/Processing/EDocPDFMock.Codeunit.al @@ -37,5 +37,7 @@ codeunit 139782 "E-Doc PDF Mock" implements IStructureReceivedEDocument, IStruct begin exit("E-Doc. Read into Draft"::Unspecified); end; - -} + procedure ResetDraft(EDocument: Record "E-Document") + begin + end; +} \ No newline at end of file diff --git a/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al b/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al index fddf9ba775..a7a3aa5640 100644 --- a/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al +++ b/Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests.Codeunit.al @@ -152,6 +152,7 @@ codeunit 139891 "E-Document Structured Tests" EDocument: Record "E-Document"; PurchaseHeader: Record "Purchase Header"; Item: Record Item; + EDocImportParameters: Record "E-Doc. Import Parameters"; EDocImport: Codeunit "E-Doc. Import"; EDocumentProcessing: Codeunit "E-Document Processing"; DataTypeManagement: Codeunit "Data Type Management"; @@ -169,7 +170,7 @@ codeunit 139891 "E-Document Structured Tests" ProcessEDocumentToStep(EDocument, "Import E-Document Steps"::"Prepare draft"); // [GIVEN] A generic item is created for manual assignment - LibraryEDoc.CreateGenericItem(Item); + LibraryEDoc.CreateGenericItem(Item,''); // [WHEN] The draft document is opened and modified through UI EDocPurchaseDraft.OpenEdit(); @@ -179,7 +180,8 @@ codeunit 139891 "E-Document Structured Tests" EDocPurchaseDraft.Lines.Next(); // [WHEN] The processing is completed to finish draft step - EDocImport.ProcessIncomingEDocument(EDocument, "Import E-Document Steps"::"Finish draft"); + EDocImportParameters."Step to Run" := "Import E-Document Steps"::"Finish draft"; + EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParameters); EDocument.Get(EDocument."Entry No"); // [WHEN] The final purchase record is retrieved