diff --git a/Apps/ES/EDocumentFormats/FacturaE/app/src/Core/EDocumentFacturaEHandler.Codeunit.al b/Apps/ES/EDocumentFormats/FacturaE/app/src/Core/EDocumentFacturaEHandler.Codeunit.al new file mode 100644 index 0000000000..a82b692994 --- /dev/null +++ b/Apps/ES/EDocumentFormats/FacturaE/app/src/Core/EDocumentFacturaEHandler.Codeunit.al @@ -0,0 +1,284 @@ +// ------------------------------------------------------------------------------------------------ +// 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.Format.FacturaE; + +using Microsoft.eServices.EDocument; +using Microsoft.eServices.EDocument.Format; +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.Foundation.UOM; +using Microsoft.Purchases.Vendor; +using System.IO; +using System.Utilities; + +codeunit 10776 "E-Document Factura-E Handler" implements IStructuredFormatReader +{ + Access = Internal; + InherentEntitlements = X; + InherentPermissions = X; + + var + EDocumentImportHelper: Codeunit "E-Document Import Helper"; + + /// + /// Reads a Factura-E XML document and converts it into a draft purchase document. + /// + /// The E-Document record containing the document information. + /// The temporary blob containing the XML content to be processed. + /// Returns the type of draft document that was created (Purchase Document). + internal procedure ReadIntoDraft(EDocument: Record "E-Document"; TempBlob: Codeunit "Temp Blob"): Enum "E-Doc. Process Draft" + var + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + FacturaEXML: XmlDocument; + XmlNamespaces: XmlNamespaceManager; + XmlElement: XmlElement; + FacturaENamespaceTok: Label 'http://www.facturae.gob.es/formato/Versiones/Facturaev3_2_2.xml', Locked = true; + DigitalSignatureNamespaceTok: Label 'http://www.w3.org/2000/09/xmldsig#', Locked = true; + ETSINamespaceTok: Label 'http://uri.etsi.org/01903/v1.2.2#', Locked = true; + XMLNode: XmlNode; + begin + EDocumentPurchaseHeader.InsertForEDocument(EDocument); + + XmlDocument.ReadFrom(TempBlob.CreateInStream(TextEncoding::UTF8), FacturaEXML); + XmlNamespaces.AddNamespace('namespace', FacturaENamespaceTok); + XmlNamespaces.AddNamespace('ds', DigitalSignatureNamespaceTok); + XmlNamespaces.AddNamespace('etsi', ETSINamespaceTok); + + FacturaEXML.GetRoot(XmlElement); + EDocumentPurchaseHeader."E-Document Type" := "E-Document Type"::"Purchase Invoice"; + if FacturaEXML.SelectSingleNode('/namespace:Facturae/Invoices/Invoice/InvoiceHeader/Corrective', XmlNamespaces, XMLNode) then + EDocumentPurchaseHeader."E-Document Type" := "E-Document Type"::"Purchase Credit Memo"; + + PopulateFacturaEPurchaseInvoiceHeader(FacturaEXML, XmlNamespaces, EDocumentPurchaseHeader, EDocument); + InsertFacturaEPurchaseInvoiceLines(FacturaEXML, XmlNamespaces, EDocumentPurchaseHeader."E-Document Entry No."); + EDocumentPurchaseHeader.Modify(false); + EDocument.Direction := EDocument.Direction::Incoming; + exit(Enum::"E-Doc. Process Draft"::"Purchase Document"); + end; + + /// + /// Displays a readable view of the E-Document by opening the purchase document page. + /// + /// The E-Document record to be displayed. + /// The temporary blob containing the document content (not used in this 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; + +#pragma warning disable AA0139 // false positive: overflow handled by SetStringValueInField + local procedure PopulateFacturaEPurchaseInvoiceHeader(FacturaEXML: 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 + EDocumentXMLHelper.SetStringValueInField(FacturaEXML, XmlNamespaces, '/namespace:Facturae/Invoices/Invoice/InvoiceHeader/InvoiceNumber', MaxStrLen(EDocumentPurchaseHeader."Sales Invoice No."), EDocumentPurchaseHeader."Sales Invoice No."); + EDocumentXMLHelper.SetDateValueInField(FacturaEXML, XmlNamespaces, '/namespace:Facturae/Invoices/Invoice/InvoiceIssueData/IssueDate', EDocumentPurchaseHeader."Document Date"); + EDocumentXMLHelper.SetCurrencyValueInField(FacturaEXML, XmlNamespaces, '/namespace:Facturae/Invoices/Invoice/InvoiceIssueData/InvoiceCurrencyCode', MaxStrLen(EDocumentPurchaseHeader."Currency Code"), EDocumentPurchaseHeader."Currency Code"); + VendorNo := ParseSellerParty(FacturaEXML, XmlNamespaces, EDocument, EDocumentPurchaseHeader); + ParseBuyerParty(FacturaEXML, XmlNamespaces, EDocumentPurchaseHeader); + EDocumentXMLHelper.SetNumberValueInField(FacturaEXML, XmlNamespaces, '/namespace:Facturae/Invoices/Invoice/InvoiceTotals/TotalGrossAmountBeforeTaxes', EDocumentPurchaseHeader."Sub Total"); + EDocumentXMLHelper.SetNumberValueInField(FacturaEXML, XmlNamespaces, '/namespace:Facturae/Invoices/Invoice/InvoiceTotals/TotalTaxOutputs', EDocumentPurchaseHeader."Total VAT"); + EDocumentXMLHelper.SetNumberValueInField(FacturaEXML, XmlNamespaces, '/namespace:Facturae/Invoices/Invoice/InvoiceTotals/InvoiceTotal', EDocumentPurchaseHeader.Total); + EDocumentXMLHelper.SetNumberValueInField(FacturaEXML, XmlNamespaces, '/namespace:Facturae/Invoices/Invoice/InvoiceTotals/TotalOutstandingAmount', EDocumentPurchaseHeader."Amount Due"); + EDocumentXMLHelper.SetDateValueInField(FacturaEXML, XmlNamespaces, '/namespace:Facturae/Invoices/Invoice/InvoiceHeader/InvoiceDocumentReference/ReferencedDocumentDate', EDocumentPurchaseHeader."Due Date"); + EDocumentXMLHelper.SetStringValueInField(FacturaEXML, XmlNamespaces, '/namespace:Facturae/Invoices/Invoice/InvoiceHeader/Corrective/InvoiceNumber', MaxStrLen(EDocumentPurchaseHeader."Purchase Order No."), EDocumentPurchaseHeader."Purchase Order No."); + + if VendorNo <> '' then + EDocumentPurchaseHeader."[BC] Vendor No." := VendorNo; + end; +#pragma warning restore AA0139 + + local procedure InsertFacturaEPurchaseInvoiceLines(FacturaEXML: XmlDocument; XmlNamespaces: XmlNamespaceManager; EDocumentEntryNo: Integer) + var + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + NewLineXML: XmlDocument; + LineXMLList: XmlNodeList; + LineXMLNode: XmlNode; + InvoiceLinePathTok: Label '/namespace:Facturae/Invoices/Invoice/Items/InvoiceLine', Locked = true; + begin + if not FacturaEXML.SelectNodes(InvoiceLinePathTok, 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); + PopulateFacturaEPurchaseLine(NewLineXML, XmlNamespaces, EDocumentPurchaseLine); + EDocumentPurchaseLine.Insert(false); + end; + end; + +#pragma warning disable AA0139 // false positive: overflow handled by SetStringValueInField + local procedure PopulateFacturaEPurchaseLine(LineXML: XmlDocument; XmlNamespaces: XmlNamespaceManager; var EDocumentPurchaseLine: Record "E-Document Purchase Line") + var + EDocumentXMLHelper: Codeunit "EDocument XML Helper"; + UOMCode: Text; + XMLNode: XmlNode; + begin + EDocumentXMLHelper.SetStringValueInField(LineXML, XmlNamespaces, 'InvoiceLine/ArticleCode', MaxStrLen(EDocumentPurchaseLine."Product Code"), EDocumentPurchaseLine."Product Code"); + EDocumentXMLHelper.SetStringValueInField(LineXML, XmlNamespaces, 'InvoiceLine/ItemDescription', MaxStrLen(EDocumentPurchaseLine.Description), EDocumentPurchaseLine.Description); + EDocumentXMLHelper.SetNumberValueInField(LineXML, XmlNamespaces, 'InvoiceLine/Quantity', EDocumentPurchaseLine.Quantity); + EDocumentXMLHelper.SetNumberValueInField(LineXML, XmlNamespaces, 'InvoiceLine/UnitPriceWithoutTax', EDocumentPurchaseLine."Unit Price"); + EDocumentXMLHelper.SetNumberValueInField(LineXML, XmlNamespaces, 'InvoiceLine/TotalCost', EDocumentPurchaseLine."Sub Total"); + EDocumentXMLHelper.SetCurrencyValueInField(LineXML, XmlNamespaces, 'InvoiceLine/TotalCost/@currencyID', MaxStrLen(EDocumentPurchaseLine."Currency Code"), EDocumentPurchaseLine."Currency Code"); + + // Handle Unit of Measure like in legacy system + if LineXML.SelectSingleNode('InvoiceLine/UnitOfMeasure', XmlNamespaces, XMLNode) then begin + UOMCode := XMLNode.AsXmlElement().InnerText(); + EDocumentPurchaseLine."Unit of Measure" := CopyStr(UOMCode, 1, MaxStrLen(EDocumentPurchaseLine."Unit of Measure")); + EDocumentPurchaseLine."[BC] Unit of Measure" := TryGetUOMCodeFromInternationalCode(UOMCode); + end; + + EDocumentXMLHelper.SetNumberValueInField(LineXML, XmlNamespaces, 'InvoiceLine/TaxesOutputs/Tax/TaxRate', EDocumentPurchaseLine."VAT Rate"); + end; +#pragma warning restore AA0139 + + local procedure ParseSellerParty(FacturaEXML: XmlDocument; XmlNamespaces: XmlNamespaceManager; var EDocument: Record "E-Document"; var EDocumentPurchaseHeader: Record "E-Document Purchase Header") VendorNo: Code[20] + var + Vendor: Record Vendor; + ServiceParticipant: Record "Service Participant"; + EDocumentService: Record "E-Document Service"; + EDocumentHelper: Codeunit "E-Document Helper"; + VendorName, VendorAddress : Text; + VATRegistrationNo: Text[20]; + VendorId: Text; + XMLNode: XmlNode; + begin + // Extract VAT registration number + if FacturaEXML.SelectSingleNode('/namespace:Facturae/Parties/SellerParty/TaxIdentification/TaxIdentificationNumber', XmlNamespaces, XMLNode) then + VATRegistrationNo := CopyStr(XMLNode.AsXmlElement().InnerText(), 1, MaxStrLen(VATRegistrationNo)); + + // Try to find vendor by VAT registration number first + VendorNo := EDocumentImportHelper.FindVendor('', '', VATRegistrationNo); + + // If vendor not found, try to find by Service Participant + if VendorNo = '' then begin + if FacturaEXML.SelectSingleNode('/namespace:Facturae/Parties/SellerParty/PartyIdentification', XmlNamespaces, XMLNode) then + VendorId := XMLNode.AsXmlElement().InnerText(); + + if VendorId <> '' then begin + EDocumentHelper.GetEdocumentService(EDocument, EDocumentService); + ServiceParticipant.SetRange("Participant Type", ServiceParticipant."Participant Type"::Vendor); + ServiceParticipant.SetRange("Participant Identifier", VendorId); + ServiceParticipant.SetRange(Service, EDocumentService.Code); + if not ServiceParticipant.FindFirst() then begin + ServiceParticipant.SetRange(Service); + if ServiceParticipant.FindFirst() then; + end; + end; + + VendorNo := ServiceParticipant.Participant; + end; + + // If vendor still not found, try to find by name and address + if VendorNo = '' then begin + VendorName := GetNameDependingOnType(FacturaEXML, XmlNamespaces, '/namespace:Facturae/Parties/SellerParty/'); + VendorAddress := GetAddressDependingOnType(FacturaEXML, XmlNamespaces, '/namespace:Facturae/Parties/SellerParty/'); + VendorNo := EDocumentImportHelper.FindVendorByNameAndAddress(VendorName, VendorAddress); + EDocumentPurchaseHeader."Vendor Company Name" := CopyStr(VendorName, 1, MaxStrLen(EDocumentPurchaseHeader."Vendor Company Name")); + end; + + // Set vendor information in E-Document Purchase Header + Vendor := EDocumentImportHelper.GetVendor(EDocument, VendorNo); + if Vendor."No." <> '' then begin + EDocumentPurchaseHeader."Vendor Company Name" := Vendor.Name; + EDocumentPurchaseHeader."Vendor VAT Id" := VATRegistrationNo; + EDocumentPurchaseHeader."Vendor Address" := CopyStr(GetAddressDependingOnType(FacturaEXML, XmlNamespaces, '/namespace:Facturae/Parties/SellerParty/'), 1, MaxStrLen(EDocumentPurchaseHeader."Vendor Address")); + end else begin + // Set extracted information even if vendor not found + VendorName := GetNameDependingOnType(FacturaEXML, XmlNamespaces, '/namespace:Facturae/Parties/SellerParty/'); + if VendorName <> '' then + EDocumentPurchaseHeader."Vendor Company Name" := CopyStr(VendorName, 1, MaxStrLen(EDocumentPurchaseHeader."Vendor Company Name")); + EDocumentPurchaseHeader."Vendor VAT Id" := VATRegistrationNo; + EDocumentPurchaseHeader."Vendor Address" := CopyStr(GetAddressDependingOnType(FacturaEXML, XmlNamespaces, '/namespace:Facturae/Parties/SellerParty/'), 1, MaxStrLen(EDocumentPurchaseHeader."Vendor Address")); + end; + end; + + local procedure ParseBuyerParty(FacturaEXML: XmlDocument; XmlNamespaces: XmlNamespaceManager; var EDocumentPurchaseHeader: Record "E-Document Purchase Header") + var + BuyerName, BuyerAddress : Text; + XMLNode: XmlNode; + begin + BuyerName := GetNameDependingOnType(FacturaEXML, XmlNamespaces, '/namespace:Facturae/Parties/BuyerParty/'); + BuyerAddress := GetAddressDependingOnType(FacturaEXML, XmlNamespaces, '/namespace:Facturae/Parties/BuyerParty/'); + + EDocumentPurchaseHeader."Customer Company Name" := CopyStr(BuyerName, 1, MaxStrLen(EDocumentPurchaseHeader."Customer Company Name")); + EDocumentPurchaseHeader."Customer Address" := CopyStr(BuyerAddress, 1, MaxStrLen(EDocumentPurchaseHeader."Customer Address")); + + if FacturaEXML.SelectSingleNode('/namespace:Facturae/Parties/BuyerParty/TaxIdentification/TaxIdentificationNumber', XmlNamespaces, XMLNode) then + EDocumentPurchaseHeader."Customer VAT Id" := CopyStr(XMLNode.AsXmlElement().InnerText(), 1, MaxStrLen(EDocumentPurchaseHeader."Customer VAT Id")); + end; + + local procedure GetNameDependingOnType(FacturaEXML: XmlDocument; XmlNamespaces: XmlNamespaceManager; PathPrefix: Text) Name: Text + var + XMLNode: XmlNode; + PersonTypeCode: Text; + IndividualTok: Label 'F', Locked = true; + begin + if FacturaEXML.SelectSingleNode(PathPrefix + 'TaxIdentification/PersonTypeCode', XmlNamespaces, XMLNode) then + PersonTypeCode := XMLNode.AsXmlElement().InnerText(); + + if PersonTypeCode = IndividualTok then begin + if FacturaEXML.SelectSingleNode(PathPrefix + 'LegalEntity/Name', XmlNamespaces, XMLNode) then + Name := XMLNode.AsXmlElement().InnerText(); + if FacturaEXML.SelectSingleNode(PathPrefix + 'LegalEntity/FirstSurname', XmlNamespaces, XMLNode) then + Name += ' ' + XMLNode.AsXmlElement().InnerText(); + if FacturaEXML.SelectSingleNode(PathPrefix + 'LegalEntity/SecondSurname', XmlNamespaces, XMLNode) then + Name += ' ' + XMLNode.AsXmlElement().InnerText(); + end else + if FacturaEXML.SelectSingleNode(PathPrefix + 'LegalEntity/CorporateName', XmlNamespaces, XMLNode) then + Name := XMLNode.AsXmlElement().InnerText(); + end; + + local procedure GetAddressDependingOnType(FacturaEXML: XmlDocument; XmlNamespaces: XmlNamespaceManager; PathPrefix: Text) Address: Text + var + XMLNode: XmlNode; + ResidenceTypeCode: Text; + ResidenceTok: Label 'R', Locked = true; + begin + if FacturaEXML.SelectSingleNode(PathPrefix + 'TaxIdentification/ResidenceTypeCode', XmlNamespaces, XMLNode) then + ResidenceTypeCode := XMLNode.AsXmlElement().InnerText(); + + if ResidenceTypeCode = ResidenceTok then begin + if FacturaEXML.SelectSingleNode(PathPrefix + 'LegalEntity/AddressInSpain/Address', XmlNamespaces, XMLNode) then + Address := XMLNode.AsXmlElement().InnerText(); + end else + if FacturaEXML.SelectSingleNode(PathPrefix + 'LegalEntity/OverseasAddress/Address', XmlNamespaces, XMLNode) then + Address := XMLNode.AsXmlElement().InnerText(); + end; + + local procedure TryGetUOMCodeFromInternationalCode(ImportedUOMCode: Text) UOMCode: Code[10] + var + UnitOfMeasure: Record "Unit of Measure"; + Code: Integer; + begin + if ImportedUOMCode <> '' then begin + Evaluate(Code, ImportedUOMCode, 9); + UnitOfMeasure.SetRange("International Standard Code", Enum::"Factura-E Units of Measure".Names().Get(Code)); + if UnitOfMeasure.FindFirst() then + UOMCode := UnitOfMeasure.Code; + end; + 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/ES/EDocumentFormats/FacturaE/app/src/Core/FacturaEEDocReadintoDraft.EnumExt.al b/Apps/ES/EDocumentFormats/FacturaE/app/src/Core/FacturaEEDocReadintoDraft.EnumExt.al new file mode 100644 index 0000000000..0e5859cff6 --- /dev/null +++ b/Apps/ES/EDocumentFormats/FacturaE/app/src/Core/FacturaEEDocReadintoDraft.EnumExt.al @@ -0,0 +1,13 @@ +namespace Microsoft.eServices.EDocument.Format.FacturaE; + +using Microsoft.eServices.EDocument.Processing.Import; +using Microsoft.eServices.EDocument.Processing.Interfaces; + +enumextension 10773 "Factura-E EDoc Read into Draft" extends "E-Doc. Read into Draft" +{ + value(10773; "Factura-E") + { + Caption = 'Factura-E'; + Implementation = IStructuredFormatReader = "E-Document Factura-E Handler"; + } +} \ No newline at end of file diff --git a/Apps/ES/EDocumentFormats/FacturaE/test/.resources/factura-e/facturae-invoice-0.xml b/Apps/ES/EDocumentFormats/FacturaE/test/.resources/factura-e/facturae-invoice-0.xml new file mode 100644 index 0000000000..9d879559fb --- /dev/null +++ b/Apps/ES/EDocumentFormats/FacturaE/test/.resources/factura-e/facturae-invoice-0.xml @@ -0,0 +1,146 @@ + + + + 3.2.2 + I + EM + + 103033 + 1 + + 14140 + + + 14140 + + + 14140 + + XYZ + + + + + + J + R + GB123456789 + + + CRONUS International + +
Main Street, 14
+ B27 4KT + Birmingham + Birmingham + ESP +
+ + + JO@contoso.com + +
+
+ + + J + R + GB789456278 + + + The Cannon Group PLC + +
192 Market Square
+ B27 4KT + Birmingham + Birmingham + ESP +
+ + + mr.andy.teal@cannongroup.com + +
+
+
+ + + + 103033 + FC + OO + + + 2026-01-22 + XYZ + XYZ + es + + + + 01 + 25 + + 14000 + + + 1000 + + + + + 14000 + 14000 + 1000 + 0 + 14140 + 14140 + 14140 + + + + Bicycle + 1 + 01 + 4000.00 + 4000 + 4000 + + + 01 + 25 + + 4000 + + + 1000 + + + + 1000 + + + Bicycle v2 + 2 + 01 + 5000.00 + 10000 + 10000 + + + 01 + 25 + + 10000 + + + 0 + + + + 2000 + + + + +
\ No newline at end of file diff --git a/Apps/ES/EDocumentFormats/FacturaE/test/app.json b/Apps/ES/EDocumentFormats/FacturaE/test/app.json index 80abb55a9a..2b9f93435f 100644 --- a/Apps/ES/EDocumentFormats/FacturaE/test/app.json +++ b/Apps/ES/EDocumentFormats/FacturaE/test/app.json @@ -51,7 +51,7 @@ "idRanges": [ { "from": 148001, - "to": 148002 + "to": 148005 } ], "resourceExposurePolicy": { @@ -59,5 +59,8 @@ "allowDownloadingSource": true, "includeSourceInSymbolFile": true }, - "application": "27.0.0.0" + "application": "27.0.0.0", + "resourceFolders": [ + ".resources" + ] } diff --git a/Apps/ES/EDocumentFormats/FacturaE/test/src/EDocFormatMock.Codeunit.al b/Apps/ES/EDocumentFormats/FacturaE/test/src/EDocFormatMock.Codeunit.al new file mode 100644 index 0000000000..1f9914b75c --- /dev/null +++ b/Apps/ES/EDocumentFormats/FacturaE/test/src/EDocFormatMock.Codeunit.al @@ -0,0 +1,54 @@ +codeunit 148004 "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/ES/EDocumentFormats/FacturaE/test/src/EDocFormatMock.EnumExt.al b/Apps/ES/EDocumentFormats/FacturaE/test/src/EDocFormatMock.EnumExt.al new file mode 100644 index 0000000000..21d54be88e --- /dev/null +++ b/Apps/ES/EDocumentFormats/FacturaE/test/src/EDocFormatMock.EnumExt.al @@ -0,0 +1,7 @@ +enumextension 148001 "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/ES/EDocumentFormats/FacturaE/test/src/EDocumentStructuredTests.Codeunit.al b/Apps/ES/EDocumentFormats/FacturaE/test/src/EDocumentStructuredTests.Codeunit.al new file mode 100644 index 0000000000..2e78e219e9 --- /dev/null +++ b/Apps/ES/EDocumentFormats/FacturaE/test/src/EDocumentStructuredTests.Codeunit.al @@ -0,0 +1,274 @@ +codeunit 148002 "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"; + FacturaEStructValidations: Codeunit "Factura-E 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 'factura-e/facturae-invoice-0.xml', Locked = true; + MockCurrencyCode: Code[10]; + MockDate: Date; + + #region FacturaE 3.0 XML + [Test] + procedure TestFacturaEInvoice_ValidDocument() + var + EDocument: Record "E-Document"; + begin + // [FEATURE] [E-Document] [FacturaE] [Import] + // [SCENARIO] Import and validate a valid FacturaE invoice document + + // [GIVEN] A valid FacturaE XML invoice document + Initialize(Enum::"Service Integration"::"No Integration"); + SetupFacturaEEDocumentService(); + 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 + FacturaEStructValidations.SetMockCurrencyCode(MockCurrencyCode); + FacturaEStructValidations.SetMockDate(MockDate); + + // [THEN] The document content is fully extracted and validated + FacturaEStructValidations.AssertFullEDocumentContentExtracted(EDocument."Entry No"); + end else + Assert.Fail(EDocumentStatusNotUpdatedErr); + end; + + [Test] + [HandlerFunctions('EDocumentPurchaseHeaderPageHandler')] + procedure TestFacturaEInvoice_ValidDocument_ViewExtractedData() + var + EDocument: Record "E-Document"; + EDocImport: Codeunit "E-Doc. Import"; + begin + // [FEATURE] [E-Document] [FacturaE] [View Data] + // [SCENARIO] View extracted data from a valid FacturaE invoice document + + // [GIVEN] A valid FacturaE XML invoice document is imported + Initialize(Enum::"Service Integration"::"No Integration"); + SetupFacturaEEDocumentService(); + 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 TestFacturaEInvoice_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] [FacturaE] [Purchase Invoice Creation] + // [SCENARIO] Create a purchase invoice from a valid FacturaE invoice document + + // [GIVEN] A valid FacturaE XML invoice document is imported + Initialize(Enum::"Service Integration"::"No Integration"); + Vendor."VAT Registration No." := 'GB123456789'; + Vendor.Modify(true); + + SetupFacturaEEDocumentService(); + 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 FacturaE data + FacturaEStructValidations.SetMockCurrencyCode(MockCurrencyCode); + FacturaEStructValidations.SetMockDate(MockDate); + FacturaEStructValidations.AssertPurchaseDocument(Vendor."No.", PurchaseHeader, DummyItem); + end; + + [Test] + procedure TestFacturaEInvoice_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] [FacturaE] [Draft Update] + // [SCENARIO] Update draft purchase document data and finalize processing + + // [GIVEN] A valid FacturaE XML invoice document is imported and processed to draft preparation + Initialize(Enum::"Service Integration"::"No Integration"); + Vendor."VAT Registration No." := 'GB123456789'; + Vendor.Modify(true); + SetupFacturaEEDocumentService(); + 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."Line Type".SetValue("Purchase Line Type"::Item); + 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 FacturaE data and manual updates + FacturaEStructValidations.SetMockCurrencyCode(MockCurrencyCode); + FacturaEStructValidations.SetMockDate(MockDate); + FacturaEStructValidations.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"::"Factura-E 3.2.2", Integration); + LibraryEDoc.SetupStandardPurchaseScenario(Vendor, EDocumentService, Enum::"E-Document Format"::"Factura-E 3.2.2", Integration); + EDocumentService."Import Process" := "E-Document Import Process"::"Version 2.0"; + EDocumentService."Read into Draft Impl." := "E-Doc. Read into Draft"::"Factura-E"; + EDocumentService.Modify(false); + 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 SetupFacturaEEDocumentService() + begin + EDocumentService."Read into Draft Impl." := "E-Doc. Read into Draft"::"Factura-E"; + 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/ES/EDocumentFormats/FacturaE/test/src/FacturaEStructValidations.Codeunit.al b/Apps/ES/EDocumentFormats/FacturaE/test/src/FacturaEStructValidations.Codeunit.al new file mode 100644 index 0000000000..b66fbc5a1d --- /dev/null +++ b/Apps/ES/EDocumentFormats/FacturaE/test/src/FacturaEStructValidations.Codeunit.al @@ -0,0 +1,102 @@ +codeunit 148005 "Factura-E Struct. Validations" +{ + var + Assert: Codeunit Assert; + UnitOfMeasureCodeTok: Label '01', Locked = true; + SalesInvoiceNoTok: Label '103033', 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(), '103033', EDocumentPurchaseHeader."Sales Invoice No.")); + Assert.AreEqual(MockDate, EDocumentPurchaseHeader."Document Date", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Document Date"), EDocumentPurchaseHeader.TableCaption(), DMY2Date(22, 1, 2026), EDocumentPurchaseHeader."Document Date")); + Assert.AreEqual(MockCurrencyCode, EDocumentPurchaseHeader."Currency Code", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Currency Code"), EDocumentPurchaseHeader.TableCaption(), 'XYZ', EDocumentPurchaseHeader."Currency Code")); + Assert.AreEqual(14000, EDocumentPurchaseHeader."Sub Total", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Sub Total"), EDocumentPurchaseHeader.TableCaption(), 14000, EDocumentPurchaseHeader."Sub Total")); + Assert.AreEqual(1000, EDocumentPurchaseHeader."Total VAT", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Total VAT"), EDocumentPurchaseHeader.TableCaption(), 1000, EDocumentPurchaseHeader."Total VAT")); + Assert.AreEqual(14140, EDocumentPurchaseHeader.Total, StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption(Total), EDocumentPurchaseHeader.TableCaption(), 14140, EDocumentPurchaseHeader.Total)); + Assert.AreEqual(14140, EDocumentPurchaseHeader."Amount Due", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseHeader.FieldCaption("Amount Due"), EDocumentPurchaseHeader.TableCaption(), 14140, EDocumentPurchaseHeader."Amount Due")); + 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('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(), '01', EDocumentPurchaseLine."Unit of Measure")); + Assert.AreEqual(4000, EDocumentPurchaseLine."Sub Total", StrSubstNo(MockDataMismatchErr, EDocumentPurchaseLine.FieldCaption("Sub Total"), EDocumentPurchaseLine.TableCaption(), 4000, EDocumentPurchaseLine."Sub Total")); + 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(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 'GL00000000', Locked = true; + Item2NoTok: Label 'GL00000001', 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(MockCurrencyCode, PurchaseHeader."Currency Code", StrSubstNo(MockDataMismatchErr, PurchaseHeader.FieldCaption("Currency Code"), PurchaseHeader.TableCaption(), MockCurrencyCode, PurchaseHeader."Currency Code")); + 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/ES/EDocumentFormats/FacturaE/test/src/LibraryEDocument.Codeunit.al b/Apps/ES/EDocumentFormats/FacturaE/test/src/LibraryEDocument.Codeunit.al new file mode 100644 index 0000000000..1e2a911d2a --- /dev/null +++ b/Apps/ES/EDocumentFormats/FacturaE/test/src/LibraryEDocument.Codeunit.al @@ -0,0 +1,1044 @@ +codeunit 148003 "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"; + UnitOfMeasureCode: Code[10]; + 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; + + UnitOfMeasureCode := '01'; + UnitOfMeasure.Init(); + UnitOfMeasure."International Standard Code" := UnitOfMeasureCode; + UnitOfMeasure.Code := UnitOfMeasureCode; + if UnitOfMeasure.Insert() then; + + CreateItemUnitOfMeasure(StandardItem."No.", UnitOfMeasure.Code); + LibraryItemReference.CreateItemReference(ItemReference, StandardItem."No.", '', UnitOfMeasure.Code, 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.", '', UnitOfMeasure.Code, Enum::"Item Reference Type"::Vendor, Vendor."No.", '2000'); + 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"; + // EDocMappingTestRec: Record "E-Doc. Mapping Test Rec"; + 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(); + // EDocMappingTestRec.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 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/W1/EDocument/app/app.json b/Apps/W1/EDocument/app/app.json index 69bae72d52..d804e13b85 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": "8238f99b-cbe5-4b9c-b247-7771145b7470", + "name": "E-Document Format for Factura-E", + "publisher": "Microsoft" + }, + { + "id": "8238f78b-cbe5-4b9c-b247-7771145b7470", + "name": "E-Document Format for Factura-E Tests", + "publisher": "Microsoft" } ], "screenshots": [],