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": [],