diff --git a/Apps/W1/EDocument/app/src/Document/EDocument.Table.al b/Apps/W1/EDocument/app/src/Document/EDocument.Table.al index 3a9f72288a..2e9537acb3 100644 --- a/Apps/W1/EDocument/app/src/Document/EDocument.Table.al +++ b/Apps/W1/EDocument/app/src/Document/EDocument.Table.al @@ -479,6 +479,7 @@ table 6121 "E-Document" exit(GetEDocumentServiceStatus()."Import Processing Status"); end; #endif + 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/Document/EDocumentType.Enum.al b/Apps/W1/EDocument/app/src/Document/EDocumentType.Enum.al index 08b282174e..72a79fb517 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..691adeec45 --- /dev/null +++ b/Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al @@ -0,0 +1,126 @@ +// ------------------------------------------------------------------------------------------------ +// 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 6177 "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"; + 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; + + /// + /// 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; + 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; + + /// + /// 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; + begin + if not XMLDocument.SelectSingleNode(Path, XMLNamespaces, XMLNode) then + exit; + + if XMLNode.AsXmlElement().InnerText() <> '' then + 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; + begin + if not XMLDocument.SelectSingleNode(Path, XMLNamespaces, XMLNode) then + exit; + + 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; +} 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/EDocEmptyDraft.Codeunit.al b/Apps/W1/EDocument/app/src/Processing/Import/EDocEmptyDraft.Codeunit.al index 415229e663..5bc2afa1e7 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,12 @@ codeunit 6193 "E-Doc. Empty Draft" implements IStructureReceivedEDocument, IStru begin Error(NoDataErr); end; + + procedure ResetDraft(EDocument: Record "E-Document") + var + EDocPurchaseHeader: Record "E-Document Purchase Header"; + begin + EDocPurchaseHeader.GetFromEDocument(EDocument); + EDocPurchaseHeader.Delete(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 new file mode 100644 index 0000000000..c83e29b1fb --- /dev/null +++ b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocCreatePurchCrMemo.Codeunit.al @@ -0,0 +1,78 @@ +// ------------------------------------------------------------------------------------------------ +// 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.eServices.EDocument.Processing.Interfaces; +using Microsoft.Finance.Dimension; +using Microsoft.Purchases.Document; + +/// +/// 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 + EDocImpSessionTelemetry: Codeunit "E-Doc. Imp. Session Telemetry"; + + /// + /// 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"; + EDocPurchaseDocumentHelper: Codeunit "E-Doc Purchase Document Helper"; + IEDocumentFinishPurchaseDraft: Interface IEDocumentCreatePurchaseCreditMemo; + begin + EDocumentPurchaseHeader.GetFromEDocument(EDocument); + IEDocumentFinishPurchaseDraft := EDocImportParameters."Processing Customizations"; + PurchaseHeader := IEDocumentFinishPurchaseDraft.CreatePurchaseCreditMemo(EDocument); + + // Post document validation - Silently emit telemetry + if not EDocPurchaseDocumentHelper.TryValidateDocumentTotals(PurchaseHeader) then + EDocImpSessionTelemetry.SetBool('Totals Validation Failed', true); + + exit(PurchaseHeader.RecordId); + end; + + /// + /// 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 + 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; + + /// + /// 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") PurchaseHeader: Record "Purchase Header" + var + EDocPurchaseDocumentHelper: Codeunit "E-Doc Purchase Document Helper"; + begin + PurchaseHeader := EDocPurchaseDocumentHelper.CreatePurchaseDocumentHeader(EDocument, "Purchase Document Type"::"Credit Memo"); + exit(PurchaseHeader); + end; +} 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 79c9f369af..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 @@ -5,15 +5,11 @@ 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.eServices.EDocument.Processing.Interfaces; +using Microsoft.Finance.Dimension; using Microsoft.Foundation.Attachment; +using Microsoft.Purchases.Document; /// /// Dealing with the creation of the purchase invoice after the draft has been populated. @@ -25,40 +21,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"; @@ -74,124 +65,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.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."; - - 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.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.ApplyAdditionalFieldsFromHistoryToPurchaseLine(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 2caa2656e3..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,7 +13,8 @@ enum 6105 "E-Doc. Create Purchase Invoice" implements IEDocumentCreatePurchaseIn { Extensible = true; DefaultImplementation = IEDocumentCreatePurchaseInvoice = "E-Doc. Create Purchase Invoice"; - value(0; "Default") - { - } + 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..07b26c8a72 --- /dev/null +++ b/Apps/W1/EDocument/app/src/Processing/Import/FinishDraft/EDocPurchaseDocumentHelper.Codeunit.al @@ -0,0 +1,246 @@ +// ------------------------------------------------------------------------------------------------ +// 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 + EDocumentPurchaseHeader.GetFromEDocument(EDocument); + 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.ApplyAdditionalFieldsFromHistoryToPurchaseLine(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 ba9f84c5c0..9c1b6658ba 100644 --- a/Apps/W1/EDocument/app/src/Processing/Import/ImportEDocumentProcess.Codeunit.al +++ b/Apps/W1/EDocument/app/src/Processing/Import/ImportEDocumentProcess.Codeunit.al @@ -175,6 +175,7 @@ codeunit 6104 "Import E-Document Process" var EDocumentHeaderMapping: Record "E-Document Header Mapping"; IEDocumentFinishDraft: Interface IEDocumentFinishDraft; + IStructuredFormatReader: Interface IStructuredFormatReader; begin case Step of Step::"Finish draft": @@ -184,6 +185,11 @@ codeunit 6104 "Import E-Document Process" Clear(EDocument."Document Record ID"); EDocument.Modify(); end; + Step::"Read into Draft": + begin + IStructuredFormatReader := EDocument."Read into Draft Impl."; + IStructuredFormatReader.ResetDraft(EDocument); + 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 6e7ee356e7..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 @@ -108,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); @@ -136,6 +139,11 @@ codeunit 6124 "E-Doc. Providers" implements IPurchaseLineProvider, IUnitOfMeasur 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 var EDocument: Record "E-Document"; @@ -171,4 +179,14 @@ codeunit 6124 "E-Doc. Providers" implements IPurchaseLineProvider, IUnitOfMeasur exit(true); end; + local procedure GetDefaultUnitOfMeasure(ItemNo: Code[20]) UnitOfMeasureCode: Code[20]; + var + Item: Record Item; + begin + 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 31a5c8abbc..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 @@ -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,13 +48,23 @@ 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 + 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); @@ -80,8 +90,6 @@ codeunit 6125 "Prepare Purchase E-Doc. Draft" implements IProcessStructuredData EDocumentPurchaseLine.Modify(); LogActivitySessionChanges(EDocActivityLogSession); EDocActivityLogSession.CleanUpLogs(); - - until EDocumentPurchaseLine.Next() = 0; // Ask Copilot to try to find fields that are suited to be matched @@ -89,7 +97,7 @@ codeunit 6125 "Prepare Purchase E-Doc. Draft" implements IProcessStructuredData CopilotLineMatching(EDocument."Entry No"); if EDocActivityLogSession.EndSession() then; - exit("E-Document Type"::"Purchase Invoice"); + exit(EDocumentPurchaseHeader."E-Document Type"); end; local procedure LogActivitySessionChanges(EDocActivityLogSession: Codeunit "E-Doc. Activity Log Session") 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..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 @@ -33,6 +33,12 @@ 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 + Clear(Rec."[BC] Item Reference No."); + end; } field("Item Reference No."; Rec."[BC] Item Reference No.") { 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 e9a08397d9..e046107b95 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 @@ -247,7 +247,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/Purchase/EDocumentPurchaseHeader.Table.al b/Apps/W1/EDocument/app/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al index 93bb50f1c9..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 @@ -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" { @@ -207,6 +208,16 @@ 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; + } + 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] @@ -233,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") 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..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 @@ -131,6 +131,14 @@ codeunit 6174 "E-Document ADI Handler" implements IStructureReceivedEDocument, I EDocReadablePurchaseDoc.Run(); end; + procedure ResetDraft(EDocument: Record "E-Document") + var + EDocPurchaseHeader: Record "E-Document Purchase Header"; + begin + EDocPurchaseHeader.GetFromEDocument(EDocument); + EDocPurchaseHeader.Delete(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 1479acb301..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 @@ -4,12 +4,12 @@ // ------------------------------------------------------------------------------------------------ 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; -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,160 @@ 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; + 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; + + foreach LineXMLNode in LineXMLList do begin + Clear(EDocumentPurchaseLine); + EDocumentPurchaseLine.Validate("E-Document Entry No.", EDocumentEntryNo); + EDocumentPurchaseLine."Line No." := EDocumentPurchaseLine.GetNextLineNo(EDocumentEntryNo); + NewLineXML.ReplaceNodes(LineXMLNode); + PopulateEDocumentPurchaseCreditMemoLine(NewLineXML, XmlNamespaces, EDocumentPurchaseLine); + EDocumentPurchaseLine.Insert(false); + 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; + // Add CreditNote namespace for proper header parsing + XmlNamespaces.AddNamespace('cn', CreditNoteNamespaceLbl); - GLSetup.Get(); - - 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; + 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."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"); + 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; -#pragma warning restore AA0139 - local procedure SetStringValueInField(XMLDocument: XmlDocument; XMLNamespaces: XmlNamespaceManager; Path: Text; MaxLength: Integer; var Field: Text) + 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.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.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 SetNumberValueInField(XMLDocument: XmlDocument; XMLNamespaces: XmlNamespaceManager; Path: Text; var DecimalValue: Decimal) + 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(DecimalValue, 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; - local procedure SetDateValueInField(XMLDocument: XmlDocument; XMLNamespaces: XmlNamespaceManager; Path: Text; var DateValue: Date) + procedure View(EDocument: Record "E-Document"; TempBlob: Codeunit "Temp Blob") var - XMLNode: XmlNode; + EDocPurchaseHeader: Record "E-Document Purchase Header"; + EDocPurchaseLine: Record "E-Document Purchase Line"; + EDocReadablePurchaseDoc: Page "E-Doc. Readable Purchase Doc."; begin - if not XMLDocument.SelectSingleNode(Path, XMLNamespaces, XMLNode) then - exit; - - if XMLNode.AsXmlElement().InnerText() <> '' then - Evaluate(DateValue, XMLNode.AsXmlElement().InnerText(), 9); + EDocPurchaseHeader.GetFromEDocument(EDocument); + EDocPurchaseLine.SetRange("E-Document Entry No.", EDocPurchaseHeader."E-Document Entry No."); + EDocReadablePurchaseDoc.SetBuffer(EDocPurchaseHeader, EDocPurchaseLine); + EDocReadablePurchaseDoc.Run(); end; - procedure View(EDocument: Record "E-Document"; TempBlob: Codeunit "Temp Blob") + procedure ResetDraft(EDocument: Record "E-Document") + var + EDocPurchaseHeader: Record "E-Document Purchase Header"; begin - Error('A view is not implemented for this handler.'); + EDocPurchaseHeader.GetFromEDocument(EDocument); + EDocPurchaseHeader.Delete(true); 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..a657e831b7 --- /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. + /// + /// 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"; +} 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 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 7f99c09200..96e3342d00 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; /// 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 ee7a702132..a7a3aa5640 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,129 @@ 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 + + // [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 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) + // EDocumentPurchaseHeaderPageHandler + end; + + [Test] + procedure TestPEPPOLInvoice_ValidDocument_PurchaseInvoiceCreated() + var + EDocument: Record "E-Document"; + PurchaseHeader: Record "Purchase Header"; + DummyItem: Record Item; + EDocumentProcessing: Codeunit "E-Document Processing"; + DataTypeManagement: Codeunit "Data Type Management"; + RecRef: RecordRef; + VariantRecord: Variant; + begin + // [FEATURE] [E-Document] [PEPPOL] [Purchase Invoice Creation] + // [SCENARIO] Create a purchase invoice from a valid PEPPOL invoice 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.AssertPurchaseDocument(Vendor."No.", PurchaseHeader, DummyItem); + 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"; + RecRef: RecordRef; + EDocPurchaseDraft: TestPage "E-Document Purchase Draft"; + VariantRecord: Variant; + begin + // [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 + 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.AssertPurchaseDocument(Vendor."No.", PurchaseHeader, Item); + 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 +239,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 +305,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..a4866eacf6 100644 --- a/Apps/W1/EDocument/test/src/Processing/PEPPOLStructuredValidations.Codeunit.al +++ b/Apps/W1/EDocument/test/src/Processing/PEPPOLStructuredValidations.Codeunit.al @@ -2,52 +2,106 @@ codeunit 139896 "PEPPOL Structured Validations" { var Assert: Codeunit Assert; + UnitOfMeasureCodeTok: Label 'PCS', Locked = true; + 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', Locked = true, Comment = '%1 = Field caption, %2 = Table caption, %3 = Expected value, %4 = Actual value'; + 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('', 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(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", '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(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", '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(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 AssertPurchaseDocument(VendorNo: Code[20]; PurchaseHeader: Record "Purchase Header"; Item: Record Item) + var + PurchaseLine: Record "Purchase Line"; + Item1NoTok: Label 'GL00000001', Locked = true; + Item2NoTok: Label 'GL00000003', Locked = true; + begin + 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(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(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(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(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) + begin + this.MockDate := MockDate; + end; + + procedure SetMockCurrencyCode(MockCurrencyCode: Code[10]) + begin + this.MockCurrencyCode := MockCurrencyCode; + end; } \ No newline at end of file