diff --git a/Apps/DE/EDocumentDE/app/src/XRechnung/EDocumentXRechnungHandler.Codeunit.al b/Apps/DE/EDocumentDE/app/src/XRechnung/EDocumentXRechnungHandler.Codeunit.al new file mode 100644 index 0000000000..2f40d9687e --- /dev/null +++ b/Apps/DE/EDocumentDE/app/src/XRechnung/EDocumentXRechnungHandler.Codeunit.al @@ -0,0 +1,323 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.eServices.EDocument.Formats; + +using Microsoft.eServices.EDocument; +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 Microsoft.eServices.EDocument.Service.Participant; +using Microsoft.Purchases.Vendor; +using System.Telemetry; +using System.Utilities; + +codeunit 13921 "E-Document XRechnung Handler" implements IStructuredFormatReader +{ + Access = Internal; + InherentEntitlements = X; + InherentPermissions = X; + + var + SchemeIDGLNTok: Label '0088', Locked = true; + InvoiceLineTok: Label 'cac:InvoiceLine', Locked = true; + CreditNoteLineTok: Label 'cac:CreditNoteLine', Locked = true; + + /// + /// Reads an XRechnung format XML document and converts it into a draft purchase document. + /// This procedure processes both Invoice and CreditNote document types and populates the E-Document Purchase Header with the extracted data. + /// + /// The E-Document record that contains the document metadata and information. + /// A temporary blob containing the XML document stream to be processed. + /// Returns an enum indicating that the process resulted in a purchase document draft. + internal procedure ReadIntoDraft(EDocument: Record "E-Document"; TempBlob: Codeunit "Temp Blob"): Enum "E-Doc. Process Draft" + var + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + FeatureTelemetry: Codeunit "Feature Telemetry"; + XRechnungXml: XmlDocument; + XmlNamespaces: XmlNamespaceManager; + XmlElement: XmlElement; + CommonAggregateComponentsTok: Label 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2', Locked = true; + CommonBasicComponentsTok: Label 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2', Locked = true; + DefaultInvoiceTok: Label 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2', Locked = true; + DefaultCreditNoteTok: Label 'urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2', Locked = true; + FeatureNameTok: Label 'E-document XRechnung Format', Locked = true; + StartEventNameTok: Label 'E-document XRechnung import started. Parsing basic information.', Locked = true; + begin + FeatureTelemetry.LogUsage('0000EXH', FeatureNameTok, StartEventNameTok); + EDocumentPurchaseHeader.InsertForEDocument(EDocument); + + XmlDocument.ReadFrom(TempBlob.CreateInStream(TextEncoding::UTF8), XRechnungXml); + XmlNamespaces.AddNamespace('cac', CommonAggregateComponentsTok); + XmlNamespaces.AddNamespace('cbc', CommonBasicComponentsTok); + XmlNamespaces.AddNamespace('inv', DefaultInvoiceTok); + XmlNamespaces.AddNamespace('cn', DefaultCreditNoteTok); + + XRechnungXml.GetRoot(XmlElement); + case UpperCase(XmlElement.LocalName()) of + 'INVOICE': + PopulateEDocumentForInvoice(XRechnungXml, XmlNamespaces, EDocumentPurchaseHeader, EDocument); + 'CREDITNOTE': + PopulateEDocumentForCreditNote(XRechnungXml, XmlNamespaces, EDocumentPurchaseHeader, EDocument); + end; + + EDocumentPurchaseHeader.Modify(false); + EDocument.Direction := EDocument.Direction::Incoming; + exit(Enum::"E-Doc. Process Draft"::"Purchase Document"); + end; + + /// + /// Displays a readable view of the processed E-Document purchase information. + /// This procedure opens a page showing the purchase header and lines in a user-friendly format for review. + /// + /// The E-Document record that contains the document to be displayed. + /// A temporary blob containing the document data (not used in current implementation). + internal procedure View(EDocument: Record "E-Document"; TempBlob: Codeunit "Temp Blob") + var + EDocPurchaseHeader: Record "E-Document Purchase Header"; + EDocPurchaseLine: Record "E-Document Purchase Line"; + EDocReadablePurchaseDoc: Page "E-Doc. Readable Purchase Doc."; + begin + EDocPurchaseHeader.GetFromEDocument(EDocument); + EDocPurchaseLine.SetRange("E-Document Entry No.", EDocPurchaseHeader."E-Document Entry No."); + EDocReadablePurchaseDoc.SetBuffer(EDocPurchaseHeader, EDocPurchaseLine); + EDocReadablePurchaseDoc.Run(); + end; + + local procedure PopulateEDocumentForInvoice(XRechnungXml: XmlDocument; XmlNamespaces: XmlNamespaceManager; var EDocumentPurchaseHeader: Record "E-Document Purchase Header"; var EDocument: Record "E-Document") + var + EDocumentXMLHelper: Codeunit "EDocument XML Helper"; + VendorNo: Code[20]; + begin + EDocumentPurchaseHeader."E-Document Type" := "E-Document Type"::"Purchase Invoice"; +#pragma warning disable AA0139 // false positive: overflow handled by SetStringValueInField + EDocumentXMLHelper.SetStringValueInField(XRechnungXml, XmlNamespaces, '/inv:Invoice/cbc:ID', MaxStrLen(EDocumentPurchaseHeader."Sales Invoice No."), EDocumentPurchaseHeader."Sales Invoice No."); + EDocumentXMLHelper.SetDateValueInField(XRechnungXml, XmlNamespaces, '/inv:Invoice/cbc:IssueDate', EDocumentPurchaseHeader."Document Date"); + EDocumentXMLHelper.SetDateValueInField(XRechnungXml, XmlNamespaces, '/inv:Invoice/cbc:DueDate', EDocumentPurchaseHeader."Due Date"); + EDocumentXMLHelper.SetCurrencyValueInField(XRechnungXml, XmlNamespaces, '/inv:Invoice/cbc:DocumentCurrencyCode', MaxStrLen(EDocumentPurchaseHeader."Currency Code"), EDocumentPurchaseHeader."Currency Code"); + EDocumentXMLHelper.SetStringValueInField(XRechnungXml, XmlNamespaces, '/inv:Invoice/cac:OrderReference/cbc:ID', MaxStrLen(EDocumentPurchaseHeader."Purchase Order No."), EDocumentPurchaseHeader."Purchase Order No."); +#pragma warning restore AA0139 + EDocumentXMLHelper.SetNumberValueInField(XRechnungXml, XmlNamespaces, '/inv:Invoice/cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount', EDocumentPurchaseHeader."Sub Total"); + EDocumentXMLHelper.SetNumberValueInField(XRechnungXml, XmlNamespaces, '/inv:Invoice/cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount', EDocumentPurchaseHeader.Total); + EDocumentXMLHelper.SetNumberValueInField(XRechnungXml, XmlNamespaces, '/inv:Invoice/cac:LegalMonetaryTotal/cbc:PayableAmount', EDocumentPurchaseHeader."Amount Due"); + EDocumentPurchaseHeader."Total VAT" := EDocumentPurchaseHeader.Total - EDocumentPurchaseHeader."Sub Total" - EDocumentPurchaseHeader."Total Discount"; + VendorNo := ParseAccountingSupplierParty(XRechnungXml, XmlNamespaces, EDocumentPurchaseHeader, EDocument, 'inv:Invoice'); + ParseAccountingCustomerParty(XRechnungXml, XmlNamespaces, EDocumentPurchaseHeader, 'inv:Invoice'); + if VendorNo <> '' then + EDocumentPurchaseHeader."[BC] Vendor No." := VendorNo; + InsertXRechnungPurchaseLines(XRechnungXml, XmlNamespaces, EDocumentPurchaseHeader."E-Document Entry No.", EDocumentPurchaseHeader."E-Document Type"); + end; + + local procedure PopulateEDocumentForCreditNote(XRechnungXml: XmlDocument; XmlNamespaces: XmlNamespaceManager; var EDocumentPurchaseHeader: Record "E-Document Purchase Header"; var EDocument: Record "E-Document") + var + EDocumentXMLHelper: Codeunit "EDocument XML Helper"; + VendorNo: Code[20]; + begin + EDocumentPurchaseHeader."E-Document Type" := "E-Document Type"::"Purchase Credit Memo"; +#pragma warning disable AA0139 // false positive: overflow handled by SetStringValueInField + EDocumentXMLHelper.SetStringValueInField(XRechnungXml, XmlNamespaces, '/cn:CreditNote/cbc:ID', MaxStrLen(EDocumentPurchaseHeader."Sales Invoice No."), EDocumentPurchaseHeader."Sales Invoice No."); + EDocumentXMLHelper.SetDateValueInField(XRechnungXml, XmlNamespaces, '/cn:CreditNote/cbc:IssueDate', EDocumentPurchaseHeader."Document Date"); + EDocumentXMLHelper.SetDateValueInField(XRechnungXml, XmlNamespaces, '/cn:CreditNote/cbc:DueDate', EDocumentPurchaseHeader."Due Date"); + EDocumentXMLHelper.SetCurrencyValueInField(XRechnungXml, XmlNamespaces, '/cn:CreditNote/cbc:DocumentCurrencyCode', MaxStrLen(EDocumentPurchaseHeader."Currency Code"), EDocumentPurchaseHeader."Currency Code"); + EDocumentXMLHelper.SetStringValueInField(XRechnungXml, XmlNamespaces, '/cn:CreditNote/cac:OrderReference/cbc:ID', MaxStrLen(EDocumentPurchaseHeader."Purchase Order No."), EDocumentPurchaseHeader."Purchase Order No."); +#pragma warning restore AA0139 + EDocumentXMLHelper.SetNumberValueInField(XRechnungXml, XmlNamespaces, '/cn:CreditNote/cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount', EDocumentPurchaseHeader."Sub Total"); + EDocumentXMLHelper.SetNumberValueInField(XRechnungXml, XmlNamespaces, '/cn:CreditNote/cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount', EDocumentPurchaseHeader.Total); + EDocumentXMLHelper.SetNumberValueInField(XRechnungXml, XmlNamespaces, '/cn:CreditNote/cac:LegalMonetaryTotal/cbc:PayableAmount', EDocumentPurchaseHeader."Amount Due"); + EDocumentPurchaseHeader."Total VAT" := EDocumentPurchaseHeader.Total - EDocumentPurchaseHeader."Sub Total" - EDocumentPurchaseHeader."Total Discount"; + VendorNo := ParseAccountingSupplierParty(XRechnungXml, XmlNamespaces, EDocumentPurchaseHeader, EDocument, 'cn:CreditNote'); + ParseAccountingCustomerParty(XRechnungXml, XmlNamespaces, EDocumentPurchaseHeader, 'cn:CreditNote'); + if VendorNo <> '' then + EDocumentPurchaseHeader."[BC] Vendor No." := VendorNo; + InsertXRechnungPurchaseLines(XRechnungXml, XmlNamespaces, EDocumentPurchaseHeader."E-Document Entry No.", EDocumentPurchaseHeader."E-Document Type"); + end; + + local procedure ParseAccountingSupplierParty(XRechnungXml: XmlDocument; XmlNamespaces: XmlNamespaceManager; var EDocumentPurchaseHeader: Record "E-Document Purchase Header"; var EDocument: Record "E-Document"; DocumentType: Text) VendorNo: Code[20] + var + EDocumentImportHelper: Codeunit "E-Document Import Helper"; + EDocumentXMLHelper: Codeunit "EDocument XML Helper"; + EMTok: Label 'EM', Locked = true; + RoutingIdTok: Label '0198', Locked = true; + VATRegNoTok: Label '9930', Locked = true; + VendorName, VendorAddress, VendorParticipantId : Text; + VATRegistrationNo: Text[20]; + EndpointID, SchemeID : Text; + GLN: Code[13]; + BasePathTxt: Text; + XMLNode: XmlNode; + begin +#pragma warning disable AA0139 // false positive: overflow handled by SetStringValueInField + BasePathTxt := '/' + DocumentType + '/cac:AccountingSupplierParty/cac:Party'; + EDocumentXMLHelper.SetStringValueInField(XRechnungXml, XmlNamespaces, BasePathTxt + '/cac:PartyName/cbc:Name', MaxStrLen(EDocumentPurchaseHeader."Vendor Company Name"), EDocumentPurchaseHeader."Vendor Company Name"); + EDocumentXMLHelper.SetStringValueInField(XRechnungXml, XmlNamespaces, BasePathTxt + '/cac:PostalAddress/cbc:StreetName', MaxStrLen(EDocumentPurchaseHeader."Vendor Address"), EDocumentPurchaseHeader."Vendor Address"); + EDocumentXMLHelper.SetStringValueInField(XRechnungXml, XmlNamespaces, BasePathTxt + '/cac:PostalAddress/cbc:AdditionalStreetName', MaxStrLen(EDocumentPurchaseHeader."Vendor Address Recipient"), EDocumentPurchaseHeader."Vendor Address Recipient"); + EDocumentXMLHelper.SetStringValueInField(XRechnungXml, XmlNamespaces, BasePathTxt + '/cac:PartyTaxScheme/cbc:CompanyID', MaxStrLen(EDocumentPurchaseHeader."Vendor VAT Id"), EDocumentPurchaseHeader."Vendor VAT Id"); + EDocumentXMLHelper.SetStringValueInField(XRechnungXml, XmlNamespaces, BasePathTxt + '/cac:Contact/cbc:Name', MaxStrLen(EDocumentPurchaseHeader."Vendor Contact Name"), EDocumentPurchaseHeader."Vendor Contact Name"); +#pragma warning restore AA0139 + if XRechnungXml.SelectSingleNode(BasePathTxt + '/cbc:EndpointID/@schemeID', XmlNamespaces, XMLNode) then begin + SchemeID := XMLNode.AsXmlAttribute().Value(); + EndpointID := EDocumentXMLHelper.GetNodeValue(XRechnungXml, XmlNamespaces, BasePathTxt + '/cbc:EndpointID'); + case SchemeID of + EMTok, RoutingIdTok, VATRegNoTok: + VATRegistrationNo := CopyStr(EndpointID, 1, MaxStrLen(VATRegistrationNo)); + SchemeIDGLNTok: + begin + GLN := CopyStr(EndpointID, 1, MaxStrLen(GLN)); + EDocumentPurchaseHeader."Vendor GLN" := GLN; + end; + end; + VendorParticipantId := SchemeID + ':' + EndpointID; + end; + VATRegistrationNo := CopyStr(EDocumentPurchaseHeader."Vendor VAT Id", 1, MaxStrLen(VATRegistrationNo)); + VendorName := EDocumentPurchaseHeader."Vendor Company Name"; + VendorAddress := EDocumentPurchaseHeader."Vendor Address"; + if not FindVendorByVATRegNoOrGLN(VendorNo, VATRegistrationNo, GLN) then + if not FindVendorByParticipantId(VendorNo, EDocument, VendorParticipantId) then + VendorNo := EDocumentImportHelper.FindVendorByNameAndAddress(VendorName, VendorAddress); + end; + + local procedure ParseAccountingCustomerParty(XRechnungXml: XmlDocument; XmlNamespaces: XmlNamespaceManager; var EDocumentPurchaseHeader: Record "E-Document Purchase Header"; DocumentType: Text) + var + EDocumentXMLHelper: Codeunit "EDocument XML Helper"; + BasePathTxt: Text; + XMLNode: XmlNode; + SchemeID, EndpointID : Text; + begin +#pragma warning disable AA0139 // false positive: overflow handled by SetStringValueInField + BasePathTxt := '/' + DocumentType + '/cac:AccountingCustomerParty/cac:Party'; + EDocumentXMLHelper.SetStringValueInField(XRechnungXml, XmlNamespaces, BasePathTxt + '/cac:PartyName/cbc:Name', MaxStrLen(EDocumentPurchaseHeader."Customer Company Name"), EDocumentPurchaseHeader."Customer Company Name"); + EDocumentXMLHelper.SetStringValueInField(XRechnungXml, XmlNamespaces, BasePathTxt + '/cac:PartyLegalEntity/cbc:RegistrationName', MaxStrLen(EDocumentPurchaseHeader."Customer Company Name"), EDocumentPurchaseHeader."Customer Company Name"); + EDocumentXMLHelper.SetStringValueInField(XRechnungXml, XmlNamespaces, BasePathTxt + '/cac:PostalAddress/cbc:StreetName', MaxStrLen(EDocumentPurchaseHeader."Customer Address"), EDocumentPurchaseHeader."Customer Address"); + EDocumentXMLHelper.SetStringValueInField(XRechnungXml, XmlNamespaces, BasePathTxt + '/cac:PostalAddress/cbc:AdditionalStreetName', MaxStrLen(EDocumentPurchaseHeader."Customer Address Recipient"), EDocumentPurchaseHeader."Customer Address Recipient"); + EDocumentXMLHelper.SetStringValueInField(XRechnungXml, XmlNamespaces, BasePathTxt + '/cac:PartyTaxScheme/cbc:CompanyID', MaxStrLen(EDocumentPurchaseHeader."Customer VAT Id"), EDocumentPurchaseHeader."Customer VAT Id"); +#pragma warning restore AA0139 + if XRechnungXml.SelectSingleNode(BasePathTxt + '/cbc:EndpointID/@schemeID', XmlNamespaces, XMLNode) then begin + SchemeID := XMLNode.AsXmlAttribute().Value(); + EndpointID := EDocumentXMLHelper.GetNodeValue(XRechnungXml, XmlNamespaces, BasePathTxt + '/cbc:EndpointID'); + if SchemeID = SchemeIDGLNTok then + EDocumentPurchaseHeader."Customer GLN" := CopyStr(EndpointID, 1, MaxStrLen(EDocumentPurchaseHeader."Customer GLN")); + end; + end; + + local procedure InsertXRechnungPurchaseLines(XRechnungXml: XmlDocument; XmlNamespaces: XmlNamespaceManager; EDocumentEntryNo: Integer; DocumentType: Enum "E-Document Type") + var + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + NewLineXML: XmlDocument; + LineXMLList: XmlNodeList; + LineXMLNode: XmlNode; + LineXPath: Text; + LineElementName: Text; + begin + case DocumentType of + "E-Document Type"::"Purchase Invoice": + begin + LineElementName := InvoiceLineTok; + LineXPath := '//inv:Invoice/cac:InvoiceLine'; + end; + "E-Document Type"::"Purchase Credit Memo": + begin + LineElementName := CreditNoteLineTok; + LineXPath := '//cn:CreditNote/cac:CreditNoteLine'; + end; + end; + + if not XRechnungXml.SelectNodes(LineXPath, 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); + PopulateXRechnungPurchaseLine(NewLineXML, XmlNamespaces, EDocumentPurchaseLine, LineElementName); + EDocumentPurchaseLine.Insert(false); + end; + end; + + local procedure PopulateXRechnungPurchaseLine(LineXML: XmlDocument; XmlNamespaces: XmlNamespaceManager; var EDocumentPurchaseLine: Record "E-Document Purchase Line"; LineElementName: Text) + var + EDocumentXMLHelper: Codeunit "EDocument XML Helper"; + QuantityFieldName: Text; + begin +#pragma warning disable AA0139 // false positive: overflow handled by SetStringValueInField + case LineElementName of + InvoiceLineTok: + QuantityFieldName := 'cac:InvoiceLine/cbc:InvoicedQuantity'; + CreditNoteLineTok: + QuantityFieldName := 'cac:CreditNoteLine/cbc:CreditedQuantity'; + end; + EDocumentXMLHelper.SetStringValueInField(LineXML, XmlNamespaces, LineElementName + '/cbc:ID', MaxStrLen(EDocumentPurchaseLine."Product Code"), EDocumentPurchaseLine."Product Code"); + EDocumentXMLHelper.SetStringValueInField(LineXML, XmlNamespaces, LineElementName + '/cac:Item/cbc:Name', MaxStrLen(EDocumentPurchaseLine.Description), EDocumentPurchaseLine.Description); + EDocumentXMLHelper.SetStringValueInField(LineXML, XmlNamespaces, LineElementName + '/cac:Item/cac:SellersItemIdentification/cbc:ID', MaxStrLen(EDocumentPurchaseLine."Product Code"), EDocumentPurchaseLine."Product Code"); + EDocumentXMLHelper.SetStringValueInField(LineXML, XmlNamespaces, LineElementName + '/cac:Item/cac:StandardItemIdentification/cbc:ID', MaxStrLen(EDocumentPurchaseLine."Product Code"), EDocumentPurchaseLine."Product Code"); + EDocumentXMLHelper.SetNumberValueInField(LineXML, XmlNamespaces, QuantityFieldName, EDocumentPurchaseLine.Quantity); + EDocumentXMLHelper.SetStringValueInField(LineXML, XmlNamespaces, QuantityFieldName + '/@unitCode', MaxStrLen(EDocumentPurchaseLine."Unit of Measure"), EDocumentPurchaseLine."Unit of Measure"); + EDocumentXMLHelper.SetNumberValueInField(LineXML, XmlNamespaces, LineElementName + '/cac:Price/cbc:PriceAmount', EDocumentPurchaseLine."Unit Price"); + EDocumentXMLHelper.SetNumberValueInField(LineXML, XmlNamespaces, LineElementName + '/cbc:LineExtensionAmount', EDocumentPurchaseLine."Sub Total"); + EDocumentXMLHelper.SetCurrencyValueInField(LineXML, XmlNamespaces, LineElementName + '/cbc:LineExtensionAmount/@currencyID', MaxStrLen(EDocumentPurchaseLine."Currency Code"), EDocumentPurchaseLine."Currency Code"); + EDocumentXMLHelper.SetNumberValueInField(LineXML, XmlNamespaces, LineElementName + '/cac:Item/cac:ClassifiedTaxCategory/cbc:Percent', EDocumentPurchaseLine."VAT Rate"); +#pragma warning restore AA0139 + end; + + local procedure FindVendorByVATRegNoOrGLN(var VendorNo: Code[20]; VATRegistrationNo: Text[20]; GLN: Code[13]): Boolean + var + Vendor: Record Vendor; + begin + // Try to find vendor by VAT Registration Number + if VATRegistrationNo <> '' then begin + Vendor.Reset(); + Vendor.SetLoadFields("VAT Registration No."); + Vendor.SetRange("VAT Registration No.", VATRegistrationNo); + if Vendor.FindFirst() then begin + VendorNo := Vendor."No."; + exit(true); + end; + end; + + // Try to find vendor by GLN + if GLN <> '' then begin + Vendor.Reset(); + Vendor.SetLoadFields("GLN"); + Vendor.SetRange("GLN", GLN); + if Vendor.FindFirst() then begin + VendorNo := Vendor."No."; + exit(true); + end; + end; + + exit(false); + end; + + local procedure FindVendorByParticipantId(var VendorNo: Code[20]; EDocument: Record "E-Document"; ParticipantId: Text): Boolean + var + EDocServiceParticipant: Record "Service Participant"; + EDocumentService: Record "E-Document Service"; + EDocumentHelper: Codeunit "E-Document Helper"; + begin + if ParticipantId = '' then + exit(false); + + EDocumentHelper.GetEdocumentService(EDocument, EDocumentService); + EDocServiceParticipant.SetRange("Participant Type", EDocServiceParticipant."Participant Type"::Vendor); + EDocServiceParticipant.SetRange("Participant Identifier", ParticipantId); + EDocServiceParticipant.SetRange(Service, EDocumentService.Code); + if not EDocServiceParticipant.FindFirst() then begin + EDocServiceParticipant.SetRange(Service); + if not EDocServiceParticipant.FindFirst() then + exit(false); + end; + + VendorNo := EDocServiceParticipant.Participant; + exit(true); + end; + + procedure ResetDraft(EDocument: Record "E-Document") + var + EDocPurchaseHeader: Record "E-Document Purchase Header"; + begin + EDocPurchaseHeader.GetFromEDocument(EDocument); + EDocPurchaseHeader.Delete(true); + end; +} diff --git a/Apps/DE/EDocumentDE/app/src/XRechnung/XRechnungEDocReadIntoDraft.EnumExt.al b/Apps/DE/EDocumentDE/app/src/XRechnung/XRechnungEDocReadIntoDraft.EnumExt.al new file mode 100644 index 0000000000..812c14ecfc --- /dev/null +++ b/Apps/DE/EDocumentDE/app/src/XRechnung/XRechnungEDocReadIntoDraft.EnumExt.al @@ -0,0 +1,18 @@ +// ------------------------------------------------------------------------------------------------ +// 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; + +using Microsoft.eServices.EDocument.Processing.Import; +using Microsoft.eServices.EDocument.Formats; +using Microsoft.eServices.EDocument.Processing.Interfaces; + +enumextension 13917 "XRechnung EDoc Read into Draft" extends "E-Doc. Read into Draft" +{ + value(13914; "XRechnung") + { + Caption = 'XRechnung'; + Implementation = IStructuredFormatReader = "E-Document XRechnung Handler"; + } +} diff --git a/Apps/DE/EDocumentDE/test/.resources/xrechnung/xrechnung-invoice-0.xml b/Apps/DE/EDocumentDE/test/.resources/xrechnung/xrechnung-invoice-0.xml new file mode 100644 index 0000000000..ef430f47a5 --- /dev/null +++ b/Apps/DE/EDocumentDE/test/.resources/xrechnung/xrechnung-invoice-0.xml @@ -0,0 +1,166 @@ + + + urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0 + urn:fdc:peppol.eu:2017:poacc:billing:01:1.0 + 103033 + 2026-01-22 + 2026-02-22 + 380 + XYZ + 1 + + 2 + + + 103033 + + + 103033 + + + + + + 103033 + + + + + + + seller@email.de + + CRONUS International + + + Main Street, 14 + Birmingham + B27 4KT + + GB + + + + GB123456789 + + VAT + + + + CRONUS International + 123456789 + 123/456/7890, HRA-Eintrag in […] + + + Jim Olive + JO@contoso.com + + + + + + buyer@info.de + + 8712345000004 + + + The Cannon Group PLC + + + 192 Market Square + Birmingham + B27 4KT + + GB + + + + GB789456278 + + VAT + + + + The Cannon Group PLC + 789456278 + + + + + 2026-01-22 + + + + + 58 + + DE75512108001245126199 + + + + Zahlbar sofort ohne Abzug. + + + 980 + + 14000 + 980 + + S + 7 + + VAT + + + + + + 14000 + 14000 + 14980 + 0 + 0.00 + 0 + 14980 + + + 1000 + Item + 1 + 4000 + + Bicycle + + S + 25 + + VAT + + + + + 4000 + + + + 2000 + Item + 2 + 10000 + + Bicycle v2 + + S + 25 + + VAT + + + + + 5000 + + + \ No newline at end of file diff --git a/Apps/DE/EDocumentDE/test/app.json b/Apps/DE/EDocumentDE/test/app.json index d00c6971aa..a8c143604d 100644 --- a/Apps/DE/EDocumentDE/test/app.json +++ b/Apps/DE/EDocumentDE/test/app.json @@ -58,7 +58,7 @@ }, { "from": 13922, - "to": 13923 + "to": 13926 } ], "features": [ @@ -70,5 +70,8 @@ "allowDownloadingSource": true, "includeSourceInSymbolFile": true }, - "application": "27.0.0.0" -} + "application": "27.0.0.0", + "resourceFolders": [ + ".resources" + ] +} \ No newline at end of file diff --git a/Apps/DE/EDocumentDE/test/src/EDocFormatMock.Codeunit.al b/Apps/DE/EDocumentDE/test/src/EDocFormatMock.Codeunit.al new file mode 100644 index 0000000000..206fda4303 --- /dev/null +++ b/Apps/DE/EDocumentDE/test/src/EDocFormatMock.Codeunit.al @@ -0,0 +1,56 @@ +codeunit 13926 "E-Doc. Format Mock" implements "E-Document" +{ + SingleInstance = true; + + procedure Check(var SourceDocumentHeader: RecordRef; EDocService: Record "E-Document Service"; EDocumentProcessingPhase: enum "E-Document Processing Phase"); + begin + OnCheck(SourceDocumentHeader, EDocService, EDocumentProcessingPhase); + end; + + procedure Create(EDocService: Record "E-Document Service"; var EDocument: Record "E-Document"; var SourceDocumentHeader: RecordRef; var SourceDocumentLines: RecordRef; var TempBlob: codeunit "Temp Blob"); + begin + OnCreate(EDocService, EDocument, SourceDocumentHeader, SourceDocumentLines, TempBlob); + end; + + procedure CreateBatch(EDocService: Record "E-Document Service"; var EDocuments: Record "E-Document"; var SourceDocumentHeaders: RecordRef; var SourceDocumentsLines: RecordRef; var TempBlob: codeunit "Temp Blob"); + begin + OnCreateBatch(EDocService, EDocuments, SourceDocumentHeaders, SourceDocumentsLines, TempBlob); + end; + + procedure GetBasicInfoFromReceivedDocument(var EDocument: Record "E-Document"; var TempBlob: codeunit "Temp Blob"); + begin + OnGetBasicInfoFromReceivedDocument(EDocument, TempBlob); + end; + + procedure GetCompleteInfoFromReceivedDocument(var EDocument: Record "E-Document"; var CreatedDocumentHeader: RecordRef; var CreatedDocumentLines: RecordRef; var TempBlob: codeunit "Temp Blob"); + begin + OnGetCompleteInfoFromReceivedDocument(EDocument, CreatedDocumentHeader, CreatedDocumentLines, TempBlob); + end; + + [IntegrationEvent(false, false)] + local procedure OnCheck(var SourceDocumentHeader: RecordRef; EDocService: Record "E-Document Service"; EDocumentProcessingPhase: enum "E-Document Processing Phase") + begin + end; + + [IntegrationEvent(false, false)] + local procedure OnCreate(EDocService: Record "E-Document Service"; var EDocument: Record "E-Document"; var SourceDocumentHeader: RecordRef; var SourceDocumentLines: RecordRef; var TempBlob: codeunit "Temp Blob"); + begin + end; + + [IntegrationEvent(false, false)] + local procedure OnCreateBatch(EDocService: Record "E-Document Service"; var EDocuments: Record "E-Document"; var SourceDocumentHeaders: RecordRef; var SourceDocumentsLines: RecordRef; var TempBlob: codeunit "Temp Blob"); + begin + end; + + [IntegrationEvent(false, false)] + local procedure OnGetBasicInfoFromReceivedDocument(var EDocument: Record "E-Document"; var TempBlob: codeunit "Temp Blob"); + begin + end; + + [IntegrationEvent(false, false)] + local procedure OnGetCompleteInfoFromReceivedDocument(var EDocument: Record "E-Document"; var CreatedDocumentHeader: RecordRef; var CreatedDocumentLines: RecordRef; var TempBlob: codeunit "Temp Blob"); + begin + end; + + +} \ No newline at end of file diff --git a/Apps/DE/EDocumentDE/test/src/EDocFormatMock.EnumExt.al b/Apps/DE/EDocumentDE/test/src/EDocFormatMock.EnumExt.al new file mode 100644 index 0000000000..017e1f0d4b --- /dev/null +++ b/Apps/DE/EDocumentDE/test/src/EDocFormatMock.EnumExt.al @@ -0,0 +1,7 @@ +enumextension 13918 "E-Doc. Format Mock" extends "E-Document Format" +{ + value(6160; "Mock") + { + Implementation = "E-Document" = "E-Doc. Format Mock"; + } +} \ No newline at end of file diff --git a/Apps/DE/EDocumentDE/test/src/EDocumentStructuredTests.Codeunit.al b/Apps/DE/EDocumentDE/test/src/EDocumentStructuredTests.Codeunit.al new file mode 100644 index 0000000000..31713dbe12 --- /dev/null +++ b/Apps/DE/EDocumentDE/test/src/EDocumentStructuredTests.Codeunit.al @@ -0,0 +1,272 @@ +codeunit 13924 "E-Document Structured Tests" +{ + Subtype = Test; + TestType = IntegrationTest; + + var + Customer: Record Customer; + Vendor: Record Vendor; + EDocumentService: Record "E-Document Service"; + Assert: Codeunit Assert; + LibraryVariableStorage: Codeunit "Library - Variable Storage"; + LibraryEDoc: Codeunit "Library - E-Document"; + LibraryLowerPermission: Codeunit "Library - Lower Permissions"; + XRechnungStructuredValidations: Codeunit "XRechnung Struct. Validations"; + IsInitialized: Boolean; + EDocumentStatusNotUpdatedErr: Label 'The status of the EDocument was not updated to the expected status after the step was executed.'; + TestFileTok: Label 'xrechnung/xrechnung-invoice-0.xml', Locked = true; + MockCurrencyCode: Code[10]; + MockDate: Date; + + #region XRechnung XML + [Test] + procedure TestXRechnungInvoice_ValidDocument() + var + EDocument: Record "E-Document"; + begin + // [FEATURE] [E-Document] [XRechnung] [Content Extraction] + // [SCENARIO] Process a valid XRechnung invoice document and validate full content extraction + + // [GIVEN] A valid XRechnung XML invoice document is imported + Initialize(Enum::"Service Integration"::"No Integration"); + SetupXRechnungEDocumentService(); + CreateInboundEDocumentFromXML(EDocument, TestFileTok); + + // [WHEN] The document is processed to read into draft step + if ProcessEDocumentToStep(EDocument, "Import E-Document Steps"::"Read into Draft") then begin + XRechnungStructuredValidations.SetMockCurrencyCode(MockCurrencyCode); + XRechnungStructuredValidations.SetMockDate(MockDate); + + // [THEN] All content from the XRechnung document is correctly extracted and validated + XRechnungStructuredValidations.AssertFullEDocumentContentExtracted(EDocument."Entry No"); + end else + Assert.Fail(EDocumentStatusNotUpdatedErr); + end; + + [Test] + [HandlerFunctions('EDocumentPurchaseHeaderPageHandler')] + procedure TestXRechnungInvoice_ValidDocument_ViewExtractedData() + var + EDocument: Record "E-Document"; + EDocImport: Codeunit "E-Doc. Import"; + begin + // [FEATURE] [E-Document] [XRechnung] [View Data] + // [SCENARIO] View extracted data from a valid XRechnung invoice document + + // [GIVEN] A valid XRechnung XML invoice document is imported + Initialize(Enum::"Service Integration"::"No Integration"); + SetupXRechnungEDocumentService(); + CreateInboundEDocumentFromXML(EDocument, TestFileTok); + + // [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 TestXRechnungInvoice_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] [XRechnung] [Purchase Invoice Creation] + // [SCENARIO] Create a purchase invoice from a valid XRechnung invoice document + + // [GIVEN] A valid XRechnung XML invoice document is imported + Initialize(Enum::"Service Integration"::"No Integration"); + Vendor."VAT Registration No." := 'GB123456789'; + Vendor.Modify(true); + SetupXRechnungEDocumentService(); + CreateInboundEDocumentFromXML(EDocument, TestFileTok); + + // [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 XRechnung data + XRechnungStructuredValidations.SetMockCurrencyCode(MockCurrencyCode); + XRechnungStructuredValidations.SetMockDate(MockDate); + XRechnungStructuredValidations.AssertPurchaseDocument(Vendor."No.", PurchaseHeader, DummyItem); + end; + + [Test] + procedure TestXRechnungInvoice_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] [XRechnung] [Draft Update] + // [SCENARIO] Update draft purchase document data and finalize processing + + // [GIVEN] A valid XRechnung XML invoice document is imported and processed to draft preparation + Initialize(Enum::"Service Integration"::"No Integration"); + Vendor."VAT Registration No." := 'GB123456789'; + Vendor.Modify(true); + SetupXRechnungEDocumentService(); + CreateInboundEDocumentFromXML(EDocument, TestFileTok); + 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 XRechnung data and manual updates + XRechnungStructuredValidations.SetMockCurrencyCode(MockCurrencyCode); + XRechnungStructuredValidations.SetMockDate(MockDate); + XRechnungStructuredValidations.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") + var + TransformationRule: Record "Transformation Rule"; + EDocument: Record "E-Document"; + EDocDataStorage: Record "E-Doc. Data Storage"; + EDocumentsSetup: Record "E-Documents Setup"; + EDocumentServiceStatus: Record "E-Document Service Status"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + DocumentAttachment: Record "Document Attachment"; + Currency: Record Currency; + begin + LibraryLowerPermission.SetOutsideO365Scope(); + LibraryVariableStorage.Clear(); + Clear(LibraryVariableStorage); + + if IsInitialized then + exit; + + EDocument.DeleteAll(false); + EDocumentServiceStatus.DeleteAll(false); + EDocumentService.DeleteAll(false); + EDocDataStorage.DeleteAll(false); + EDocumentPurchaseHeader.DeleteAll(false); + EDocumentPurchaseLine.DeleteAll(false); + DocumentAttachment.DeleteAll(false); + + LibraryEDoc.SetupStandardVAT(); + LibraryEDoc.SetupStandardSalesScenario(Customer, EDocumentService, Enum::"E-Document Format"::Mock, Integration); + LibraryEDoc.SetupStandardPurchaseScenario(Vendor, EDocumentService, Enum::"E-Document Format"::Mock, Integration); + EDocumentService."Import Process" := "E-Document Import Process"::"Version 2.0"; + EDocumentService."Read into Draft Impl." := "E-Doc. Read into Draft"::XRechnung; + EDocumentService.Modify(); + EDocumentsSetup.InsertNewExperienceSetup(); + + // Set a currency that can be used across all localizations + MockCurrencyCode := 'XYZ'; + Currency.Init(); + Currency.Validate(Code, MockCurrencyCode); + if Currency.Insert(true) then; + CreateCurrencyExchangeRate(); + + MockDate := DMY2Date(22, 01, 2026); + + TransformationRule.DeleteAll(false); + TransformationRule.CreateDefaultTransformations(); + + IsInitialized := true; + end; + + local procedure SetupXRechnungEDocumentService() + begin + EDocumentService."Read into Draft Impl." := "E-Doc. Read into Draft"::XRechnung; + EDocumentService.Modify(false); + end; + + local procedure CreateInboundEDocumentFromXML(var EDocument: Record "E-Document"; FilePath: Text) + var + EDocLogRecord: Record "E-Document Log"; + EDocumentLog: Codeunit "E-Document Log"; + begin + LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); + + EDocumentLog.SetBlob('Test', Enum::"E-Doc. File Format"::XML, NavApp.GetResourceAsText(FilePath)); + EDocumentLog.SetFields(EDocument, EDocumentService); + EDocLogRecord := EDocumentLog.InsertLog(Enum::"E-Document Service Status"::Imported, Enum::"Import E-Doc. Proc. Status"::Readable); + + EDocument."Structured Data Entry No." := EDocLogRecord."E-Doc. Data Storage Entry No."; + EDocument.Modify(false); + end; + + local procedure ProcessEDocumentToStep(var EDocument: Record "E-Document"; ProcessingStep: Enum "Import E-Document Steps"): Boolean + var + EDocImportParameters: Record "E-Doc. Import Parameters"; + EDocImport: Codeunit "E-Doc. Import"; + EDocumentProcessing: Codeunit "E-Document Processing"; + begin + EDocumentProcessing.ModifyEDocumentProcessingStatus(EDocument, "Import E-Doc. Proc. Status"::Readable); + EDocImportParameters."Step to Run" := ProcessingStep; + EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParameters); + EDocument.CalcFields("Import Processing Status"); + + // 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; +} \ No newline at end of file diff --git a/Apps/DE/EDocumentDE/test/src/LibraryEDocument.Codeunit.al b/Apps/DE/EDocumentDE/test/src/LibraryEDocument.Codeunit.al new file mode 100644 index 0000000000..88596086a7 --- /dev/null +++ b/Apps/DE/EDocumentDE/test/src/LibraryEDocument.Codeunit.al @@ -0,0 +1,1080 @@ +codeunit 13923 "Library - E-Document" +{ + EventSubscriberInstance = Manual; + Permissions = tabledata "E-Document Service" = rimd, + tabledata "E-Doc. Service Supported Type" = rimd, + tabledata "E-Doc. Mapping" = rimd; + + var + StandardItem: Record Item; + VATPostingSetup: Record "VAT Posting Setup"; + Assert: Codeunit Assert; + LibraryUtility: Codeunit "Library - Utility"; + LibraryWorkflow: Codeunit "Library - Workflow"; + LibrarySales: Codeunit "Library - Sales"; + LibraryPurchase: Codeunit "Library - Purchase"; + LibraryERM: Codeunit "Library - ERM"; + LibraryRandom: Codeunit "Library - Random"; + LibraryInvt: Codeunit "Library - Inventory"; + LibraryVariableStorage: Codeunit "Library - Variable Storage"; + LibraryFinChargeMemo: Codeunit "Library - Finance Charge Memo"; + LibraryInventory: Codeunit "Library - Inventory"; + + procedure SetupStandardVAT() + begin + if (VATPostingSetup."VAT Bus. Posting Group" = '') and (VATPostingSetup."VAT Prod. Posting Group" = '') then + LibraryERM.CreateVATPostingSetupWithAccounts(VATPostingSetup, Enum::"Tax Calculation Type"::"Normal VAT", 1); + end; + +#if not CLEAN26 +#pragma warning disable AL0432 + [Obsolete('Use SetupStandardSalesScenario(var Customer: Record Customer; var EDocService: Record "E-Document Service"; EDocDocumentFormat: Enum "E-Document Format"; EDocIntegration: Enum "Service Integration") instead', '26.0')] + procedure SetupStandardSalesScenario(var Customer: Record Customer; var EDocService: Record "E-Document Service"; EDocDocumentFormat: Enum "E-Document Format"; EDocIntegration: Enum "E-Document Integration") + var + ServiceCode: Code[20]; + begin + // Create standard service and simple workflow + ServiceCode := CreateService(EDocDocumentFormat, EDocIntegration); + EDocService.Get(ServiceCode); + SetupStandardSalesScenario(Customer, EDocService); + end; +#pragma warning restore AL0432 +#endif + + procedure SetupStandardSalesScenario(var Customer: Record Customer; var EDocService: Record "E-Document Service"; EDocDocumentFormat: Enum "E-Document Format"; EDocIntegration: Enum "Service Integration") + var + ServiceCode: Code[20]; + begin + // Create standard service and simple workflow + ServiceCode := CreateService(EDocDocumentFormat, EDocIntegration); + EDocService.Get(ServiceCode); + SetupStandardSalesScenario(Customer, EDocService); + end; + + procedure SetupStandardSalesScenario(var Customer: Record Customer; var EDocService: Record "E-Document Service") + var + CountryRegion: Record "Country/Region"; + DocumentSendingProfile: Record "Document Sending Profile"; + SalesSetup: Record "Sales & Receivables Setup"; + WorkflowSetup: Codeunit "Workflow Setup"; + WorkflowCode: Code[20]; + begin + LibraryWorkflow.DeleteAllExistingWorkflows(); + WorkflowSetup.InitWorkflow(); + SetupCompanyInfo(); + + CreateDocSendingProfile(DocumentSendingProfile); + WorkflowCode := CreateSimpleFlow(DocumentSendingProfile.Code, EDocService.Code); + DocumentSendingProfile."Electronic Document" := DocumentSendingProfile."Electronic Document"::"Extended E-Document Service Flow"; + DocumentSendingProfile."Electronic Service Flow" := WorkflowCode; + DocumentSendingProfile.Modify(); + + // Create Customer for sales scenario + LibrarySales.CreateCustomer(Customer); + LibraryERM.FindCountryRegion(CountryRegion); + Customer.Validate(Address, LibraryUtility.GenerateRandomCode(Customer.FieldNo(Address), DATABASE::Customer)); + Customer.Validate("Country/Region Code", CountryRegion.Code); + Customer.Validate(City, LibraryUtility.GenerateRandomCode(Customer.FieldNo(City), DATABASE::Customer)); + Customer.Validate("Post Code", LibraryUtility.GenerateRandomCode(Customer.FieldNo("Post Code"), DATABASE::Customer)); + Customer.Validate("VAT Bus. Posting Group", VATPostingSetup."VAT Bus. Posting Group"); + Customer."VAT Registration No." := LibraryERM.GenerateVATRegistrationNo(CountryRegion.Code); + Customer.Validate(GLN, '1234567890128'); + Customer."Document Sending Profile" := DocumentSendingProfile.Code; + Customer.Modify(true); + + // Create Item + if StandardItem."No." = '' then begin + VATPostingSetup.TestField("VAT Prod. Posting Group"); + CreateGenericItem(StandardItem); + StandardItem."VAT Prod. Posting Group" := VATPostingSetup."VAT Prod. Posting Group"; + StandardItem.Modify(); + end; + + SalesSetup.Get(); + SalesSetup."Invoice Rounding" := false; + SalesSetup.Modify(); + end; + + procedure GetGenericItem(var Item: Record Item) + begin + if StandardItem."No." = '' then + CreateGenericItem(StandardItem); + Item.Get(StandardItem."No."); + end; + +#if not CLEAN26 +#pragma warning disable AL0432 + [Obsolete('Use SetupStandardPurchaseScenario(var Vendor: Record Vendor; var EDocService: Record "E-Document Service"; EDocDocumentFormat: Enum "E-Document Format"; EDocIntegration: Enum "Service Integration") instead', '26.0')] + procedure SetupStandardPurchaseScenario(var Vendor: Record Vendor; var EDocService: Record "E-Document Service"; EDocDocumentFormat: Enum "E-Document Format"; EDocIntegration: Enum "E-Document Integration") + var + ServiceCode: Code[20]; + begin + // Create standard service and simple workflow + if EDocService.Code = '' then begin + ServiceCode := CreateService(EDocDocumentFormat, EDocIntegration); + EDocService.Get(ServiceCode); + end; + SetupStandardPurchaseScenario(Vendor, EDocService); + end; +#pragma warning restore AL0432 +#endif + + procedure SetupStandardPurchaseScenario(var Vendor: Record Vendor; var EDocService: Record "E-Document Service"; EDocDocumentFormat: Enum "E-Document Format"; EDocIntegration: Enum "Service Integration") + var + ServiceCode: Code[20]; + begin + // Create standard service and simple workflow + if EDocService.Code = '' then begin + ServiceCode := CreateService(EDocDocumentFormat, EDocIntegration); + EDocService.Get(ServiceCode); + end; + SetupStandardPurchaseScenario(Vendor, EDocService); + end; + + + procedure SetupStandardPurchaseScenario(var Vendor: Record Vendor; var EDocService: Record "E-Document Service") + var + CountryRegion: Record "Country/Region"; + ItemReference: Record "Item Reference"; + UnitOfMeasure: Record "Unit of Measure"; + ExtraItem: Record "Item"; + WorkflowSetup: Codeunit "Workflow Setup"; + LibraryItemReference: Codeunit "Library - Item Reference"; + begin + WorkflowSetup.InitWorkflow(); + SetupCompanyInfo(); + + // Create Customer for sales scenario + LibraryPurchase.CreateVendor(Vendor); + LibraryERM.FindCountryRegion(CountryRegion); + Vendor.Validate(Address, LibraryUtility.GenerateRandomCode(Vendor.FieldNo(Address), DATABASE::Vendor)); + Vendor.Validate("Country/Region Code", CountryRegion.Code); + Vendor.Validate(City, LibraryUtility.GenerateRandomCode(Vendor.FieldNo(City), DATABASE::Vendor)); + Vendor.Validate("Post Code", LibraryUtility.GenerateRandomCode(Vendor.FieldNo("Post Code"), DATABASE::Vendor)); + Vendor.Validate("VAT Bus. Posting Group", VATPostingSetup."VAT Bus. Posting Group"); + Vendor."VAT Registration No." := LibraryERM.GenerateVATRegistrationNo(CountryRegion.Code); + Vendor."Receive E-Document To" := Enum::"E-Document Type"::"Purchase Invoice"; + Vendor.Validate(GLN, '1234567890128'); + Vendor.Modify(true); + + // Create Item + if StandardItem."No." = '' then begin + VATPostingSetup.TestField("VAT Prod. Posting Group"); + CreateGenericItem(StandardItem, VATPostingSetup."VAT Prod. Posting Group"); + end; + + UnitOfMeasure.Init(); + UnitOfMeasure."International Standard Code" := 'PCS'; + UnitOfMeasure.Code := 'PCS'; + if UnitOfMeasure.Insert() then; + + CreateItemUnitOfMeasure(StandardItem."No.", UnitOfMeasure.Code); + LibraryItemReference.CreateItemReference(ItemReference, StandardItem."No.", '', 'PCS', Enum::"Item Reference Type"::Vendor, Vendor."No.", '1000'); + + CreateGenericItem(ExtraItem, VATPostingSetup."VAT Prod. Posting Group"); + CreateItemUnitOfMeasure(ExtraItem."No.", UnitOfMeasure.Code); + LibraryItemReference.CreateItemReference(ItemReference, ExtraItem."No.", '', 'PCS', Enum::"Item Reference Type"::Vendor, Vendor."No.", '2000'); + end; + + procedure PostSalesShipment(var Customer: Record Customer) SalesShipmentHeader: Record "Sales Shipment Header"; + var + SalesHeader: Record "Sales Header"; + begin + this.CreateSalesHeaderWithItem(Customer, SalesHeader, Enum::"Sales Document Type"::Order); + SalesShipmentHeader.Get(this.LibrarySales.PostSalesDocument(SalesHeader, true, false)); + end; + + procedure CreateInboundEDocument(var EDocument: Record "E-Document"; EDocService: Record "E-Document Service") + var + EDocumentServiceStatus: Record "E-Document Service Status"; + begin + EDocument.Insert(); + EDocumentServiceStatus."E-Document Entry No" := EDocument."Entry No"; + EDocumentServiceStatus."E-Document Service Code" := EDocService.Code; + EDocumentServiceStatus.Insert(); + end; + + procedure MockPurchaseDraftPrepared(EDocument: Record "E-Document") + var + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentProcessing: Codeunit "E-Document Processing"; + begin + EDocumentPurchaseHeader.InsertForEDocument(EDocument); + EDocumentPurchaseHeader."Sub Total" := 1000; + EDocumentPurchaseHeader."Total VAT" := 100; + EDocumentPurchaseHeader.Total := 1100; + EDocumentPurchaseHeader.Modify(); + EDocumentProcessing.ModifyEDocumentProcessingStatus(EDocument, "Import E-Doc. Proc. Status"::"Draft Ready"); + EDocument."Document Type" := "E-Document Type"::"Purchase Invoice"; + EDocument.Modify(); + end; + + procedure CreateInboundPEPPOLDocumentToState(var EDocument: Record "E-Document"; EDocumentService: Record "E-Document Service"; FileName: Text; EDocImportParams: Record "E-Doc. Import Parameters"): Boolean + var + EDocImport: Codeunit "E-Doc. Import"; + InStream: InStream; + begin + NavApp.GetResource(FileName, InStream, TextEncoding::UTF8); + EDocImport.CreateFromType(EDocument, EDocumentService, Enum::"E-Doc. File Format"::XML, 'TestFile', InStream); + exit(EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParams)); + end; + + /// + /// Given a purchase header with purchase lines created from an e-document, it modifies the required fields to make it ready for posting. + /// + procedure EditPurchaseDocumentFromEDocumentForPosting(var PurchaseHeader: Record "Purchase Header"; var EDocument: Record "E-Document") + var + PurchaseLine: Record "Purchase Line"; + GLAccount: Record "G/L Account"; + GeneralPostingSetup: Record "General Posting Setup"; + GenBusinessPostingGroup: Record "Gen. Business Posting Group"; + GenProductPostingGroup: Record "Gen. Product Posting Group"; + VATBusinessPostingGroup: Record "VAT Business Posting Group"; + VATProductPostingGroup: Record "VAT Product Posting Group"; + LocalVATPostingSetup: Record "VAT Posting Setup"; + begin + Assert.AreEqual(EDocument.SystemId, PurchaseHeader."E-Document Link", 'The purchase header has no link to the e-document.'); + LibraryERM.CreateGenBusPostingGroup(GenBusinessPostingGroup); + LibraryERM.CreateGenProdPostingGroup(GenProductPostingGroup); + LibraryERM.CreateGeneralPostingSetup(GeneralPostingSetup, GenBusinessPostingGroup.Code, GenProductPostingGroup.Code); + LibraryERM.CreateVATBusinessPostingGroup(VATBusinessPostingGroup); + LibraryERM.CreateVATProductPostingGroup(VATProductPostingGroup); + LibraryERM.CreateVATPostingSetup(LocalVATPostingSetup, VATBusinessPostingGroup.Code, VATProductPostingGroup.Code); + LibraryERM.CreateGLAccount(GLAccount); + LocalVATPostingSetup."Purchase VAT Account" := GLAccount."No."; + LocalVATPostingSetup.Modify(); + LibraryERM.CreateGLAccount(GLAccount); + PurchaseLine.SetRange("Document Type", PurchaseHeader."Document Type"); + PurchaseLine.SetRange("Document No.", PurchaseHeader."No."); + PurchaseLine.FindSet(); + repeat + PurchaseLine.Type := PurchaseLine.Type::"G/L Account"; + PurchaseLine."No." := GLAccount."No."; + PurchaseLine."Gen. Bus. Posting Group" := GenBusinessPostingGroup.Code; + PurchaseLine."Gen. Prod. Posting Group" := GenProductPostingGroup.Code; + PurchaseLine."VAT Bus. Posting Group" := VATBusinessPostingGroup.Code; + PurchaseLine."VAT Prod. Posting Group" := VATProductPostingGroup.Code; + PurchaseLine.UpdateAmounts(); + PurchaseLine.Modify(); + until PurchaseLine.Next() = 0; + PurchaseHeader.CalcFields("Amount Including VAT"); + EDocument."Amount Incl. VAT" := PurchaseHeader."Amount Including VAT"; + EDocument.Modify(); + end; + + procedure CreateDocSendingProfile(var DocumentSendingProfile: Record "Document Sending Profile") + begin + DocumentSendingProfile.Init(); + DocumentSendingProfile.Code := LibraryUtility.GenerateRandomCode(DocumentSendingProfile.FieldNo(Code), DATABASE::"Document Sending Profile"); + DocumentSendingProfile.Insert(); + end; + + + procedure CreateSimpleFlow(DocSendingProfileCode: Code[20]; ServiceCode: Code[20]): Code[20] + var + Workflow: Record Workflow; + WorkflowStepResponse: Record "Workflow Step"; + WorkflowStepArgument: Record "Workflow Step Argument"; + WorkflowStep: Record "Workflow Step"; + EDocWorkflowSetup: Codeunit "E-Document Workflow Setup"; + EDocCreatedEventID, SendEDocResponseEventID : Integer; + begin + // Create a simple workflow + // Send to Service 'ServiceCode' when using Document Sending Profile 'DocSendingProfile' + WorkflowStep.SetRange("Function Name", EDocWorkflowSetup.EDocCreated()); + WorkflowStep.SetRange("Entry Point", true); + if WorkflowStep.FindSet() then + repeat + Workflow.Get(WorkflowStep."Workflow Code"); + if not Workflow.Template then + exit; + until WorkflowStep.Next() = 0; + + LibraryWorkflow.CreateWorkflow(Workflow); + EDocCreatedEventID := LibraryWorkflow.InsertEntryPointEventStep(Workflow, EDocWorkflowSetup.EDocCreated()); + SendEDocResponseEventID := LibraryWorkflow.InsertResponseStep(Workflow, EDocWorkflowSetup.EDocSendEDocResponseCode(), EDocCreatedEventID); + + WorkflowStepResponse.Get(Workflow.Code, SendEDocResponseEventID); + WorkflowStepArgument.Get(WorkflowStepResponse.Argument); + + WorkflowStepArgument."E-Document Service" := ServiceCode; + WorkflowStepArgument.Modify(); + + LibraryWorkflow.EnableWorkflow(Workflow); + exit(Workflow.Code); + end; + + procedure CreateCustomerNoWithEDocSendingProfile(var DocumentSendingProfile: Code[20]): Code[20] + var + CustomerNo: Code[20]; + begin + CustomerNo := LibrarySales.CreateCustomerNo(); + DocumentSendingProfile := CreateDocumentSendingProfileForWorkflow(CustomerNo, ''); + exit(CustomerNo); + end; + + procedure CreateEDocumentFromSales(var EDocument: Record "E-Document") + begin + CreateEDocumentFromSales(EDocument, LibrarySales.CreateCustomerNo()); + end; + + procedure CreateEDocumentFromSales(var EDocument: Record "E-Document"; CustomerNo: Code[20]) + var + SalesInvHeader: Record "Sales Invoice Header"; + SalesHeader: Record "Sales Header"; + begin + LibrarySales.CreateSalesInvoiceForCustomerNo(SalesHeader, CustomerNo); + SalesInvHeader.Get(LibrarySales.PostSalesDocument(SalesHeader, true, true)); + EDocument.FindLast(); + end; + + local procedure CreateGenericSalesHeader(var Cust: Record Customer; var SalesHeader: Record "Sales Header"; DocumentType: Enum "Sales Document Type") + begin + LibrarySales.CreateSalesHeader(SalesHeader, DocumentType, Cust."No."); + SalesHeader.Validate("Your Reference", LibraryUtility.GenerateRandomCode(SalesHeader.FieldNo("Your Reference"), DATABASE::"Sales Header")); + + if DocumentType = SalesHeader."Document Type"::"Credit Memo" then + SalesHeader.Validate("Shipment Date", WorkDate()); + + SalesHeader.Modify(true); + end; + + procedure CreateGenericItem(var Item: Record Item; VATProdPostingGroupCode: Code[20]) + begin + CreateGenericItem(Item); + Item."VAT Prod. Posting Group" := VATPostingSetup."VAT Prod. Posting Group"; + Item.Modify(); + end; + + procedure CreateGenericItem(var Item: Record Item) + var + UOM: Record "Unit of Measure"; + ItemUOM: Record "Item Unit of Measure"; + QtyPerUnit: Integer; + begin + QtyPerUnit := LibraryRandom.RandInt(10); + + LibraryInvt.CreateUnitOfMeasureCode(UOM); + UOM.Validate("International Standard Code", + LibraryUtility.GenerateRandomCode(UOM.FieldNo("International Standard Code"), DATABASE::"Unit of Measure")); + UOM.Modify(true); + + CreateItemWithPrice(Item, LibraryRandom.RandInt(10)); + + LibraryInvt.CreateItemUnitOfMeasure(ItemUOM, Item."No.", UOM.Code, QtyPerUnit); + + Item.Validate("Sales Unit of Measure", UOM.Code); + Item.Modify(true); + end; + + local procedure CreateItemWithPrice(var Item: Record Item; UnitPrice: Decimal) + begin + LibraryInvt.CreateItem(Item); + Item."Unit Price" := UnitPrice; + Item.Modify(); + end; + + procedure SetupCompanyInfo() + var + CompanyInfo: Record "Company Information"; + CountryRegion: Record "Country/Region"; + begin + LibraryERM.FindCountryRegion(CountryRegion); + + CompanyInfo.Get(); + CompanyInfo.Validate(IBAN, 'GB33BUKB20201555555555'); + CompanyInfo.Validate("SWIFT Code", 'MIDLGB22Z0K'); + CompanyInfo.Validate("Bank Branch No.", '1234'); + CompanyInfo.Validate(Address, CopyStr(LibraryUtility.GenerateRandomXMLText(MaxStrLen(CompanyInfo.Address)), 1, MaxStrLen(CompanyInfo.Address))); + CompanyInfo.Validate("Post Code", CopyStr(LibraryUtility.GenerateRandomXMLText(MaxStrLen(CompanyInfo."Post Code")), 1, MaxStrLen(CompanyInfo."Post Code"))); + CompanyInfo.Validate("City", CopyStr(LibraryUtility.GenerateRandomXMLText(MaxStrLen(CompanyInfo."City")), 1, MaxStrLen(CompanyInfo."Post Code"))); + CompanyInfo."Country/Region Code" := CountryRegion.Code; + + if CompanyInfo."VAT Registration No." = '' then + CompanyInfo."VAT Registration No." := LibraryERM.GenerateVATRegistrationNo(CompanyInfo."Country/Region Code"); + + CompanyInfo.Modify(true); + end; + + procedure CreateSalesHeaderWithItem(Customer: Record Customer; var SalesHeader: Record "Sales Header"; DocumentType: Enum "Sales Document Type") + var + SalesLine: Record "Sales Line"; + begin + CreateGenericSalesHeader(Customer, SalesHeader, DocumentType); + + if StandardItem."No." = '' then + CreateGenericItem(StandardItem); + + LibrarySales.CreateSalesLine(SalesLine, SalesHeader, SalesLine.Type::Item, StandardItem."No.", 1); + end; + + procedure CreatePurchaseOrderWithLine(var Vendor: Record Vendor; var PurchaseHeader: Record "Purchase Header"; var PurchaseLine: Record "Purchase Line"; Quantity: Decimal) + begin + LibraryPurchase.CreatePurchHeader(PurchaseHeader, Enum::"Purchase Document Type"::Order, Vendor."No."); + if StandardItem."No." = '' then + CreateGenericItem(StandardItem); + LibraryPurchase.CreatePurchaseLine(PurchaseLine, PurchaseHeader, PurchaseLine.Type::Item, StandardItem."No.", Quantity); + end; + + procedure PostSalesDocument(var SalesHeader: Record "Sales Header"; var SalesInvHeader: Record "Sales Invoice Header") + begin + SalesInvHeader.Get(LibrarySales.PostSalesDocument(SalesHeader, true, true)); + end; + + procedure PostSalesDocument(var SalesHeader: Record "Sales Header"; var SalesInvHeader: Record "Sales Invoice Header"; Ship: Boolean) + begin + SalesInvHeader.Get(LibrarySales.PostSalesDocument(SalesHeader, Ship, true)); + end; + + procedure CreateReminderWithLine(Customer: Record Customer; var ReminderHeader: Record "Reminder Header") + var + ReminderLine: Record "Reminder Line"; + begin + LibraryERM.CreateReminderHeader(ReminderHeader); + ReminderHeader.Validate("Customer No.", Customer."No."); + ReminderHeader."Your Reference" := LibraryRandom.RandText(35); + ReminderHeader.Modify(false); + + LibraryERM.CreateReminderLine(ReminderLine, ReminderHeader."No.", Enum::"Reminder Source Type"::"G/L Account"); + ReminderLine.Validate("Remaining Amount", this.LibraryRandom.RandInt(100)); + ReminderLine.Description := LibraryRandom.RandText(100); + ReminderLine.Modify(false); + end; + + procedure CreateFinChargeMemoWithLine(Customer: Record Customer; var FinChargeMemoHeader: Record "Finance Charge Memo Header") + var + FinChargeMemoLine: Record "Finance Charge Memo Line"; + FinanceChargeTerms: Record "Finance Charge Terms"; + begin + LibraryERM.CreateFinanceChargeMemoHeader(FinChargeMemoHeader, Customer."No."); + LibraryFinChargeMemo.CreateFinanceChargeTermAndText(FinanceChargeTerms); + FinChargeMemoHeader.Validate("Fin. Charge Terms Code", FinanceChargeTerms.Code); + FinChargeMemoHeader."Your Reference" := LibraryRandom.RandText(35); + FinChargeMemoHeader.Modify(false); + + LibraryERM.CreateFinanceChargeMemoLine(FinChargeMemoLine, FinChargeMemoHeader."No.", FinChargeMemoLine.Type::"G/L Account"); + FinChargeMemoLine.Validate("Remaining Amount", this.LibraryRandom.RandInt(100)); + FinChargeMemoLine.Description := LibraryRandom.RandText(100); + FinChargeMemoLine.Modify(false); + end; + + procedure IssueReminder(Customer: Record Customer) IssuedReminderHeader: Record "Issued Reminder Header" + var + ReminderHeader: Record "Reminder Header"; + ReminderIssue: Codeunit "Reminder-Issue"; + begin + CreateReminderWithLine(Customer, ReminderHeader); + + ReminderHeader.SetRange("No.", ReminderHeader."No."); + ReminderIssue.Set(ReminderHeader, false, 0D); + ReminderIssue.Run(); + + ReminderIssue.GetIssuedReminder(IssuedReminderHeader); + end; + + procedure IssueFinChargeMemo(Customer: Record Customer) IssuedFinChargeMemoHeader: Record "Issued Fin. Charge Memo Header" + var + FinChargeMemoHeader: Record "Finance Charge Memo Header"; + FinChargeMemoIssue: Codeunit "FinChrgMemo-Issue"; + begin + CreateFinChargeMemoWithLine(Customer, FinChargeMemoHeader); + + FinChargeMemoHeader.SetRange("No.", FinChargeMemoHeader."No."); + FinChargeMemoIssue.Set(FinChargeMemoHeader, false, 0D); + FinChargeMemoIssue.Run(); + + FinChargeMemoIssue.GetIssuedFinChrgMemo(IssuedFinChargeMemoHeader); + end; + + procedure SetupReminderNoSeries() + var + SalesSetup: Record "Sales & Receivables Setup"; + begin + SalesSetup.Get(); + SalesSetup.Validate("Reminder Nos.", this.LibraryERM.CreateNoSeriesCode()); + SalesSetup.Validate("Issued Reminder Nos.", this.LibraryERM.CreateNoSeriesCode()); + SalesSetup.Modify(false); + end; + + procedure SetupFinChargeMemoNoSeries() + var + SalesSetup: Record "Sales & Receivables Setup"; + begin + SalesSetup.Get(); + SalesSetup.Validate("Fin. Chrg. Memo Nos.", this.LibraryERM.CreateNoSeriesCode()); + SalesSetup.Validate("Issued Fin. Chrg. M. Nos.", this.LibraryERM.CreateNoSeriesCode()); + SalesSetup.Modify(false); + end; + + procedure Initialize() + var + DocumentSendingProfile: Record "Document Sending Profile"; + EDocService: Record "E-Document Service"; + EDocServiceStatus: Record "E-Document Service Status"; + EDocServiceSupportedType: Record "E-Doc. Service Supported Type"; + EDocMapping: Record "E-Doc. Mapping"; + EDocLogs: Record "E-Document Log"; + EDocMappingLogs: Record "E-Doc. Mapping Log"; + EDocDataStorage: Record "E-Doc. Data Storage"; + EDocument: Record "E-Document"; + WorkflowSetup: Codeunit "Workflow Setup"; + begin + LibraryWorkflow.DeleteAllExistingWorkflows(); + WorkflowSetup.InitWorkflow(); + DocumentSendingProfile.DeleteAll(); + EDocService.DeleteAll(); + EDocServiceSupportedType.DeleteAll(); + EDocument.DeleteAll(); + EDocServiceStatus.DeleteAll(); + EDocDataStorage.DeleteAll(); + EDocMapping.DeleteAll(); + EDocLogs.DeleteAll(); + EDocMappingLogs.DeleteAll(); + Commit(); + end; + + procedure PostSalesDocument(CustomerNo: Code[20]): Code[20] + var + SalesInvHeader: Record "Sales Invoice Header"; + SalesHeader: Record "Sales Header"; + begin + LibrarySales.CreateSalesInvoiceForCustomerNo(SalesHeader, CustomerNo); + SalesInvHeader.Get(LibrarySales.PostSalesDocument(SalesHeader, true, true)); + exit(SalesInvHeader."No."); + end; + + procedure PostSalesDocument(): Code[20] + begin + PostSalesDocument(''); + end; + + procedure CreateDocumentSendingProfileForWorkflow(CustomerNo: Code[20]; WorkflowCode: Code[20]): Code[20] + var + Customer: Record Customer; + DocumentSendingProfile: Record "Document Sending Profile"; + begin + DocumentSendingProfile.Init(); + DocumentSendingProfile.Code := LibraryUtility.GenerateRandomCode20(DocumentSendingProfile.FieldNo(Code), Database::"Document Sending Profile"); + DocumentSendingProfile."Electronic Document" := Enum::"Doc. Sending Profile Elec.Doc."::"Extended E-Document Service Flow"; + DocumentSendingProfile."Electronic Service Flow" := WorkflowCode; + DocumentSendingProfile.Insert(); + + Customer.Get(CustomerNo); + Customer.Validate("Document Sending Profile", DocumentSendingProfile.Code); + Customer.Modify(); + exit(DocumentSendingProfile.Code); + end; + + procedure UpdateWorkflowOnDocumentSendingProfile(DocSendingProfile: Code[20]; WorkflowCode: Code[20]) + var + DocumentSendingProfile: Record "Document Sending Profile"; + begin + DocumentSendingProfile.Get(DocSendingProfile); + DocumentSendingProfile.Validate("Electronic Service Flow", WorkflowCode); + DocumentSendingProfile.Modify(); + end; + + procedure CreateFlowWithService(DocSendingProfile: Code[20]; ServiceCode: Code[20]): Code[20] + var + Workflow: Record Workflow; + WorkflowStepResponse: Record "Workflow Step"; + WorkflowStepArgument: Record "Workflow Step Argument"; + EDocWorkflowSetup: Codeunit "E-Document Workflow Setup"; + EDocCreatedEventID, SendEDocResponseEventID : Integer; + EventConditions: Text; + begin + LibraryWorkflow.CreateWorkflow(Workflow); + EventConditions := CreateWorkflowEventConditionDocSendingProfileFilter(DocSendingProfile); + + EDocCreatedEventID := LibraryWorkflow.InsertEntryPointEventStep(Workflow, EDocWorkflowSetup.EDocCreated()); + LibraryWorkflow.InsertEventArgument(EDocCreatedEventID, EventConditions); + SendEDocResponseEventID := LibraryWorkflow.InsertResponseStep(Workflow, EDocWorkflowSetup.EDocSendEDocResponseCode(), EDocCreatedEventID); + + WorkflowStepResponse.Get(Workflow.Code, SendEDocResponseEventID); + WorkflowStepArgument.Get(WorkflowStepResponse.Argument); + + WorkflowStepArgument.Validate("E-Document Service", ServiceCode); + WorkflowStepArgument.Modify(); + + LibraryWorkflow.EnableWorkflow(Workflow); + exit(Workflow.Code); + end; + + procedure CreateEmptyFlow(): Code[20] + var + Workflow: Record Workflow; + EDocWorkflowSetup: Codeunit "E-Document Workflow Setup"; + EDocCreatedEventID: Integer; + begin + LibraryWorkflow.CreateWorkflow(Workflow); + EDocCreatedEventID := LibraryWorkflow.InsertEntryPointEventStep(Workflow, EDocWorkflowSetup.EDocCreated()); + LibraryWorkflow.InsertResponseStep(Workflow, EDocWorkflowSetup.EDocSendEDocResponseCode(), EDocCreatedEventID); + + LibraryWorkflow.EnableWorkflow(Workflow); + exit(Workflow.Code); + end; + + procedure CreateFlowWithServices(DocSendingProfile: Code[20]; ServiceCodeA: Code[20]; ServiceCodeB: Code[20]): Code[20] + var + Workflow: Record Workflow; + WorkflowStepResponse: Record "Workflow Step"; + WorkflowStepArgument: Record "Workflow Step Argument"; + EDocWorkflowSetup: Codeunit "E-Document Workflow Setup"; + EDocCreatedEventID, SendEDocResponseEventIDA, SendEDocResponseEventIDB : Integer; + EventConditionsDocProfile, EventConditionsService : Text; + begin + LibraryWorkflow.CreateWorkflow(Workflow); + EventConditionsDocProfile := CreateWorkflowEventConditionDocSendingProfileFilter(DocSendingProfile); + EventConditionsService := CreateWorkflowEventConditionServiceFilter(ServiceCodeA); + + EDocCreatedEventID := LibraryWorkflow.InsertEntryPointEventStep(Workflow, EDocWorkflowSetup.EDocCreated()); + LibraryWorkflow.InsertEventArgument(EDocCreatedEventID, EventConditionsDocProfile); + SendEDocResponseEventIDA := LibraryWorkflow.InsertResponseStep(Workflow, EDocWorkflowSetup.EDocSendEDocResponseCode(), EDocCreatedEventID); + SendEDocResponseEventIDB := LibraryWorkflow.InsertResponseStep(Workflow, EDocWorkflowSetup.EDocSendEDocResponseCode(), SendEDocResponseEventIDA); + + WorkflowStepResponse.Get(Workflow.Code, SendEDocResponseEventIDA); + WorkflowStepArgument.Get(WorkflowStepResponse.Argument); + WorkflowStepArgument."E-Document Service" := ServiceCodeA; + WorkflowStepArgument.Modify(); + + WorkflowStepResponse.Get(Workflow.Code, SendEDocResponseEventIDB); + WorkflowStepArgument.Get(WorkflowStepResponse.Argument); + WorkflowStepArgument."E-Document Service" := ServiceCodeB; + WorkflowStepArgument.Modify(); + + LibraryWorkflow.EnableWorkflow(Workflow); + exit(Workflow.Code); + end; + + local procedure DeleteEDocumentRelatedEntities() + var + DynamicRequestPageEntity: Record "Dynamic Request Page Entity"; + begin + DynamicRequestPageEntity.SetRange("Table ID", DATABASE::"E-Document"); + DynamicRequestPageEntity.DeleteAll(true); + end; + + local procedure CreateWorkflowEventConditionDocSendingProfileFilter(DocSendingProfile: Code[20]): Text + var + RequestPageParametersHelper: Codeunit "Request Page Parameters Helper"; + FilterPageBuilder: FilterPageBuilder; + EntityName: Code[20]; + begin + EntityName := CreateDynamicRequestPageEntity(DATABASE::"E-Document", Database::"Document Sending Profile"); + CreateEDocumentDocSendingProfileDataItem(FilterPageBuilder, DocSendingProfile); + exit(RequestPageParametersHelper.GetViewFromDynamicRequestPage(FilterPageBuilder, EntityName, Database::"E-Document")); + end; + + local procedure CreateWorkflowEventConditionServiceFilter(ServiceCode: Code[20]): Text + var + RequestPageParametersHelper: Codeunit "Request Page Parameters Helper"; + FilterPageBuilder: FilterPageBuilder; + EntityName: Code[20]; + begin + EntityName := CreateDynamicRequestPageEntity(DATABASE::"E-Document", Database::"E-Document Service"); + CreateEDocServiceDataItem(FilterPageBuilder, ServiceCode); + exit(RequestPageParametersHelper.GetViewFromDynamicRequestPage(FilterPageBuilder, EntityName, Database::"E-Document Service")); + end; + + local procedure CreateEDocumentDocSendingProfileDataItem(var FilterPageBuilder: FilterPageBuilder; DocumentSendingProfile: Code[20]) + var + EDocument: Record "E-Document"; + EDocumentDataItem: Text; + begin + EDocumentDataItem := FilterPageBuilder.AddTable(EDocument.TableCaption, DATABASE::"E-Document"); + FilterPageBuilder.AddField(EDocumentDataItem, EDocument."Document Sending Profile", DocumentSendingProfile); + end; + + local procedure CreateEDocServiceDataItem(var FilterPageBuilder: FilterPageBuilder; ServiceCode: Code[20]) + var + EDocService: Record "E-Document Service"; + EDocumentDataItem: Text; + begin + EDocumentDataItem := FilterPageBuilder.AddTable(EDocService.TableCaption, DATABASE::"E-Document Service"); + FilterPageBuilder.AddField(EDocumentDataItem, EDocService.Code, ServiceCode); + end; + + local procedure CreateDynamicRequestPageEntity(TableID: Integer; RelatedTable: Integer): Code[20] + var + EntityName: Code[20]; + begin + DeleteEDocumentRelatedEntities(); + EntityName := LibraryUtility.GenerateGUID(); + LibraryWorkflow.CreateDynamicRequestPageEntity(EntityName, TableID, RelatedTable); + exit(EntityName); + end; + +#if not CLEAN26 +#pragma warning disable AL0432 + [Obsolete('Use CreateService(EDocDocumentFormat: Enum "E-Document Format"; EDocIntegration: Enum "Service Integration") instead', '26.0')] + procedure CreateService(Integration: Enum "E-Document Integration"): Code[20] + var + EDocService: Record "E-Document Service"; + begin + EDocService.Init(); + EDocService.Code := LibraryUtility.GenerateRandomCode20(EDocService.FieldNo(Code), Database::"E-Document Service"); + EDocService."Document Format" := "E-Document Format"::Mock; + EDocService."Service Integration" := Integration; + EDocService.Insert(); + + CreateSupportedDocTypes(EDocService); + + exit(EDocService.Code); + end; +#pragma warning restore AL0432 +#endif + + procedure CreateService(Integration: Enum "Service Integration"): Code[20] + var + EDocService: Record "E-Document Service"; + begin + EDocService.Init(); + EDocService.Code := LibraryUtility.GenerateRandomCode20(EDocService.FieldNo(Code), Database::"E-Document Service"); + EDocService."Document Format" := "E-Document Format"::Mock; + EDocService."Service Integration V2" := Integration; + EDocService.Insert(); + + CreateSupportedDocTypes(EDocService); + + exit(EDocService.Code); + end; + +#if not CLEAN26 +#pragma warning disable AL0432 + [Obsolete('Use CreateService(EDocDocumentFormat: Enum "E-Document Format"; EDocIntegration: Enum "Service Integration") instead', '26.0')] + procedure CreateService(EDocDocumentFormat: Enum "E-Document Format"; EDocIntegration: Enum "E-Document Integration"): Code[20] + var + EDocService: Record "E-Document Service"; + begin + EDocService.Init(); + EDocService.Code := LibraryUtility.GenerateRandomCode20(EDocService.FieldNo(Code), Database::"E-Document Service"); + EDocService."Document Format" := EDocDocumentFormat; + EDocService."Service Integration" := EDocIntegration; + EDocService.Insert(); + + CreateSupportedDocTypes(EDocService); + + exit(EDocService.Code); + end; +#pragma warning restore AL0432 +#endif + + procedure CreateService(EDocDocumentFormat: Enum "E-Document Format"; EDocIntegration: Enum "Service Integration"): Code[20] + var + EDocService: Record "E-Document Service"; + begin + EDocService.Init(); + EDocService.Code := LibraryUtility.GenerateRandomCode20(EDocService.FieldNo(Code), Database::"E-Document Service"); + EDocService."Document Format" := EDocDocumentFormat; + EDocService."Service Integration V2" := EDocIntegration; + EDocService.Insert(); + + CreateSupportedDocTypes(EDocService); + + exit(EDocService.Code); + end; + + + procedure CreateServiceMapping(EDocService: Record "E-Document Service") + var + TransformationRule: Record "Transformation Rule"; + EDocMapping: Record "E-Doc. Mapping"; + SalesInvHeader: Record "Sales Invoice Header"; + begin + TransformationRule.Get(TransformationRule.GetLowercaseCode()); + // Lower case mapping + CreateTransformationMapping(EDocMapping, TransformationRule, EDocService.Code); + EDocMapping."Table ID" := Database::"Sales Invoice Header"; + EDocMapping."Field ID" := SalesInvHeader.FieldNo("Bill-to Name"); + EDocMapping.Modify(); + CreateTransformationMapping(EDocMapping, TransformationRule, EDocService.Code); + EDocMapping."Table ID" := Database::"Sales Invoice Header"; + EDocMapping."Field ID" := SalesInvHeader.FieldNo("Bill-to Address"); + EDocMapping.Modify(); + end; + + procedure DeleteServiceMapping(EDocService: Record "E-Document Service") + var + EDocMapping: Record "E-Doc. Mapping"; + begin + EDocMapping.SetRange(Code, EDocService.Code); + EDocMapping.DeleteAll(); + end; + + + // procedure CreateServiceWithMapping(var EDocMapping: Record "E-Doc. Mapping"; TransformationRule: Record "Transformation Rule"; Integration: Enum "E-Document Integration"): Code[20] + // begin + // exit(CreateServiceWithMapping(EDocMapping, TransformationRule, false, Integration)); + // end; + + // procedure CreateServiceWithMapping(var EDocMapping: Record "E-Doc. Mapping"; TransformationRule: Record "Transformation Rule"; UseBatching: Boolean; Integration: Enum "E-Document Integration"): Code[20] + // var + // SalesInvHeader: Record "Sales Invoice Header"; + // EDocService: Record "E-Document Service"; + // begin + // EDocService.Init(); + // EDocService.Code := LibraryUtility.GenerateRandomCode20(EDocService.FieldNo(Code), Database::"E-Document Service"); + // EDocService."Document Format" := "E-Document Format"::Mock; + // EDocService."Service Integration" := Integration; + // EDocService."Use Batch Processing" := UseBatching; + // EDocService.Insert(); + + // CreateSupportedDocTypes(EDocService); + + // // Lower case mapping + // CreateTransformationMapping(EDocMapping, TransformationRule, EDocService.Code); + // EDocMapping."Table ID" := Database::"Sales Invoice Header"; + // EDocMapping."Field ID" := SalesInvHeader.FieldNo("Bill-to Name"); + // EDocMapping.Modify(); + // CreateTransformationMapping(EDocMapping, TransformationRule, EDocService.Code); + // EDocMapping."Table ID" := Database::"Sales Invoice Header"; + // EDocMapping."Field ID" := SalesInvHeader.FieldNo("Bill-to Address"); + // EDocMapping.Modify(); + + // exit(EDocService.Code); + // end; + + procedure CreateSupportedDocTypes(EDocService: Record "E-Document Service") + var + EDocServiceSupportedType: Record "E-Doc. Service Supported Type"; + begin + EDocServiceSupportedType.Init(); + EDocServiceSupportedType."E-Document Service Code" := EDocService.Code; + EDocServiceSupportedType."Source Document Type" := EDocServiceSupportedType."Source Document Type"::"Sales Invoice"; + EDocServiceSupportedType.Insert(); + + EDocServiceSupportedType."Source Document Type" := EDocServiceSupportedType."Source Document Type"::"Sales Credit Memo"; + EDocServiceSupportedType.Insert(); + + EDocServiceSupportedType."Source Document Type" := EDocServiceSupportedType."Source Document Type"::"Service Invoice"; + EDocServiceSupportedType.Insert(); + + EDocServiceSupportedType."Source Document Type" := EDocServiceSupportedType."Source Document Type"::"Service Credit Memo"; + EDocServiceSupportedType.Insert(); + + EDocServiceSupportedType."Source Document Type" := EDocServiceSupportedType."Source Document Type"::"Issued Finance Charge Memo"; + EDocServiceSupportedType.Insert(); + + EDocServiceSupportedType."Source Document Type" := EDocServiceSupportedType."Source Document Type"::"Issued Reminder"; + EDocServiceSupportedType.Insert(); + end; + + procedure AddEDocServiceSupportedType(EDocService: Record "E-Document Service"; EDocumentType: Enum "E-Document Type") + var + EDocServiceSupportedType: Record "E-Doc. Service Supported Type"; + begin + if not EDocService.Get(EDocService.Code) then + exit; + + EDocServiceSupportedType.Init(); + EDocServiceSupportedType."E-Document Service Code" := EDocService.Code; + EDocServiceSupportedType."Source Document Type" := EDocumentType; + if EDocServiceSupportedType.Insert() then; + end; + + procedure CreateTestReceiveServiceForEDoc(var EDocService: Record "E-Document Service"; Integration: Enum "Service Integration") + begin + if not EDocService.Get('TESTRECEIVE') then begin + EDocService.Init(); + EDocService.Code := 'TESTRECEIVE'; + EDocService."Document Format" := "E-Document Format"::Mock; + EDocService."Service Integration V2" := Integration; + EDocService.Insert(); + end; + end; + +#if not CLEAN26 +#pragma warning disable AL0432 + [Obsolete('Use CreateTestReceiveServiceForEDoc(var EDocService: Record "E-Document Service"; Integration: Enum "Service Integration") instead', '26.0')] + procedure CreateTestReceiveServiceForEDoc(var EDocService: Record "E-Document Service"; Integration: Enum "E-Document Integration") + begin + if not EDocService.Get('TESTRECEIVE') then begin + EDocService.Init(); + EDocService.Code := 'TESTRECEIVE'; + EDocService."Document Format" := "E-Document Format"::Mock; + EDocService."Service Integration" := Integration; + EDocService.Insert(); + end; + end; +#pragma warning restore AL0432 +#endif + + procedure CreateGetBasicInfoErrorReceiveServiceForEDoc(var EDocService: Record "E-Document Service"; Integration: Enum "Service Integration") + begin + if not EDocService.Get('BIERRRECEIVE') then begin + EDocService.Init(); + EDocService.Code := 'BIERRRECEIVE'; + EDocService."Document Format" := "E-Document Format"::Mock; + EDocService."Service Integration V2" := Integration; + EDocService.Insert(); + end; + end; + +#if not CLEAN26 +#pragma warning disable AL0432 + [Obsolete('Use CreateGetBasicInfoErrorReceiveServiceForEDoc(var EDocService: Record "E-Document Service"; Integration: Enum "Service Integration") instead', '26.0')] + procedure CreateGetBasicInfoErrorReceiveServiceForEDoc(var EDocService: Record "E-Document Service"; Integration: Enum "E-Document Integration") + begin + if not EDocService.Get('BIERRRECEIVE') then begin + EDocService.Init(); + EDocService.Code := 'BIERRRECEIVE'; + EDocService."Document Format" := "E-Document Format"::Mock; + EDocService."Service Integration" := Integration; + EDocService.Insert(); + end; + end; +#pragma warning restore AL0432 +#endif + + procedure CreateGetCompleteInfoErrorReceiveServiceForEDoc(var EDocService: Record "E-Document Service"; Integration: Enum "Service Integration") + begin + if not EDocService.Get('CIERRRECEIVE') then begin + EDocService.Init(); + EDocService.Code := 'CIERRRECEIVE'; + EDocService."Document Format" := "E-Document Format"::Mock; + EDocService."Service Integration V2" := Integration; + EDocService.Insert(); + end; + end; + +#if not CLEAN26 +#pragma warning disable AL0432 + [Obsolete('Use CreateGetCompleteInfoErrorReceiveServiceForEDoc(var EDocService: Record "E-Document Service"; Integration: Enum "Service Integration") instead', '26.0')] + procedure CreateGetCompleteInfoErrorReceiveServiceForEDoc(var EDocService: Record "E-Document Service"; Integration: Enum "E-Document Integration") + begin + if not EDocService.Get('CIERRRECEIVE') then begin + EDocService.Init(); + EDocService.Code := 'CIERRRECEIVE'; + EDocService."Document Format" := "E-Document Format"::Mock; + EDocService."Service Integration" := Integration; + EDocService.Insert(); + end; + end; +#pragma warning restore AL0432 +#endif + + procedure CreateDirectMapping(var EDocMapping: Record "E-Doc. Mapping"; EDocService: Record "E-Document Service"; FindValue: Text; ReplaceValue: Text) + begin + CreateDirectMapping(EDocMapping, EDocService, FindValue, ReplaceValue, 0, 0); + end; + + procedure CreateTransformationMapping(var EDocMapping: Record "E-Doc. Mapping"; TransformationRule: Record "Transformation Rule") + begin + CreateTransformationMapping(EDocMapping, TransformationRule, ''); + end; + + procedure CreateTransformationMapping(var EDocMapping: Record "E-Doc. Mapping"; TransformationRule: Record "Transformation Rule"; ServiceCode: Code[20]) + begin + EDocMapping.Init(); + EDocMapping.Code := ServiceCode; + EDocMapping."Entry No." := 0; + EDocMapping."Transformation Rule" := TransformationRule.Code; + EDocMapping.Insert(); + end; + + procedure CreateDirectMapping(var EDocMapping: Record "E-Doc. Mapping"; EDocService: Record "E-Document Service"; FindValue: Text; ReplaceValue: Text; TableId: Integer; FieldId: Integer) + begin + EDocMapping.Init(); + EDocMapping."Entry No." := 0; + EDocMapping.Code := EDocService.Code; + EDocMapping."Table ID" := TableId; + EDocMapping."Field ID" := FieldId; + EDocMapping."Find Value" := CopyStr(FindValue, 1, LibraryUtility.GetFieldLength(DATABASE::"E-Doc. Mapping", EDocMapping.FieldNo("Find Value"))); + EDocMapping."Replace Value" := CopyStr(ReplaceValue, 1, LibraryUtility.GetFieldLength(DATABASE::"E-Doc. Mapping", EDocMapping.FieldNo("Replace Value"))); + EDocMapping.Insert(); + end; + + local procedure CreateItemUnitOfMeasure(ItemNo: Code[20]; UnitOfMeasureCode: Code[10]) + var + ItemUnitOfMeasure: Record "Item Unit of Measure"; + begin + ItemUnitOfMeasure.Init(); + ItemUnitOfMeasure.Validate("Item No.", ItemNo); + ItemUnitOfMeasure.Validate(Code, UnitOfMeasureCode); + ItemUnitOfMeasure."Qty. per Unit of Measure" := 1; + if ItemUnitOfMeasure.Insert() then; + end; + + procedure TempBlobToTxt(var TempBlob: Codeunit "Temp Blob"): Text + var + InStr: InStream; + Content: Text; + begin + TempBlob.CreateInStream(InStr); + InStr.Read(Content); + exit(Content); + end; + + internal procedure CreateLocationsWithPostingSetups( + var FromLocation: Record Location; + var ToLocation: Record Location; + var InTransitLocation: Record Location; + var InventoryPostingGroup: Record "Inventory Posting Group") + var + InventoryPostingSetupFromLocation: Record "Inventory Posting Setup"; + InventoryPostingSetupToLocation: Record "Inventory Posting Setup"; + InventoryPostingSetupInTransitLocation: Record "Inventory Posting Setup"; + LibraryWarehouse: Codeunit "Library - Warehouse"; + begin + LibraryWarehouse.CreateLocation(FromLocation); + LibraryWarehouse.CreateLocation(ToLocation); + LibraryWarehouse.CreateInTransitLocation(InTransitLocation); + + LibraryInventory.CreateInventoryPostingGroup(InventoryPostingGroup); + LibraryInventory.CreateInventoryPostingSetup(InventoryPostingSetupFromLocation, FromLocation.Code, InventoryPostingGroup.Code); + LibraryInventory.UpdateInventoryPostingSetup(FromLocation, InventoryPostingGroup.Code); + + InventoryPostingSetupFromLocation.Get(FromLocation.Code, InventoryPostingGroup.Code); + InventoryPostingSetupToLocation := InventoryPostingSetupFromLocation; + InventoryPostingSetupToLocation."Location Code" := ToLocation.Code; + InventoryPostingSetupToLocation.Insert(false); + + InventoryPostingSetupInTransitLocation := InventoryPostingSetupFromLocation; + InventoryPostingSetupInTransitLocation."Location Code" := InTransitLocation.Code; + InventoryPostingSetupInTransitLocation.Insert(false); + end; + + internal procedure CreateItemWithInventoryPostingGroup(var Item: Record Item; InventoryPostingGroupCode: Code[20]) + begin + LibraryInventory.CreateItem(Item); + Item."Inventory Posting Group" := InventoryPostingGroupCode; + Item.Modify(false); + end; + + internal procedure CreateItemWIthInventoryStock(var Item: Record Item; var FromLocation: Record Location; var InventoryPostingGroup: Record "Inventory Posting Group") + var + ItemJournalLine: Record "Item Journal Line"; + begin + CreateItemWithInventoryPostingGroup(Item, InventoryPostingGroup.Code); + LibraryInventory.CreateItemJournalLineInItemTemplate( + ItemJournalLine, Item."No.", FromLocation.Code, '', LibraryRandom.RandIntInRange(10, 20)); + LibraryInventory.PostItemJournalLine( + ItemJournalLine."Journal Template Name", ItemJournalLine."Journal Batch Name"); + end; + + // Verify procedures + + procedure AssertEDocumentLogs(EDocument: Record "E-Document"; EDocumentService: Record "E-Document Service"; EDocLogList: List of [Enum "E-Document Service Status"]) + var + EDocLog: Record "E-Document Log"; + Count: Integer; + begin + EDocLog.SetRange("E-Doc. Entry No", EDocument."Entry No"); + EDocLog.SetRange("Service Code", EDocumentService.Code); + Assert.AreEqual(EDocLogList.Count(), EDocLog.Count(), 'Wrong number of logs'); + Count := 1; + EDocLog.SetCurrentKey("Entry No."); + EDocLog.SetAscending("Entry No.", true); + if EDocLog.FindSet() then + repeat + Assert.AreEqual(EDocLogList.Get(Count), EDocLog.Status, 'Wrong status'); + Count := Count + 1; + until EDocLog.Next() = 0; + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"E-Doc. Export", 'OnAfterCreateEDocument', '', false, false)] + local procedure OnAfterCreateEDocument(var EDocument: Record "E-Document") + begin + LibraryVariableStorage.Enqueue(EDocument); + end; +} \ No newline at end of file diff --git a/Apps/DE/EDocumentDE/test/src/XRechnungStructValidations.Codeunit.al b/Apps/DE/EDocumentDE/test/src/XRechnungStructValidations.Codeunit.al new file mode 100644 index 0000000000..844b19651c --- /dev/null +++ b/Apps/DE/EDocumentDE/test/src/XRechnungStructValidations.Codeunit.al @@ -0,0 +1,107 @@ +codeunit 13925 "XRechnung Struct. 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 + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + begin + EDocumentPurchaseHeader.Get(EDocumentEntryNo); + Assert.AreEqual(SalesInvoiceNoTok, EDocumentPurchaseHeader."Sales Invoice No.", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Sales Invoice No."), EDocumentPurchaseHeader.TableCaption(), SalesInvoiceNoTok, EDocumentPurchaseHeader."Sales Invoice No.")); + Assert.AreEqual(MockDate, EDocumentPurchaseHeader."Document Date", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Document Date"), EDocumentPurchaseHeader.TableCaption(), MockDate, EDocumentPurchaseHeader."Document Date")); + Assert.AreEqual(CalcDate('<+1M>', MockDate), EDocumentPurchaseHeader."Due Date", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Due Date"), EDocumentPurchaseHeader.TableCaption(), CalcDate('<+1M>', MockDate), EDocumentPurchaseHeader."Due Date")); + Assert.AreEqual(MockCurrencyCode, EDocumentPurchaseHeader."Currency Code", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Currency Code"), EDocumentPurchaseHeader.TableCaption(), MockCurrencyCode, EDocumentPurchaseHeader."Currency Code")); + Assert.AreEqual(PurchaseorderNoTok, EDocumentPurchaseHeader."Purchase Order No.", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Purchase Order No."), EDocumentPurchaseHeader.TableCaption(), PurchaseorderNoTok, EDocumentPurchaseHeader."Purchase Order No.")); + Assert.AreEqual('CRONUS International', EDocumentPurchaseHeader."Vendor Company Name", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Vendor Company Name"), EDocumentPurchaseHeader.TableCaption(), 'CRONUS International', EDocumentPurchaseHeader."Vendor Company Name")); + Assert.AreEqual('Main Street, 14', EDocumentPurchaseHeader."Vendor Address", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Vendor Address"), EDocumentPurchaseHeader.TableCaption(), 'Main Street, 14', EDocumentPurchaseHeader."Vendor Address")); + Assert.AreEqual('GB123456789', EDocumentPurchaseHeader."Vendor VAT Id", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Vendor VAT Id"), EDocumentPurchaseHeader.TableCaption(), 'GB123456789', EDocumentPurchaseHeader."Vendor VAT Id")); + Assert.AreEqual('Jim Olive', EDocumentPurchaseHeader."Vendor Contact Name", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Vendor Contact Name"), EDocumentPurchaseHeader.TableCaption(), 'Jim Olive', EDocumentPurchaseHeader."Vendor Contact Name")); + Assert.AreEqual('The Cannon Group PLC', EDocumentPurchaseHeader."Customer Company Name", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Customer Company Name"), EDocumentPurchaseHeader.TableCaption(), 'The Cannon Group PLC', EDocumentPurchaseHeader."Customer Company Name")); + Assert.AreEqual('GB789456278', EDocumentPurchaseHeader."Customer VAT Id", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Customer VAT Id"), EDocumentPurchaseHeader.TableCaption(), 'GB789456278', EDocumentPurchaseHeader."Customer VAT Id")); + Assert.AreEqual('192 Market Square', EDocumentPurchaseHeader."Customer Address", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Customer Address"), EDocumentPurchaseHeader.TableCaption(), '192 Market Square', EDocumentPurchaseHeader."Customer Address")); + + EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocumentEntryNo); + EDocumentPurchaseLine.FindSet(); + Assert.AreEqual(1, EDocumentPurchaseLine."Quantity", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Quantity"), EDocumentPurchaseLine.TableCaption(), 1, EDocumentPurchaseLine."Quantity")); + Assert.AreEqual(UnitOfMeasureCodeTok, EDocumentPurchaseLine."Unit of Measure", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Unit of Measure"), EDocumentPurchaseLine.TableCaption(), UnitOfMeasureCodeTok, EDocumentPurchaseLine."Unit of Measure")); + Assert.AreEqual(4000, EDocumentPurchaseLine."Sub Total", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Sub Total"), EDocumentPurchaseLine.TableCaption(), 4000, EDocumentPurchaseLine."Sub Total")); + Assert.AreEqual(MockCurrencyCode, EDocumentPurchaseLine."Currency Code", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Currency Code"), EDocumentPurchaseLine.TableCaption(), MockCurrencyCode, EDocumentPurchaseLine."Currency Code")); + Assert.AreEqual(0, EDocumentPurchaseLine."Total Discount", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Total Discount"), EDocumentPurchaseLine.TableCaption(), 0, EDocumentPurchaseLine."Total Discount")); + Assert.AreEqual('Bicycle', EDocumentPurchaseLine.Description, StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption(Description), EDocumentPurchaseLine.TableCaption(), 'Bicycle', EDocumentPurchaseLine.Description)); + Assert.AreEqual('1000', EDocumentPurchaseLine."Product Code", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Product Code"), EDocumentPurchaseLine.TableCaption(), '1000', EDocumentPurchaseLine."Product Code")); + Assert.AreEqual(25, EDocumentPurchaseLine."VAT Rate", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("VAT Rate"), EDocumentPurchaseLine.TableCaption(), 25, EDocumentPurchaseLine."VAT Rate")); + Assert.AreEqual(4000, EDocumentPurchaseLine."Unit Price", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Unit Price"), EDocumentPurchaseLine.TableCaption(), 4000, EDocumentPurchaseLine."Unit Price")); + + EDocumentPurchaseLine.Next(); + Assert.AreEqual(2, EDocumentPurchaseLine."Quantity", StrSubstNo(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 diff --git a/Apps/W1/EDocument/app/app.json b/Apps/W1/EDocument/app/app.json index 98dceb38a3..f193c1dead 100644 --- a/Apps/W1/EDocument/app/app.json +++ b/Apps/W1/EDocument/app/app.json @@ -37,6 +37,16 @@ "id": "967eceac-106e-4102-b76a-fe5dc3d799e5", "name": "Payables Agent Tests", "publisher": "Microsoft" + }, + { + "id": "fdeb586e-beff-49d8-947d-1e73ce980b34", + "name": "E-Document for Germany", + "publisher": "Microsoft" + }, + { + "id": "4022897d-41ca-4ae2-a766-a429d0b19273", + "name": "E-Document for Germany Tests", + "publisher": "Microsoft" } ], "screenshots": [],