From 5a92fa7819fd71fc4cd079013cb222650c2c96ce Mon Sep 17 00:00:00 2001 From: Andrius Andrulevicius Date: Wed, 17 Jun 2026 16:58:41 +0300 Subject: [PATCH 1/2] Adds PEPPOL order response message handling Adds a message model for e-documents so order responses are tracked as lifecycle events instead of creating extra documents. Generates outgoing acknowledgements and accept/reject responses where supported, including a manual reject action for inbound orders. Improves usability and reliability with message visibility in the UI, payload viewing, cleanup of related message data, and new integration tests. --- .../App/src/Document/EDocument.Page.al | 19 +++ .../App/src/Document/EDocument.Table.al | 6 + .../App/src/Document/EDocuments.Page.al | 46 ++++++- .../Inbound/InboundEDocuments.Page.al | 8 ++ .../App/src/Processing/EDocImport.Codeunit.al | 20 ++- .../EDocumentProcessing.Codeunit.al | 17 +++ .../EDocumentSubscribers.Codeunit.al | 26 ++++ .../Import/EDocEmptyDraft.Codeunit.al | 10 ++ .../Import/EDocProcessDraft.Enum.al | 11 ++ .../Import/EDocUnspecifiedImpl.Codeunit.al | 10 ++ .../Import/ImportEDocumentProcess.Codeunit.al | 12 ++ .../EDocDataExchPurchHandler.Codeunit.al | 10 ++ .../EDocumentADIHandler.Codeunit.al | 10 ++ .../EDocumentMLLMHandler.Codeunit.al | 10 ++ .../EDocumentPEPPOLHandler.Codeunit.al | 63 ++++++++- .../IStructuredFormatReader.Interface.al | 19 ++- .../EDocMessageDraftHandler.Codeunit.al | 42 ++++++ .../Message/EDocMessageMgt.Codeunit.al | 96 +++++++++++++ .../Message/EDocMessageStatus.Enum.al | 22 +++ .../Message/EDocResponseTypeExt.EnumExt.al | 15 +++ .../Message/EDocumentMessage.Table.al | 85 ++++++++++++ .../EDocumentMessageTypeExt.EnumExt.al | 15 +++ .../Message/EDocumentMessagesPart.Page.al | 97 +++++++++++++ .../App/src/Service/EdocumentService.Page.al | 6 +- .../EDocMessageRespTests.Codeunit.al | 127 ++++++++++++++++++ .../src/Processing/EDocPDFMock.Codeunit.al | 9 ++ .../App/src/Response/EDocResponseType.Enum.al | 28 ++++ .../src/Response/EDocumentMessageType.Enum.al | 19 +++ .../PEPPOLOrderRespBuilder.Codeunit.al | 114 ++++++++++++++++ 29 files changed, 962 insertions(+), 10 deletions(-) create mode 100644 src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageDraftHandler.Codeunit.al create mode 100644 src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageMgt.Codeunit.al create mode 100644 src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageStatus.Enum.al create mode 100644 src/Apps/W1/EDocument/App/src/Processing/Message/EDocResponseTypeExt.EnumExt.al create mode 100644 src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessage.Table.al create mode 100644 src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessageTypeExt.EnumExt.al create mode 100644 src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessagesPart.Page.al create mode 100644 src/Apps/W1/EDocument/Test/src/Processing/EDocMessageRespTests.Codeunit.al create mode 100644 src/Apps/W1/PEPPOL/App/src/Response/EDocResponseType.Enum.al create mode 100644 src/Apps/W1/PEPPOL/App/src/Response/EDocumentMessageType.Enum.al create mode 100644 src/Apps/W1/PEPPOL/App/src/Response/PEPPOLOrderRespBuilder.Codeunit.al diff --git a/src/Apps/W1/EDocument/App/src/Document/EDocument.Page.al b/src/Apps/W1/EDocument/App/src/Document/EDocument.Page.al index 3e2f8ea79b..6ee41e8a0e 100644 --- a/src/Apps/W1/EDocument/App/src/Document/EDocument.Page.al +++ b/src/Apps/W1/EDocument/App/src/Document/EDocument.Page.al @@ -11,6 +11,7 @@ using Microsoft.eServices.EDocument.OrderMatch; using Microsoft.eServices.EDocument.OrderMatch.Copilot; using Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument.Processing.Import.Purchase; +using Microsoft.eServices.EDocument.Processing.Message; using Microsoft.eServices.EDocument.Service; using Microsoft.Foundation.Attachment; using System.Telemetry; @@ -220,6 +221,12 @@ page 6121 "E-Document" Enabled = Rec.Direction = Rec.Direction::Outgoing; Visible = Rec.Direction = Rec.Direction::Outgoing; } + part(EDocMessages; "E-Document Messages Part") + { + Caption = 'Messages'; + SubPageLink = "E-Document Entry No." = field("Entry No"); + ShowFilter = false; + } } } actions @@ -305,6 +312,18 @@ page 6121 "E-Document" end end; } + action(RejectOrder) + { + Caption = 'Reject Order'; + ToolTip = 'Sends a rejection response to the sender of this inbound order.'; + Image = Reject; + Visible = IsIncomingDoc; + + trigger OnAction() + begin + EDocumentHelper.SendOrderRejection(Rec); + end; + } action(ViewFile) { ApplicationArea = Basic, Suite; diff --git a/src/Apps/W1/EDocument/App/src/Document/EDocument.Table.al b/src/Apps/W1/EDocument/App/src/Document/EDocument.Table.al index 3b0390915b..26644b27f4 100644 --- a/src/Apps/W1/EDocument/App/src/Document/EDocument.Table.al +++ b/src/Apps/W1/EDocument/App/src/Document/EDocument.Table.al @@ -8,6 +8,7 @@ using Microsoft.eServices.EDocument.Integration; using Microsoft.eServices.EDocument.OrderMatch; using Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument.Processing.Import.Purchase; +using Microsoft.eServices.EDocument.Processing.Message; using Microsoft.eServices.EDocument.Processing.Interfaces; using Microsoft.Finance.Currency; using Microsoft.Foundation.Attachment; @@ -416,6 +417,7 @@ table 6121 "E-Document" EDocumentIntegrationLog: Record "E-Document Integration Log"; EDocumentLog: Record "E-Document Log"; EDocImportedLine: Record "E-Doc. Imported Line"; + EDocumentMessage: Record "E-Document Message"; EDocumentServiceStatus: Record "E-Document Service Status"; #if not CLEAN27 PurchaseHeader: Record "Purchase Header"; @@ -453,6 +455,10 @@ table 6121 "E-Document" if not EDocImportedLine.IsEmpty() then EDocImportedLine.DeleteAll(true); + EDocumentMessage.SetRange("E-Document Entry No.", Rec."Entry No"); + if not EDocumentMessage.IsEmpty() then + EDocumentMessage.DeleteAll(true); + #if not CLEAN27 // Version 1 processing cleanup // Can be removed soon as version 1 is fully migrated to version 2 diff --git a/src/Apps/W1/EDocument/App/src/Document/EDocuments.Page.al b/src/Apps/W1/EDocument/App/src/Document/EDocuments.Page.al index 1fa1018045..3e22108858 100644 --- a/src/Apps/W1/EDocument/App/src/Document/EDocuments.Page.al +++ b/src/Apps/W1/EDocument/App/src/Document/EDocuments.Page.al @@ -4,6 +4,10 @@ // ------------------------------------------------------------------------------------------------ namespace Microsoft.eServices.EDocument; +using Microsoft.eServices.EDocument.Processing.Message; +using Microsoft.eServices.EDocument.Service; +using Microsoft.Foundation.Attachment; + page 6122 "E-Documents" { ApplicationArea = Basic, Suite; @@ -61,6 +65,39 @@ page 6122 "E-Documents" } } } + area(FactBoxes) + { + part("Attached Documents List"; "Doc. Attachment List Factbox") + { + ApplicationArea = All; + Caption = 'Documents'; + UpdatePropagation = Both; + SubPageLink = "E-Document Entry No." = field("Entry No"), + "E-Document Attachment" = const(true); + } + part(InboundEDocFactbox; "Inbound E-Doc. Factbox") + { + Caption = 'Details'; + SubPageLink = "E-Document Entry No" = field("Entry No"); + ShowFilter = false; + Enabled = Rec.Direction = Rec.Direction::Incoming; + Visible = Rec.Direction = Rec.Direction::Incoming; + } + part("Outbound E-Doc. Factbox"; "Outbound E-Doc. Factbox") + { + Caption = 'Details'; + SubPageLink = "E-Document Entry No" = field("Entry No"); + ShowFilter = false; + Enabled = Rec.Direction = Rec.Direction::Outgoing; + Visible = Rec.Direction = Rec.Direction::Outgoing; + } + part(EDocMessages; "E-Document Messages Part") + { + Caption = 'Messages'; + SubPageLink = "E-Document Entry No." = field("Entry No"); + ShowFilter = false; + } + } } actions { @@ -138,9 +175,14 @@ page 6122 "E-Documents" EDocImport: Codeunit "E-Doc. Import"; begin EDocImport.UploadDocument(EDocument); - if EDocument."Entry No" <> 0 then begin - EDocImport.ProcessIncomingEDocument(EDocument, EDocument.GetEDocumentService().GetDefaultImportParameters()); + if EDocument."Entry No" = 0 then + exit; + if EDocument.Direction = EDocument.Direction::Outgoing then begin + // File was classified as a message (e.g. OrderResponse) linked to this outbound document. Page.Run(Page::"E-Document", EDocument); + exit; end; + EDocImport.ProcessIncomingEDocument(EDocument, EDocument.GetEDocumentService().GetDefaultImportParameters()); + Page.Run(Page::"E-Document", EDocument); end; } diff --git a/src/Apps/W1/EDocument/App/src/Document/Inbound/InboundEDocuments.Page.al b/src/Apps/W1/EDocument/App/src/Document/Inbound/InboundEDocuments.Page.al index 1c35a24fb2..b1b537ea63 100644 --- a/src/Apps/W1/EDocument/App/src/Document/Inbound/InboundEDocuments.Page.al +++ b/src/Apps/W1/EDocument/App/src/Document/Inbound/InboundEDocuments.Page.al @@ -6,6 +6,8 @@ namespace Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument.Processing.Import.Purchase; +using Microsoft.eServices.EDocument.Processing.Message; +using Microsoft.EServices.EDocument.Processing.Import.Sales; using Microsoft.Foundation.Attachment; using Microsoft.Purchases.Vendor; using System.Agents; @@ -187,6 +189,12 @@ page 6105 "Inbound E-Documents" SubPageLink = "E-Document Entry No" = field("Entry No"); ShowFilter = false; } + part(EDocMessages; "E-Document Messages Part") + { + Caption = 'Messages'; + SubPageLink = "E-Document Entry No." = field("Entry No"); + ShowFilter = false; + } } } actions diff --git a/src/Apps/W1/EDocument/App/src/Processing/EDocImport.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/EDocImport.Codeunit.al index c2d3853933..b0ca178628 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/EDocImport.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/EDocImport.Codeunit.al @@ -196,11 +196,19 @@ codeunit 6140 "E-Doc. Import" InStr: InStream; FileName: Text; EDocumentServiceStatus: Enum "E-Document Service Status"; + Handled: Boolean; begin - if Page.RunModal(Page::"E-Document Services", EDocumentService) <> Action::LookupOK then + if not UploadIntoStream('', '', '', FileName, InStr) then exit; - if not UploadIntoStream('', '', '', FileName, InStr) then + OutStr := TempBlob.CreateOutStream(); + CopyStream(OutStr, InStr); + + OnAfterUploadFile(TempBlob, FileName, EDocument, Handled); + if Handled then + exit; + + if Page.RunModal(Page::"E-Document Services", EDocumentService) <> Action::LookupOK then exit; EDocument.Direction := EDocument.Direction::Incoming; @@ -208,9 +216,6 @@ codeunit 6140 "E-Doc. Import" EDocument.Service := EDocumentService.Code; EDocumentServiceStatus := "E-Document Service Status"::Imported; - OutStr := TempBlob.CreateOutStream(); - CopyStream(OutStr, InStr); - EDocument."File Name" := CopyStr(FileName, 1, 256); if EDocument."Entry No" = 0 then begin @@ -226,6 +231,11 @@ codeunit 6140 "E-Doc. Import" EDocument.Modify(); end; + [IntegrationEvent(false, false)] + local procedure OnAfterUploadFile(var TempBlob: Codeunit "Temp Blob"; FileName: Text; var EDocument: Record "E-Document"; var Handled: Boolean) + begin + end; + internal procedure V1_GetBasicInfo(var EDocument: Record "E-Document") var EDocService: Record "E-Document Service"; diff --git a/src/Apps/W1/EDocument/App/src/Processing/EDocumentProcessing.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/EDocumentProcessing.Codeunit.al index 759e43edc2..ada16e4a55 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/EDocumentProcessing.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/EDocumentProcessing.Codeunit.al @@ -6,6 +6,9 @@ namespace Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument.Processing.Import.Purchase; +using Microsoft.eServices.EDocument.Processing.Interfaces; +using Microsoft.eServices.EDocument.Processing.Message; +using Microsoft.Peppol.Response; using Microsoft.Finance.GeneralLedger.Journal; using Microsoft.Finance.GeneralLedger.Ledger; using Microsoft.Foundation.Reporting; @@ -744,6 +747,20 @@ codeunit 6108 "E-Document Processing" exit(false); end; + procedure SendOrderRejection(EDocument: Record "E-Document") + var + EDocMessageMgt: Codeunit "E-Doc. Message Mgt."; + IReader: Interface IStructuredFormatReader; + ResponseBlob: Codeunit "Temp Blob"; + begin + EDocument.TestField(Direction, EDocument.Direction::Incoming); + IReader := EDocument."Read into Draft Impl."; + if not IReader.SupportsOrderResponse(EDocument) then + exit; + IReader.BuildOrderResponse(EDocument, "E-Doc. Response Type"::Rejected, ResponseBlob); + EDocMessageMgt.CreateMessage(EDocument, "E-Document Message Type"::"PEPPOL Order Response", "E-Document Direction"::Outgoing, "E-Doc. Response Type"::Rejected, ResponseBlob); + end; + [IntegrationEvent(false, false)] local procedure OnAfterGetTypeFromSourceDocument(RecordVariant: Variant; var EDocumentType: Enum "E-Document Type") begin diff --git a/src/Apps/W1/EDocument/App/src/Processing/EDocumentSubscribers.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/EDocumentSubscribers.Codeunit.al index 650193f4dc..b6043e1e84 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/EDocumentSubscribers.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/EDocumentSubscribers.Codeunit.al @@ -10,6 +10,9 @@ using Microsoft.eServices.EDocument.OrderMatch; using Microsoft.EServices.EDocument.Processing; using Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument.Processing.Import.Purchase; +using Microsoft.eServices.EDocument.Processing.Interfaces; +using Microsoft.eServices.EDocument.Processing.Message; +using Microsoft.Peppol.Response; using Microsoft.eServices.EDocument.Service.Participant; using Microsoft.Finance.GeneralLedger.Journal; using Microsoft.Finance.GeneralLedger.Ledger; @@ -744,4 +747,27 @@ codeunit 6103 "E-Document Subscribers" local procedure OnAfterUpdateToPostedPurchaseEDocument(var EDocument: Record "E-Document"; PostedRecord: Variant; PostedDocumentNo: Code[20]; DocumentType: Enum "E-Document Type") begin end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Release Sales Document", 'OnAfterReleaseSalesDoc', '', false, false)] + local procedure OnAfterReleaseSalesDoc(var SalesHeader: Record "Sales Header"; PreviewMode: Boolean; var LinesWereModified: Boolean; SkipWhseRequestOperations: Boolean) + var + EDocument: Record "E-Document"; + EDocMessageMgt: Codeunit "E-Doc. Message Mgt."; + IReader: Interface IStructuredFormatReader; + ResponseBlob: Codeunit "Temp Blob"; + SalesHeaderRef: RecordRef; + begin + if PreviewMode then + exit; + SalesHeaderRef.GetTable(SalesHeader); + EDocument.SetRange("Document Record ID", SalesHeaderRef.RecordId); + EDocument.SetRange(Direction, EDocument.Direction::Incoming); + if not EDocument.FindLast() then + exit; + IReader := EDocument."Read into Draft Impl."; + if not IReader.SupportsOrderResponse(EDocument) then + exit; + IReader.BuildOrderResponse(EDocument, "E-Doc. Response Type"::Accepted, ResponseBlob); + EDocMessageMgt.CreateMessage(EDocument, "E-Document Message Type"::"PEPPOL Order Response", "E-Document Direction"::Outgoing, "E-Doc. Response Type"::Accepted, ResponseBlob); + end; } diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/EDocEmptyDraft.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/EDocEmptyDraft.Codeunit.al index fb845be46d..4d92e8179c 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/EDocEmptyDraft.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/EDocEmptyDraft.Codeunit.al @@ -7,6 +7,7 @@ namespace Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument.Processing.Interfaces; +using Microsoft.Peppol.Response; using System.Utilities; codeunit 6193 "E-Doc. Empty Draft" implements IStructureReceivedEDocument, IStructuredDataType, IStructuredFormatReader @@ -51,4 +52,13 @@ codeunit 6193 "E-Doc. Empty Draft" implements IStructureReceivedEDocument, IStru begin Error(NoDataErr); end; + + procedure SupportsOrderResponse(EDocument: Record "E-Document"): Boolean + begin + exit(false); + end; + + procedure BuildOrderResponse(EDocument: Record "E-Document"; ResponseType: Enum "E-Doc. Response Type"; var TempBlob: Codeunit "Temp Blob") + begin + end; } diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/EDocProcessDraft.Enum.al b/src/Apps/W1/EDocument/App/src/Processing/Import/EDocProcessDraft.Enum.al index 9b62c4ab3f..363c4e3f7e 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/EDocProcessDraft.Enum.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/EDocProcessDraft.Enum.al @@ -5,6 +5,7 @@ namespace Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument.Processing.Interfaces; +using Microsoft.eServices.EDocument.Processing.Message; /// /// Enum for E-Document Processing @@ -33,4 +34,14 @@ enum 6107 "E-Doc. Process Draft" implements IProcessStructuredData Caption = 'Purchase Credit Memo'; Implementation = IProcessStructuredData = "EDoc Prepare Cr. Memo Draft"; } + value(3; "Sales Order") + { + Caption = 'Sales Order'; + Implementation = IProcessStructuredData = "Prepare Sales E-Doc. Draft"; + } + value(4; "E-Document Message") + { + Caption = 'E-Document Message'; + Implementation = IProcessStructuredData = "E-Doc. Message Draft Handler"; + } } diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/EDocUnspecifiedImpl.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/EDocUnspecifiedImpl.Codeunit.al index a47c0c0896..a38f6fd0e6 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/EDocUnspecifiedImpl.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/EDocUnspecifiedImpl.Codeunit.al @@ -6,6 +6,7 @@ namespace Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Processing.Interfaces; +using Microsoft.Peppol.Response; using System.Utilities; /// @@ -46,6 +47,15 @@ codeunit 6116 "E-Doc. Unspecified Impl." implements IStructureReceivedEDocument, Error(EDocumentNoReadSpecifiedErr); end; + procedure SupportsOrderResponse(EDocument: Record "E-Document"): Boolean + begin + exit(false); + end; + + procedure BuildOrderResponse(EDocument: Record "E-Document"; ResponseType: Enum "E-Doc. Response Type"; var TempBlob: Codeunit "Temp Blob") + begin + end; + procedure FileExtension(): Text begin end; diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/ImportEDocumentProcess.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/ImportEDocumentProcess.Codeunit.al index f7ba514c45..4543c3a3c8 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/ImportEDocumentProcess.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/ImportEDocumentProcess.Codeunit.al @@ -7,6 +7,8 @@ namespace Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument.Processing.Interfaces; +using Microsoft.eServices.EDocument.Processing.Message; +using Microsoft.Peppol.Response; using Microsoft.Purchases.Vendor; using System.IO; using System.Utilities; @@ -118,7 +120,9 @@ codeunit 6104 "Import E-Document Process" local procedure ReadIntoDraft(EDocument: Record "E-Document") var EDocumentDataStorage: Record "E-Doc. Data Storage"; + EDocMessageMgt: Codeunit "E-Doc. Message Mgt."; FromBlob: Codeunit "Temp Blob"; + ResponseBlob: Codeunit "Temp Blob"; IStructuredFormatReader: Interface IStructuredFormatReader; begin if EDocumentDataStorage.Get(EDocument."Structured Data Entry No.") then @@ -131,6 +135,11 @@ codeunit 6104 "Import E-Document Process" EDocument."Process Draft Impl." := IStructuredFormatReader.ReadIntoDraft(EDocument, FromBlob); EDocument.Modify(); + + if IStructuredFormatReader.SupportsOrderResponse(EDocument) then begin + IStructuredFormatReader.BuildOrderResponse(EDocument, "E-Doc. Response Type"::Acknowledged, ResponseBlob); + EDocMessageMgt.CreateMessage(EDocument, "E-Document Message Type"::"PEPPOL Order Response", "E-Document Direction"::Outgoing, "E-Doc. Response Type"::Acknowledged, ResponseBlob); + end; end; local procedure PrepareDraft(EDocument: Record "E-Document"; EDocImportParameters: Record "E-Doc. Import Parameters") @@ -164,6 +173,9 @@ codeunit 6104 "Import E-Document Process" var IEDocumentFinishDraft: Interface IEDocumentFinishDraft; begin + if EDocument."Document Type" = "E-Document Type"::None then + exit; + IEDocumentFinishDraft := EDocument."Document Type"; // Clean up / reset E-Document fields diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocDataExchPurchHandler.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocDataExchPurchHandler.Codeunit.al index 1d7866427f..416b7a34c0 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocDataExchPurchHandler.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocDataExchPurchHandler.Codeunit.al @@ -9,6 +9,7 @@ using Microsoft.eServices.EDocument.IO.Peppol; using Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument.Processing.Interfaces; using Microsoft.Finance.GeneralLedger.Setup; +using Microsoft.Peppol.Response; using Microsoft.Foundation.Attachment; using System.IO; using System.Text; @@ -35,6 +36,15 @@ codeunit 6407 "E-Doc. DataExch. Purch Handler" implements IStructuredFormatReade Error(ViewNotImplementedErr); end; + procedure SupportsOrderResponse(EDocument: Record "E-Document"): Boolean + begin + exit(false); + end; + + procedure BuildOrderResponse(EDocument: Record "E-Document"; ResponseType: Enum "E-Doc. Response Type"; var TempBlob: Codeunit "Temp Blob") + begin + end; + #region Auto-Detection /// diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al index ddc548d7f5..0891f75a64 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al @@ -9,6 +9,7 @@ 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.Peppol.Response; using System.AI; using System.AI.DocumentIntelligence; using System.Text; @@ -238,4 +239,13 @@ codeunit 6174 "E-Document ADI Handler" implements IStructureReceivedEDocument, I exit(-1); // Signal parse failure end; #pragma warning restore AA0139 + + procedure SupportsOrderResponse(EDocument: Record "E-Document"): Boolean + begin + exit(false); + end; + + procedure BuildOrderResponse(EDocument: Record "E-Document"; ResponseType: Enum "E-Doc. Response Type"; var TempBlob: Codeunit "Temp Blob") + begin + end; } diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentMLLMHandler.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentMLLMHandler.Codeunit.al index 15f83d9e68..2ee70b5060 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentMLLMHandler.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentMLLMHandler.Codeunit.al @@ -8,6 +8,7 @@ using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument.Processing.Interfaces; +using Microsoft.Peppol.Response; using System.AI; using System.Azure.KeyVault; using System.Telemetry; @@ -328,4 +329,13 @@ codeunit 6231 "E-Document MLLM Handler" implements IStructureReceivedEDocument, if not CopilotCapability.IsCapabilityRegistered(Enum::"Copilot Capability"::"E-Document MLLM Analysis") then CopilotCapability.RegisterCapability(Enum::"Copilot Capability"::"E-Document MLLM Analysis", ''); end; + + procedure SupportsOrderResponse(EDocument: Record "E-Document"): Boolean + begin + exit(false); + end; + + procedure BuildOrderResponse(EDocument: Record "E-Document"; ResponseType: Enum "E-Doc. Response Type"; var TempBlob: Codeunit "Temp Blob") + begin + end; } diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al index f7d82d018e..7aa8b524ef 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al @@ -8,6 +8,8 @@ using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument.Processing.Interfaces; +using Microsoft.eServices.EDocument.Processing.Message; +using Microsoft.Peppol.Response; using System.Utilities; /// @@ -26,6 +28,7 @@ codeunit 6173 "E-Document PEPPOL Handler" implements IStructuredFormatReader var PeppolUtility: Codeunit "E-Document PEPPOL Utility"; BillingReferenceEmptyTelemetryTxt: Label 'CreditNote BillingReference is empty - no originating invoice reference found.', Locked = true; + UnsupportedRootElementErr: Label 'Unsupported XML root element: %1. Only Invoice, CreditNote, Order, and OrderResponse are supported.', Comment = '%1 = XML root element name'; procedure ReadIntoDraft(EDocument: Record "E-Document"; TempBlob: Codeunit "Temp Blob"): Enum "E-Doc. Process Draft" var @@ -34,12 +37,16 @@ codeunit 6173 "E-Document PEPPOL Handler" implements IStructuredFormatReader PeppolXML: XmlDocument; XmlNamespaces: XmlNamespaceManager; RootElement: XmlElement; + OrderNamespaceLbl: Label 'urn:oasis:names:specification:ubl:schema:xsd:Order-2', Locked = true; + OrderResponseNamespaceLbl: Label 'urn:oasis:names:specification:ubl:schema:xsd:OrderResponse-2', Locked = true; begin EDocumentPurchaseHeader.InsertForEDocument(EDocument); TempBlob.CreateInStream(DocStream, TextEncoding::UTF8); XmlDocument.ReadFrom(DocStream, PeppolXML); PeppolUtility.InitializePEPPOL3Namespaces(XmlNamespaces); + XmlNamespaces.AddNamespace('order', OrderNamespaceLbl); + XmlNamespaces.AddNamespace('resp', OrderResponseNamespaceLbl); PeppolXML.GetRoot(RootElement); case UpperCase(RootElement.LocalName()) of @@ -65,7 +72,11 @@ codeunit 6173 "E-Document PEPPOL Handler" implements IStructuredFormatReader 'INVOICE': exit(Enum::"E-Doc. Process Draft"::"Purchase Invoice"); 'CREDITNOTE': - exit(Enum::"E-Doc. Process Draft"::"Purchase Credit Memo"); + exit(ProcessCreditNote(EDocument, PeppolXML, XmlNamespaces)); + 'ORDER': + exit(ProcessOrder(EDocument, PeppolXML, XmlNamespaces)); + 'ORDERRESPONSE': + exit(HandleInboundOrderResponse(EDocument, TempBlob, PeppolXML, XmlNamespaces)); else exit(Enum::"E-Doc. Process Draft"::"Purchase Invoice"); end; @@ -220,4 +231,54 @@ codeunit 6173 "E-Document PEPPOL Handler" implements IStructuredFormatReader Error('A view is not implemented for this handler.'); end; + procedure SupportsOrderResponse(EDocument: Record "E-Document"): Boolean + begin + exit(EDocument."Process Draft Impl." = "E-Doc. Process Draft"::"Sales Order"); + end; + + procedure BuildOrderResponse(EDocument: Record "E-Document"; ResponseType: Enum "E-Doc. Response Type"; var TempBlob: Codeunit "Temp Blob") + var + EDocSalesHeader: Record "E-Document Sales Header"; + Builder: Codeunit "PEPPOL Order Resp. Builder"; + begin + EDocSalesHeader.GetFromEDocument(EDocument); + Builder.Build(EDocSalesHeader."E-Document Entry No.", EDocSalesHeader."Buyer Order No.", EDocSalesHeader."Seller Company Name", EDocSalesHeader."Buyer Company Name", ResponseType, TempBlob); + end; + + local procedure HandleInboundOrderResponse(EDocument: Record "E-Document"; TempBlob: Codeunit "Temp Blob"; PeppolXML: XmlDocument; XmlNamespaces: XmlNamespaceManager): Enum "E-Doc. Process Draft" + var + OutboundEDocument: Record "E-Document"; + EDocMessageMgt: Codeunit "E-Doc. Message Mgt."; + ResponseCode: Text; + OrderRefId: Text; + ResponseType: Enum "E-Doc. Response Type"; + begin + PeppolUtility.TryGetStringValue(PeppolXML, XmlNamespaces, '/resp:OrderResponse/cbc:OrderResponseCode', ResponseCode); + PeppolUtility.TryGetStringValue(PeppolXML, XmlNamespaces, '/resp:OrderResponse/cac:OrderReference/cbc:ID', OrderRefId); + ResponseType := CodeToResponseType(ResponseCode); + + OutboundEDocument.SetRange("Document No.", CopyStr(OrderRefId, 1, MaxStrLen(OutboundEDocument."Document No."))); + OutboundEDocument.SetRange(Direction, OutboundEDocument.Direction::Outgoing); + if OutboundEDocument.FindLast() then + EDocMessageMgt.CreateMessage(OutboundEDocument, "E-Document Message Type"::"PEPPOL Order Response", "E-Document Direction"::Incoming, ResponseType, TempBlob) + else + EDocMessageMgt.CreateMessage(EDocument, "E-Document Message Type"::"PEPPOL Order Response", "E-Document Direction"::Incoming, ResponseType, TempBlob); + + exit("E-Doc. Process Draft"::"E-Document Message"); + end; + + local procedure CodeToResponseType(ResponseCode: Text): Enum "E-Doc. Response Type" + begin + case UpperCase(ResponseCode) of + 'AB': + exit("E-Doc. Response Type"::Acknowledged); + 'AC', 'AP': + exit("E-Doc. Response Type"::Accepted); + 'RE': + exit("E-Doc. Response Type"::Rejected); + else + exit("E-Doc. Response Type"::None); + end; + end; + } diff --git a/src/Apps/W1/EDocument/App/src/Processing/Interfaces/IStructuredFormatReader.Interface.al b/src/Apps/W1/EDocument/App/src/Processing/Interfaces/IStructuredFormatReader.Interface.al index 219c281c6f..853998272d 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Interfaces/IStructuredFormatReader.Interface.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Interfaces/IStructuredFormatReader.Interface.al @@ -6,6 +6,7 @@ namespace Microsoft.eServices.EDocument.Processing.Interfaces; using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Processing.Import; +using Microsoft.Peppol.Response; using System.Utilities; /// @@ -25,10 +26,26 @@ interface IStructuredFormatReader /// - /// Presents a view of the data + /// Presents a view of the data /// /// The E-Document record. /// The temporary blob that contains the data to read procedure View(EDocument: Record "E-Document"; TempBlob: Codeunit "Temp Blob"); + /// + /// Returns whether this format reader expects an order response to be sent back to the sender for the given E-Document. + /// Format apps that generate outbound response messages (e.g. PEPPOL Order Response) implement this to signal eligibility. + /// EDocument."Process Draft Impl." is already set when this is called, so implementations can inspect the draft type. + /// + /// The E-Document record with "Process Draft Impl." populated. + /// True if an order response should be generated; false otherwise. + procedure SupportsOrderResponse(EDocument: Record "E-Document"): Boolean; + + /// + /// Builds a format-specific order response XML blob into TempBlob. + /// Called by the framework after ReadIntoDraft (for AB) and after Sales Order release (for AC). + /// Implementations that return false from SupportsOrderResponse may leave TempBlob empty. + /// + procedure BuildOrderResponse(EDocument: Record "E-Document"; ResponseType: Enum "E-Doc. Response Type"; var TempBlob: Codeunit "Temp Blob"); + } \ No newline at end of file diff --git a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageDraftHandler.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageDraftHandler.Codeunit.al new file mode 100644 index 0000000000..d31619dd48 --- /dev/null +++ b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageDraftHandler.Codeunit.al @@ -0,0 +1,42 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.eServices.EDocument.Processing.Message; + +using Microsoft.eServices.EDocument; +using Microsoft.eServices.EDocument.Processing.Import; +using Microsoft.eServices.EDocument.Processing.Interfaces; +using Microsoft.Purchases.Vendor; + +/// +/// No-op IProcessStructuredData implementation for inbound E-Document messages (e.g. PEPPOL Order Response). +/// When ReadIntoDraft returns "E-Document Message", the pipeline calls PrepareDraft which returns None +/// so that FinishDraft exits early without creating a BC document. +/// +codeunit 50005 "E-Doc. Message Draft Handler" implements IProcessStructuredData +{ + Access = Internal; + InherentEntitlements = X; + InherentPermissions = X; + + procedure PrepareDraft(EDocument: Record "E-Document"; EDocImportParameters: Record "E-Doc. Import Parameters"): Enum "E-Document Type" + begin + exit("E-Document Type"::None); + end; + + procedure GetVendor(EDocument: Record "E-Document"; Customizations: Enum "E-Doc. Proc. Customizations"): Record Vendor + var + EmptyVendor: Record Vendor; + begin + exit(EmptyVendor); + end; + + procedure OpenDraftPage(var EDocument: Record "E-Document") + begin + end; + + procedure CleanUpDraft(EDocument: Record "E-Document") + begin + end; +} diff --git a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageMgt.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageMgt.Codeunit.al new file mode 100644 index 0000000000..573aac52bf --- /dev/null +++ b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageMgt.Codeunit.al @@ -0,0 +1,96 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.eServices.EDocument.Processing.Message; + +using Microsoft.eServices.EDocument; +using Microsoft.Peppol.Response; +using System.Utilities; + +/// +/// Public API for creating and reading E-Document messages. +/// Format apps call CreateMessage to store a response/message blob linked to an E-Document. +/// +codeunit 50003 "E-Doc. Message Mgt." +{ + Access = Public; + InherentEntitlements = X; + InherentPermissions = X; + + Permissions = + tabledata "E-Document Message" = rim, + tabledata "E-Doc. Data Storage" = rim; + + /// + /// Creates an E-Document message record and stores the XML payload blob. + /// Returns the Entry No. of the new message row. + /// + procedure CreateMessage(EDocument: Record "E-Document"; MessageType: Enum "E-Document Message Type"; var TempBlob: Codeunit "Temp Blob"): Integer + begin + exit(CreateMessage(EDocument, MessageType, "E-Document Direction"::Outgoing, "E-Doc. Response Type"::None, TempBlob)); + end; + + /// + /// Creates an E-Document message record with an explicit direction and stores the XML payload blob. + /// Returns the Entry No. of the new message row. + /// + procedure CreateMessage(EDocument: Record "E-Document"; MessageType: Enum "E-Document Message Type"; Direction: Enum "E-Document Direction"; var TempBlob: Codeunit "Temp Blob"): Integer + begin + exit(CreateMessage(EDocument, MessageType, Direction, "E-Doc. Response Type"::None, TempBlob)); + end; + + /// + /// Creates an E-Document message record with an explicit direction and response type, and stores the XML payload blob. + /// Returns the Entry No. of the new message row. + /// + procedure CreateMessage(EDocument: Record "E-Document"; MessageType: Enum "E-Document Message Type"; Direction: Enum "E-Document Direction"; ResponseType: Enum "E-Doc. Response Type"; var TempBlob: Codeunit "Temp Blob"): Integer + var + EDocMessage: Record "E-Document Message"; + DataStorageEntryNo: Integer; + begin + DataStorageEntryNo := InsertDataStorage(TempBlob); + + EDocMessage.Init(); + EDocMessage."E-Document Entry No." := EDocument."Entry No"; + EDocMessage."Message Type" := MessageType; + EDocMessage.Direction := Direction; + EDocMessage."Response Type" := ResponseType; + EDocMessage.Status := EDocMessage.Status::Created; + EDocMessage.Service := EDocument.Service; + EDocMessage."Data Storage Entry No." := DataStorageEntryNo; + EDocMessage."Created At" := CurrentDateTime(); + EDocMessage.Insert(); + exit(EDocMessage."Entry No."); + end; + + /// + /// Loads the payload blob for the given message into TempBlob. + /// + procedure GetMessageBlob(EDocMessage: Record "E-Document Message"; var TempBlob: Codeunit "Temp Blob") + var + EDocDataStorage: Record "E-Doc. Data Storage"; + begin + if not EDocDataStorage.Get(EDocMessage."Data Storage Entry No.") then + exit; + TempBlob := EDocDataStorage.GetTempBlob(); + end; + + local procedure InsertDataStorage(TempBlob: Codeunit "Temp Blob"): Integer + var + EDocDataStorage: Record "E-Doc. Data Storage"; + EDocRecRef: RecordRef; + begin + if not TempBlob.HasValue() then + exit(0); + + EDocDataStorage.Init(); + EDocDataStorage.Insert(); + EDocDataStorage.Name := ''; + EDocDataStorage."Data Storage Size" := TempBlob.Length(); + EDocRecRef.GetTable(EDocDataStorage); + TempBlob.ToRecordRef(EDocRecRef, EDocDataStorage.FieldNo("Data Storage")); + EDocRecRef.Modify(); + exit(EDocDataStorage."Entry No."); + end; +} diff --git a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageStatus.Enum.al b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageStatus.Enum.al new file mode 100644 index 0000000000..171c549cc3 --- /dev/null +++ b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageStatus.Enum.al @@ -0,0 +1,22 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.eServices.EDocument.Processing.Message; + +/// +/// Operational status of an E-Document message record. +/// +enum 50001 "E-Doc. Message Status" +{ + Extensible = true; + + value(0; Created) + { + Caption = 'Created'; + } + value(1; Sent) + { + Caption = 'Sent'; + } +} diff --git a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocResponseTypeExt.EnumExt.al b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocResponseTypeExt.EnumExt.al new file mode 100644 index 0000000000..2ac3165494 --- /dev/null +++ b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocResponseTypeExt.EnumExt.al @@ -0,0 +1,15 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.eServices.EDocument.Processing.Message; + +using Microsoft.Peppol.Response; + +enumextension 50005 "E-Doc. Response Type Core Ext" extends "E-Doc. Response Type" +{ + value(0; None) + { + Caption = 'None'; + } +} diff --git a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessage.Table.al b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessage.Table.al new file mode 100644 index 0000000000..4917e9bc17 --- /dev/null +++ b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessage.Table.al @@ -0,0 +1,85 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.eServices.EDocument.Processing.Message; + +using Microsoft.eServices.EDocument; +using Microsoft.Peppol.Response; + +/// +/// Stores a message (e.g. a PEPPOL Order Response) that relates to an existing E-Document. +/// A message does not produce a BC document; it updates the lifecycle state of the parent E-Document. +/// +table 50002 "E-Document Message" +{ + Access = Internal; + Caption = 'E-Document Message'; + ReplicateData = false; + InherentEntitlements = RIMDX; + InherentPermissions = RIMDX; + + fields + { + field(1; "Entry No."; Integer) + { + Caption = 'Entry No.'; + AutoIncrement = true; + DataClassification = SystemMetadata; + } + field(2; "E-Document Entry No."; Integer) + { + Caption = 'E-Document Entry No.'; + TableRelation = "E-Document"."Entry No"; + DataClassification = SystemMetadata; + } + field(3; "Message Type"; Enum "E-Document Message Type") + { + Caption = 'Message Type'; + DataClassification = SystemMetadata; + } + field(4; Direction; Enum "E-Document Direction") + { + Caption = 'Direction'; + DataClassification = SystemMetadata; + } + field(5; Status; Enum "E-Doc. Message Status") + { + Caption = 'Status'; + DataClassification = SystemMetadata; + } + field(6; "Data Storage Entry No."; Integer) + { + Caption = 'Data Storage Entry No.'; + TableRelation = "E-Doc. Data Storage"."Entry No."; + DataClassification = SystemMetadata; + } + field(7; "Created At"; DateTime) + { + Caption = 'Created At'; + DataClassification = SystemMetadata; + } + field(8; "Response Type"; Enum "E-Doc. Response Type") + { + Caption = 'Response Type'; + DataClassification = SystemMetadata; + } + field(9; Service; Code[20]) + { + Caption = 'Service'; + TableRelation = "E-Document Service"; + DataClassification = SystemMetadata; + } + } + + keys + { + key(PK; "Entry No.") + { + Clustered = true; + } + key(EDocument; "E-Document Entry No.") + { + } + } +} diff --git a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessageTypeExt.EnumExt.al b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessageTypeExt.EnumExt.al new file mode 100644 index 0000000000..244ed085b4 --- /dev/null +++ b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessageTypeExt.EnumExt.al @@ -0,0 +1,15 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.eServices.EDocument.Processing.Message; + +using Microsoft.Peppol.Response; + +enumextension 50000 "E-Doc. Message Type Core Ext" extends "E-Document Message Type" +{ + value(0; Unknown) + { + Caption = 'Unknown'; + } +} diff --git a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessagesPart.Page.al b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessagesPart.Page.al new file mode 100644 index 0000000000..a18e3e1d01 --- /dev/null +++ b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessagesPart.Page.al @@ -0,0 +1,97 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.eServices.EDocument.Processing.Message; + +using System.Utilities; + +/// +/// Factbox listpart showing all messages related to an E-Document. +/// Surfaced on the E-Document card page via SubPageLink on "E-Document Entry No.". +/// +page 50004 "E-Document Messages Part" +{ + ApplicationArea = Basic, Suite; + Caption = 'Messages'; + PageType = ListPart; + SourceTable = "E-Document Message"; + Editable = false; + InsertAllowed = false; + DeleteAllowed = false; + ModifyAllowed = false; + + layout + { + area(content) + { + repeater(Messages) + { + field("Message Type"; Rec."Message Type") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the type of the message, for example PEPPOL Order Response.'; + } + field(Direction; Rec.Direction) + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies whether the message was sent (Outgoing) or received (Incoming).'; + } + field(Status; Rec.Status) + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies the current status of the message.'; + } + field("Created At"; Rec."Created At") + { + ApplicationArea = Basic, Suite; + ToolTip = 'Specifies when the message was created.'; + } + } + } + } + + actions + { + area(processing) + { + action(ViewXML) + { + ApplicationArea = Basic, Suite; + Caption = 'View XML'; + ToolTip = 'Download the raw XML payload of this message.'; + Image = XMLFile; + Scope = Repeater; + + trigger OnAction() + var + EDocMessageMgt: Codeunit "E-Doc. Message Mgt."; + TempBlob: Codeunit "Temp Blob"; + InStr: InStream; + FileName: Text; + begin + EDocMessageMgt.GetMessageBlob(Rec, TempBlob); + if not TempBlob.HasValue() then + exit; + TempBlob.CreateInStream(InStr); + FileName := BuildFileName(); + DownloadFromStream(InStr, '', '', '', FileName); + end; + } + } + } + + var + FileNameTok: Label 'E-Document_%1_Response_%2.xml', Comment = '%1 = E-Document number, %2 = human-readable response type', Locked = true; + + local procedure BuildFileName(): Text + var + ResponseTypeText: Text; + begin + if Rec."Response Type" = Rec."Response Type"::None then + ResponseTypeText := Format(Rec."Message Type") + else + ResponseTypeText := Format(Rec."Response Type"); + exit(StrSubstNo(FileNameTok, Rec."E-Document Entry No.", ResponseTypeText)); + end; +} diff --git a/src/Apps/W1/EDocument/App/src/Service/EdocumentService.Page.al b/src/Apps/W1/EDocument/App/src/Service/EdocumentService.Page.al index ba607b422a..e41be31d8b 100644 --- a/src/Apps/W1/EDocument/App/src/Service/EdocumentService.Page.al +++ b/src/Apps/W1/EDocument/App/src/Service/EdocumentService.Page.al @@ -72,7 +72,11 @@ page 6133 "E-Document Service" field("Import Process"; Rec."Import Process") { ToolTip = 'Specifies the version of the import process to use for incoming e-documents.'; - Visible = false; + // Visible = false; + } + field("Read into Draft Impl.";"Read into Draft Impl.") + { + ToolTip = 'Specifies whether incoming purchase invoice e-documents are read into purchase invoice drafts.'; } group(PurchaseDraft) { diff --git a/src/Apps/W1/EDocument/Test/src/Processing/EDocMessageRespTests.Codeunit.al b/src/Apps/W1/EDocument/Test/src/Processing/EDocMessageRespTests.Codeunit.al new file mode 100644 index 0000000000..0daa318ed3 --- /dev/null +++ b/src/Apps/W1/EDocument/Test/src/Processing/EDocMessageRespTests.Codeunit.al @@ -0,0 +1,127 @@ +// ------------------------------------------------------------------------------------------------ +// 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.Test; + +using Microsoft.eServices.EDocument; +using Microsoft.eServices.EDocument.Processing.Message; +using Microsoft.Peppol.Response; +using Microsoft.eServices.EDocument.Integration; +using Microsoft.Sales.Customer; +using System.TestLibraries.Utilities; +using System.Utilities; + +codeunit 50200 "E-Doc. Message Response Tests" +{ + Subtype = Test; + TestType = IntegrationTest; + TestPermissions = Disabled; + + var + EDocumentService: Record "E-Document Service"; + Assert: Codeunit Assert; + LibraryEDoc: Codeunit "Library - E-Document"; + LibraryLowerPermission: Codeunit "Library - Lower Permissions"; + IsInitialized: Boolean; + + [Test] + procedure CreateAcknowledgedMessage() + var + EDocument: Record "E-Document"; + EDocMessage: Record "E-Document Message"; + EDocMessageMgt: Codeunit "E-Doc. Message Mgt."; + PEPPOLRespBuilder: Codeunit "PEPPOL Order Resp. Builder"; + TempBlob: Codeunit "Temp Blob"; + MessageEntryNo: Integer; + begin + Initialize(); + LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); + + PEPPOLRespBuilder.Build(EDocument."Entry No", 'PO-001', 'Seller Corp.', 'Buyer Inc.', "E-Doc. Response Type"::Acknowledged, TempBlob); + MessageEntryNo := EDocMessageMgt.CreateMessage(EDocument, "E-Document Message Type"::"PEPPOL Order Response", "E-Document Direction"::Incoming, "E-Doc. Response Type"::Acknowledged, TempBlob); + + EDocMessage.Get(MessageEntryNo); + Assert.AreEqual("E-Doc. Response Type"::Acknowledged, EDocMessage."Response Type", 'Response type must be Acknowledged.'); + Assert.AreEqual("E-Document Message Type"::"PEPPOL Order Response", EDocMessage."Message Type", 'Message type must be PEPPOL Order Response.'); + Assert.AreEqual("E-Document Direction"::Incoming, EDocMessage.Direction, 'Direction must be Incoming.'); + Assert.AreEqual(EDocument."Entry No", EDocMessage."E-Document Entry No.", 'E-Document entry no. must match.'); + Assert.AreEqual("E-Doc. Message Status"::Created, EDocMessage.Status, 'Status must be Created.'); + Assert.IsTrue(EDocMessage."Data Storage Entry No." > 0, 'Data storage entry must be set.'); + end; + + [Test] + procedure CreateAcceptedMessage() + var + EDocument: Record "E-Document"; + EDocMessage: Record "E-Document Message"; + EDocMessageMgt: Codeunit "E-Doc. Message Mgt."; + PEPPOLRespBuilder: Codeunit "PEPPOL Order Resp. Builder"; + TempBlob: Codeunit "Temp Blob"; + MessageEntryNo: Integer; + begin + Initialize(); + LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); + + PEPPOLRespBuilder.Build(EDocument."Entry No", 'PO-002', 'Seller Corp.', 'Buyer Inc.', "E-Doc. Response Type"::Accepted, TempBlob); + MessageEntryNo := EDocMessageMgt.CreateMessage(EDocument, "E-Document Message Type"::"PEPPOL Order Response", "E-Document Direction"::Incoming, "E-Doc. Response Type"::Accepted, TempBlob); + + EDocMessage.Get(MessageEntryNo); + Assert.AreEqual("E-Doc. Response Type"::Accepted, EDocMessage."Response Type", 'Response type must be Accepted.'); + Assert.AreEqual("E-Document Message Type"::"PEPPOL Order Response", EDocMessage."Message Type", 'Message type must be PEPPOL Order Response.'); + Assert.AreEqual("E-Document Direction"::Incoming, EDocMessage.Direction, 'Direction must be Incoming.'); + Assert.AreEqual(EDocument."Entry No", EDocMessage."E-Document Entry No.", 'E-Document entry no. must match.'); + Assert.AreEqual("E-Doc. Message Status"::Created, EDocMessage.Status, 'Status must be Created.'); + Assert.IsTrue(EDocMessage."Data Storage Entry No." > 0, 'Data storage entry must be set.'); + end; + + [Test] + procedure CreateRejectedMessage() + var + EDocument: Record "E-Document"; + EDocMessage: Record "E-Document Message"; + EDocMessageMgt: Codeunit "E-Doc. Message Mgt."; + PEPPOLRespBuilder: Codeunit "PEPPOL Order Resp. Builder"; + TempBlob: Codeunit "Temp Blob"; + MessageEntryNo: Integer; + begin + Initialize(); + LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); + + PEPPOLRespBuilder.Build(EDocument."Entry No", 'PO-003', 'Seller Corp.', 'Buyer Inc.', "E-Doc. Response Type"::Rejected, TempBlob); + MessageEntryNo := EDocMessageMgt.CreateMessage(EDocument, "E-Document Message Type"::"PEPPOL Order Response", "E-Document Direction"::Incoming, "E-Doc. Response Type"::Rejected, TempBlob); + + EDocMessage.Get(MessageEntryNo); + Assert.AreEqual("E-Doc. Response Type"::Rejected, EDocMessage."Response Type", 'Response type must be Rejected.'); + Assert.AreEqual("E-Document Message Type"::"PEPPOL Order Response", EDocMessage."Message Type", 'Message type must be PEPPOL Order Response.'); + Assert.AreEqual("E-Document Direction"::Incoming, EDocMessage.Direction, 'Direction must be Incoming.'); + Assert.AreEqual(EDocument."Entry No", EDocMessage."E-Document Entry No.", 'E-Document entry no. must match.'); + Assert.AreEqual("E-Doc. Message Status"::Created, EDocMessage.Status, 'Status must be Created.'); + Assert.IsTrue(EDocMessage."Data Storage Entry No." > 0, 'Data storage entry must be set.'); + end; + + local procedure Initialize() + var + EDocument: Record "E-Document"; + EDocumentServiceStatus: Record "E-Document Service Status"; + EDocMessage: Record "E-Document Message"; + EDocDataStorage: Record "E-Doc. Data Storage"; + Customer: Record Customer; + begin + LibraryLowerPermission.SetOutsideO365Scope(); + + EDocMessage.DeleteAll(); + EDocumentServiceStatus.DeleteAll(); + EDocument.DeleteAll(); + EDocDataStorage.DeleteAll(); + + if IsInitialized then + exit; + + LibraryEDoc.SetupStandardVAT(); + EDocumentService.DeleteAll(); + LibraryEDoc.SetupStandardSalesScenario(Customer, EDocumentService, Enum::"E-Document Format"::Mock, Enum::"Service Integration"::"Mock"); + + IsInitialized := true; + end; +} diff --git a/src/Apps/W1/EDocument/Test/src/Processing/EDocPDFMock.Codeunit.al b/src/Apps/W1/EDocument/Test/src/Processing/EDocPDFMock.Codeunit.al index 6c7376fd77..3125e98822 100644 --- a/src/Apps/W1/EDocument/Test/src/Processing/EDocPDFMock.Codeunit.al +++ b/src/Apps/W1/EDocument/Test/src/Processing/EDocPDFMock.Codeunit.al @@ -5,6 +5,7 @@ namespace Microsoft.eServices.EDocument.Test; using Microsoft.eServices.EDocument; +using Microsoft.Peppol.Response; using Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument.Processing.Interfaces; @@ -50,4 +51,12 @@ codeunit 139782 "E-Doc PDF Mock" implements IStructureReceivedEDocument, IStruct exit("E-Doc. Read into Draft"::Unspecified); end; + procedure SupportsOrderResponse(EDocument: Record "E-Document"): Boolean + begin + exit(false); + end; + + procedure BuildOrderResponse(EDocument: Record "E-Document"; ResponseType: Enum "E-Doc. Response Type"; var TempBlob: Codeunit "Temp Blob") + begin + end; } diff --git a/src/Apps/W1/PEPPOL/App/src/Response/EDocResponseType.Enum.al b/src/Apps/W1/PEPPOL/App/src/Response/EDocResponseType.Enum.al new file mode 100644 index 0000000000..eefe723b93 --- /dev/null +++ b/src/Apps/W1/PEPPOL/App/src/Response/EDocResponseType.Enum.al @@ -0,0 +1,28 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.Peppol.Response; + +/// +/// Human-readable response type carried by an E-Document message (e.g. a PEPPOL Order Response). +/// UNCL4343 OrderResponseCode: AB = Acknowledged, AC = Accepted, RE = Rejected. +/// Extend this enum in a format-specific app to declare additional response types. +/// +enum 50005 "E-Doc. Response Type" +{ + Extensible = true; + + value(50100; Acknowledged) + { + Caption = 'Acknowledged'; + } + value(50101; Accepted) + { + Caption = 'Accepted'; + } + value(50102; Rejected) + { + Caption = 'Rejected'; + } +} diff --git a/src/Apps/W1/PEPPOL/App/src/Response/EDocumentMessageType.Enum.al b/src/Apps/W1/PEPPOL/App/src/Response/EDocumentMessageType.Enum.al new file mode 100644 index 0000000000..cff5f96aeb --- /dev/null +++ b/src/Apps/W1/PEPPOL/App/src/Response/EDocumentMessageType.Enum.al @@ -0,0 +1,19 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.Peppol.Response; + +/// +/// Identifies the type of an E-Document message (e.g. a PEPPOL Order Response). +/// Extend this enum in a format-specific app to declare new message types. +/// +enum 50000 "E-Document Message Type" +{ + Extensible = true; + + value(50100; "PEPPOL Order Response") + { + Caption = 'PEPPOL Order Response'; + } +} diff --git a/src/Apps/W1/PEPPOL/App/src/Response/PEPPOLOrderRespBuilder.Codeunit.al b/src/Apps/W1/PEPPOL/App/src/Response/PEPPOLOrderRespBuilder.Codeunit.al new file mode 100644 index 0000000000..94b351c7f7 --- /dev/null +++ b/src/Apps/W1/PEPPOL/App/src/Response/PEPPOLOrderRespBuilder.Codeunit.al @@ -0,0 +1,114 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.Peppol.Response; + +using System.Utilities; +using System.Xml; + +/// +/// Builds a minimal, well-formed PEPPOL BIS 28 Order Response (code AB = Acknowledged) +/// for an inbound Sales Order that was read into draft. +/// +codeunit 50102 "PEPPOL Order Resp. Builder" +{ + Access = Public; + InherentEntitlements = X; + InherentPermissions = X; + + /// + /// Generates a minimal UBL OrderResponse XML blob using primitive parameters. + /// Writes the result into TempBlob. + /// + procedure Build(EDocEntryNo: Integer; BuyerOrderNo: Code[20]; SellerName: Text[100]; BuyerName: Text[100]; ResponseType: Enum "E-Doc. Response Type"; var TempBlob: Codeunit "Temp Blob") + var + XmlDoc: XmlDocument; + RootNode: XmlElement; + OutStr: OutStream; + begin + XmlDoc := XmlDocument.Create(); + XmlDoc.SetDeclaration(XmlDeclaration.Create('1.0', 'UTF-8', 'no')); + + RootNode := BuildOrderResponse(EDocEntryNo, BuyerOrderNo, SellerName, BuyerName, ResponseTypeToCode(ResponseType)); + XmlDoc.Add(RootNode); + + TempBlob.CreateOutStream(OutStr, TextEncoding::UTF8); + XmlDoc.WriteTo(OutStr); + end; + + /// + /// Maps a response type to its UNCL4343 OrderResponseCode used on the wire. + /// + local procedure ResponseTypeToCode(ResponseType: Enum "E-Doc. Response Type"): Code[10] + begin + case ResponseType of + "E-Doc. Response Type"::Acknowledged: + exit('AB'); + "E-Doc. Response Type"::Accepted: + exit('AC'); + "E-Doc. Response Type"::Rejected: + exit('RE'); + end; + end; + + local procedure BuildOrderResponse(EDocEntryNo: Integer; BuyerOrderNo: Code[20]; SellerName: Text[100]; BuyerName: Text[100]; ResponseCode: Code[10]) RootNode: XmlElement + var + OrderRefId: Text; + begin + RootNode := XmlElement.Create('OrderResponse', 'urn:oasis:names:specification:ubl:schema:xsd:OrderResponse-2'); + RootNode.Add(XmlAttribute.CreateNamespaceDeclaration('cac', 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2')); + RootNode.Add(XmlAttribute.CreateNamespaceDeclaration('cbc', 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2')); + + // BIS 28 customization / profile + RootNode.Add(XmlElement.Create('CustomizationID', 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2', + 'urn:fdc:peppol.eu:poacc:trns:order_response:3')); + RootNode.Add(XmlElement.Create('ProfileID', 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2', + 'urn:fdc:peppol.eu:poacc:bis:order_only:3')); + + RootNode.Add(XmlElement.Create('ID', 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2', + Format(EDocEntryNo))); + RootNode.Add(XmlElement.Create('IssueDate', 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2', + Format(Today(), 0, '--'))); + + RootNode.Add(XmlElement.Create('OrderResponseCode', 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2', ResponseCode)); + + // OrderReference + OrderRefId := BuyerOrderNo; + if OrderRefId = '' then + OrderRefId := Format(EDocEntryNo); + RootNode.Add(BuildOrderReference(OrderRefId)); + + // SellerSupplierParty + RootNode.Add(BuildParty('SellerSupplierParty', SellerName)); + + // BuyerCustomerParty + RootNode.Add(BuildParty('BuyerCustomerParty', BuyerName)); + end; + + local procedure BuildOrderReference(OrderId: Text) Node: XmlElement + var + IdNode: XmlElement; + begin + Node := XmlElement.Create('OrderReference', 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'); + IdNode := XmlElement.Create('ID', 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2', OrderId); + Node.Add(IdNode); + end; + + local procedure BuildParty(PartyRoleName: Text; PartyName: Text) RoleNode: XmlElement + var + PartyNode: XmlElement; + PartyNameNode: XmlElement; + NameNode: XmlElement; + begin + RoleNode := XmlElement.Create(PartyRoleName, 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'); + + PartyNode := XmlElement.Create('Party', 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'); + PartyNameNode := XmlElement.Create('PartyName', 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'); + NameNode := XmlElement.Create('Name', 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2', PartyName); + + PartyNameNode.Add(NameNode); + PartyNode.Add(PartyNameNode); + RoleNode.Add(PartyNode); + end; +} From 5089f99c4736afff8799b69f0e28ae47be479828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Fri, 19 Jun 2026 10:25:29 +0300 Subject: [PATCH 2/2] Decouples order responses from format readers Introduces a dedicated response-building contract so draft-reading integrations remain stable while only eligible formats handle outbound responses. Updates response generation paths to use the new contract for acknowledgment, acceptance, and rejection, which removes redundant no-op implementations and clarifies responsibilities. Aligns message UI and APIs with the refactor, and reassigns related object IDs into reserved ranges to prevent conflicts. --- src/Apps/W1/EDocument/App/app.json | 4 +++ .../App/src/Document/EDocument.Page.al | 2 +- .../App/src/Document/EDocument.Table.al | 2 +- .../App/src/Document/EDocuments.Page.al | 2 +- .../Inbound/InboundEDocuments.Page.al | 4 +-- .../EDocumentProcessing.Codeunit.al | 10 +++--- .../EDocumentSubscribers.Codeunit.al | 10 +++--- .../Import/EDocEmptyDraft.Codeunit.al | 9 ----- .../Import/EDocReadIntoDraft.Enum.al | 8 +++-- .../Import/EDocUnspecifiedImpl.Codeunit.al | 2 +- .../Import/ImportEDocumentProcess.Codeunit.al | 6 ++-- .../EDocDataExchPurchHandler.Codeunit.al | 10 ------ .../EDocumentADIHandler.Codeunit.al | 9 ----- .../EDocumentMLLMHandler.Codeunit.al | 9 ----- .../EDocumentPEPPOLHandler.Codeunit.al | 4 +-- .../IOrderResponseBuilder.Interface.al | 34 +++++++++++++++++++ .../IStructuredFormatReader.Interface.al | 17 ---------- .../EDocMessageDraftHandler.Codeunit.al | 2 +- .../Message/EDocMessageMgt.Codeunit.al | 9 +++-- .../Message/EDocMessageStatus.Enum.al | 2 +- .../Message/EDocResponseTypeExt.EnumExt.al | 2 +- .../Message/EDocumentMessage.Table.al | 2 +- .../EDocumentMessageTypeExt.EnumExt.al | 2 +- ...ge.al => EDocumentMessagesFactBox.Page.al} | 4 +-- .../App/src/Service/EdocumentService.Page.al | 6 +--- ...l => EDocMessageResponseTests.Codeunit.al} | 5 ++- .../src/Processing/EDocPDFMock.Codeunit.al | 9 ----- .../App/src/Response/EDocResponseType.Enum.al | 8 ++--- .../src/Response/EDocumentMessageType.Enum.al | 4 +-- .../PEPPOLOrderRespBuilder.Codeunit.al | 3 +- 30 files changed, 87 insertions(+), 113 deletions(-) create mode 100644 src/Apps/W1/EDocument/App/src/Processing/Interfaces/IOrderResponseBuilder.Interface.al rename src/Apps/W1/EDocument/App/src/Processing/Message/{EDocumentMessagesPart.Page.al => EDocumentMessagesFactBox.Page.al} (96%) rename src/Apps/W1/EDocument/Test/src/Processing/{EDocMessageRespTests.Codeunit.al => EDocMessageResponseTests.Codeunit.al} (98%) diff --git a/src/Apps/W1/EDocument/App/app.json b/src/Apps/W1/EDocument/App/app.json index 3aa16d9bca..972589e59f 100644 --- a/src/Apps/W1/EDocument/App/app.json +++ b/src/Apps/W1/EDocument/App/app.json @@ -68,6 +68,10 @@ { "from": 6401, "to": 6410 + }, + { + "from": 6427, + "to": 6436 } ], "resourceExposurePolicy": { diff --git a/src/Apps/W1/EDocument/App/src/Document/EDocument.Page.al b/src/Apps/W1/EDocument/App/src/Document/EDocument.Page.al index 6ee41e8a0e..1f97b1eb0f 100644 --- a/src/Apps/W1/EDocument/App/src/Document/EDocument.Page.al +++ b/src/Apps/W1/EDocument/App/src/Document/EDocument.Page.al @@ -221,7 +221,7 @@ page 6121 "E-Document" Enabled = Rec.Direction = Rec.Direction::Outgoing; Visible = Rec.Direction = Rec.Direction::Outgoing; } - part(EDocMessages; "E-Document Messages Part") + part(EDocMessages; "E-Document Messages FactBox") { Caption = 'Messages'; SubPageLink = "E-Document Entry No." = field("Entry No"); diff --git a/src/Apps/W1/EDocument/App/src/Document/EDocument.Table.al b/src/Apps/W1/EDocument/App/src/Document/EDocument.Table.al index 26644b27f4..c3d3b01a96 100644 --- a/src/Apps/W1/EDocument/App/src/Document/EDocument.Table.al +++ b/src/Apps/W1/EDocument/App/src/Document/EDocument.Table.al @@ -8,8 +8,8 @@ using Microsoft.eServices.EDocument.Integration; using Microsoft.eServices.EDocument.OrderMatch; using Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument.Processing.Import.Purchase; -using Microsoft.eServices.EDocument.Processing.Message; using Microsoft.eServices.EDocument.Processing.Interfaces; +using Microsoft.eServices.EDocument.Processing.Message; using Microsoft.Finance.Currency; using Microsoft.Foundation.Attachment; using Microsoft.Foundation.Reporting; diff --git a/src/Apps/W1/EDocument/App/src/Document/EDocuments.Page.al b/src/Apps/W1/EDocument/App/src/Document/EDocuments.Page.al index 3e22108858..e0876718ef 100644 --- a/src/Apps/W1/EDocument/App/src/Document/EDocuments.Page.al +++ b/src/Apps/W1/EDocument/App/src/Document/EDocuments.Page.al @@ -91,7 +91,7 @@ page 6122 "E-Documents" Enabled = Rec.Direction = Rec.Direction::Outgoing; Visible = Rec.Direction = Rec.Direction::Outgoing; } - part(EDocMessages; "E-Document Messages Part") + part(EDocMessages; "E-Document Messages FactBox") { Caption = 'Messages'; SubPageLink = "E-Document Entry No." = field("Entry No"); diff --git a/src/Apps/W1/EDocument/App/src/Document/Inbound/InboundEDocuments.Page.al b/src/Apps/W1/EDocument/App/src/Document/Inbound/InboundEDocuments.Page.al index b1b537ea63..79741a7e6a 100644 --- a/src/Apps/W1/EDocument/App/src/Document/Inbound/InboundEDocuments.Page.al +++ b/src/Apps/W1/EDocument/App/src/Document/Inbound/InboundEDocuments.Page.al @@ -6,8 +6,8 @@ namespace Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument.Processing.Import.Purchase; -using Microsoft.eServices.EDocument.Processing.Message; using Microsoft.EServices.EDocument.Processing.Import.Sales; +using Microsoft.eServices.EDocument.Processing.Message; using Microsoft.Foundation.Attachment; using Microsoft.Purchases.Vendor; using System.Agents; @@ -189,7 +189,7 @@ page 6105 "Inbound E-Documents" SubPageLink = "E-Document Entry No" = field("Entry No"); ShowFilter = false; } - part(EDocMessages; "E-Document Messages Part") + part(EDocMessages; "E-Document Messages FactBox") { Caption = 'Messages'; SubPageLink = "E-Document Entry No." = field("Entry No"); diff --git a/src/Apps/W1/EDocument/App/src/Processing/EDocumentProcessing.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/EDocumentProcessing.Codeunit.al index ada16e4a55..ac24fdb398 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/EDocumentProcessing.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/EDocumentProcessing.Codeunit.al @@ -8,12 +8,12 @@ using Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument.Processing.Interfaces; using Microsoft.eServices.EDocument.Processing.Message; -using Microsoft.Peppol.Response; using Microsoft.Finance.GeneralLedger.Journal; using Microsoft.Finance.GeneralLedger.Ledger; using Microsoft.Foundation.Reporting; using Microsoft.Inventory.Location; using Microsoft.Inventory.Transfer; +using Microsoft.Peppol.Response; using Microsoft.Purchases.Document; using Microsoft.Purchases.History; using Microsoft.Purchases.Vendor; @@ -750,14 +750,14 @@ codeunit 6108 "E-Document Processing" procedure SendOrderRejection(EDocument: Record "E-Document") var EDocMessageMgt: Codeunit "E-Doc. Message Mgt."; - IReader: Interface IStructuredFormatReader; ResponseBlob: Codeunit "Temp Blob"; + IResponseBuilder: Interface IOrderResponseBuilder; begin EDocument.TestField(Direction, EDocument.Direction::Incoming); - IReader := EDocument."Read into Draft Impl."; - if not IReader.SupportsOrderResponse(EDocument) then + IResponseBuilder := EDocument."Read into Draft Impl."; + if not IResponseBuilder.SupportsOrderResponse(EDocument) then exit; - IReader.BuildOrderResponse(EDocument, "E-Doc. Response Type"::Rejected, ResponseBlob); + IResponseBuilder.BuildOrderResponse(EDocument, "E-Doc. Response Type"::Rejected, ResponseBlob); EDocMessageMgt.CreateMessage(EDocument, "E-Document Message Type"::"PEPPOL Order Response", "E-Document Direction"::Outgoing, "E-Doc. Response Type"::Rejected, ResponseBlob); end; diff --git a/src/Apps/W1/EDocument/App/src/Processing/EDocumentSubscribers.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/EDocumentSubscribers.Codeunit.al index b6043e1e84..513436f979 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/EDocumentSubscribers.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/EDocumentSubscribers.Codeunit.al @@ -12,7 +12,6 @@ using Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument.Processing.Interfaces; using Microsoft.eServices.EDocument.Processing.Message; -using Microsoft.Peppol.Response; using Microsoft.eServices.EDocument.Service.Participant; using Microsoft.Finance.GeneralLedger.Journal; using Microsoft.Finance.GeneralLedger.Ledger; @@ -20,6 +19,7 @@ using Microsoft.Finance.GeneralLedger.Posting; using Microsoft.Finance.VAT.Setup; using Microsoft.Foundation.Reporting; using Microsoft.Inventory.Transfer; +using Microsoft.Peppol.Response; using Microsoft.Purchases.Document; using Microsoft.Purchases.History; using Microsoft.Purchases.Posting; @@ -753,9 +753,9 @@ codeunit 6103 "E-Document Subscribers" var EDocument: Record "E-Document"; EDocMessageMgt: Codeunit "E-Doc. Message Mgt."; - IReader: Interface IStructuredFormatReader; ResponseBlob: Codeunit "Temp Blob"; SalesHeaderRef: RecordRef; + IResponseBuilder: Interface IOrderResponseBuilder; begin if PreviewMode then exit; @@ -764,10 +764,10 @@ codeunit 6103 "E-Document Subscribers" EDocument.SetRange(Direction, EDocument.Direction::Incoming); if not EDocument.FindLast() then exit; - IReader := EDocument."Read into Draft Impl."; - if not IReader.SupportsOrderResponse(EDocument) then + IResponseBuilder := EDocument."Read into Draft Impl."; + if not IResponseBuilder.SupportsOrderResponse(EDocument) then exit; - IReader.BuildOrderResponse(EDocument, "E-Doc. Response Type"::Accepted, ResponseBlob); + IResponseBuilder.BuildOrderResponse(EDocument, "E-Doc. Response Type"::Accepted, ResponseBlob); EDocMessageMgt.CreateMessage(EDocument, "E-Document Message Type"::"PEPPOL Order Response", "E-Document Direction"::Outgoing, "E-Doc. Response Type"::Accepted, ResponseBlob); end; } diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/EDocEmptyDraft.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/EDocEmptyDraft.Codeunit.al index 4d92e8179c..ad08b5f3c4 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/EDocEmptyDraft.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/EDocEmptyDraft.Codeunit.al @@ -7,7 +7,6 @@ namespace Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument.Processing.Interfaces; -using Microsoft.Peppol.Response; using System.Utilities; codeunit 6193 "E-Doc. Empty Draft" implements IStructureReceivedEDocument, IStructuredDataType, IStructuredFormatReader @@ -53,12 +52,4 @@ codeunit 6193 "E-Doc. Empty Draft" implements IStructureReceivedEDocument, IStru Error(NoDataErr); end; - procedure SupportsOrderResponse(EDocument: Record "E-Document"): Boolean - begin - exit(false); - end; - - procedure BuildOrderResponse(EDocument: Record "E-Document"; ResponseType: Enum "E-Doc. Response Type"; var TempBlob: Codeunit "Temp Blob") - begin - end; } diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/EDocReadIntoDraft.Enum.al b/src/Apps/W1/EDocument/App/src/Processing/Import/EDocReadIntoDraft.Enum.al index ce558f9aff..a3ae458d46 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/EDocReadIntoDraft.Enum.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/EDocReadIntoDraft.Enum.al @@ -10,10 +10,11 @@ using Microsoft.eServices.EDocument.Processing.Interfaces; /// /// Specifies how a structured data type should be interpreted and read into a draft. /// -enum 6109 "E-Doc. Read into Draft" implements IStructuredFormatReader +enum 6109 "E-Doc. Read into Draft" implements IStructuredFormatReader, IOrderResponseBuilder { Extensible = true; - DefaultImplementation = IStructuredFormatReader = "E-Doc. Unspecified Impl."; + DefaultImplementation = IStructuredFormatReader = "E-Doc. Unspecified Impl.", + IOrderResponseBuilder = "E-Doc. Unspecified Impl."; value(0; "Unspecified") { @@ -33,7 +34,8 @@ enum 6109 "E-Doc. Read into Draft" implements IStructuredFormatReader value(3; "PEPPOL") { Caption = 'PEPPOL'; - Implementation = IStructuredFormatReader = "E-Document PEPPOL Handler"; + Implementation = IStructuredFormatReader = "E-Document PEPPOL Handler", + IOrderResponseBuilder = "E-Document PEPPOL Handler"; } value(4; "MLLM") { diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/EDocUnspecifiedImpl.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/EDocUnspecifiedImpl.Codeunit.al index a38f6fd0e6..849c2bf3c9 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/EDocUnspecifiedImpl.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/EDocUnspecifiedImpl.Codeunit.al @@ -12,7 +12,7 @@ using System.Utilities; /// /// Default implementations for E-Document interfaces. /// -codeunit 6116 "E-Doc. Unspecified Impl." implements IStructureReceivedEDocument, IEDocumentFinishDraft, IStructuredFormatReader, IEDocFileFormat +codeunit 6116 "E-Doc. Unspecified Impl." implements IStructureReceivedEDocument, IEDocumentFinishDraft, IStructuredFormatReader, IOrderResponseBuilder, IEDocFileFormat { Access = Internal; InherentEntitlements = X; diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/ImportEDocumentProcess.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/ImportEDocumentProcess.Codeunit.al index 4543c3a3c8..c836db8003 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/ImportEDocumentProcess.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/ImportEDocumentProcess.Codeunit.al @@ -124,6 +124,7 @@ codeunit 6104 "Import E-Document Process" FromBlob: Codeunit "Temp Blob"; ResponseBlob: Codeunit "Temp Blob"; IStructuredFormatReader: Interface IStructuredFormatReader; + IResponseBuilder: Interface IOrderResponseBuilder; begin if EDocumentDataStorage.Get(EDocument."Structured Data Entry No.") then FromBlob := EDocumentDataStorage.GetTempBlob(); @@ -136,8 +137,9 @@ codeunit 6104 "Import E-Document Process" EDocument."Process Draft Impl." := IStructuredFormatReader.ReadIntoDraft(EDocument, FromBlob); EDocument.Modify(); - if IStructuredFormatReader.SupportsOrderResponse(EDocument) then begin - IStructuredFormatReader.BuildOrderResponse(EDocument, "E-Doc. Response Type"::Acknowledged, ResponseBlob); + IResponseBuilder := EDocument."Read into Draft Impl."; + if IResponseBuilder.SupportsOrderResponse(EDocument) then begin + IResponseBuilder.BuildOrderResponse(EDocument, "E-Doc. Response Type"::Acknowledged, ResponseBlob); EDocMessageMgt.CreateMessage(EDocument, "E-Document Message Type"::"PEPPOL Order Response", "E-Document Direction"::Outgoing, "E-Doc. Response Type"::Acknowledged, ResponseBlob); end; end; diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocDataExchPurchHandler.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocDataExchPurchHandler.Codeunit.al index 416b7a34c0..1d7866427f 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocDataExchPurchHandler.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocDataExchPurchHandler.Codeunit.al @@ -9,7 +9,6 @@ using Microsoft.eServices.EDocument.IO.Peppol; using Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument.Processing.Interfaces; using Microsoft.Finance.GeneralLedger.Setup; -using Microsoft.Peppol.Response; using Microsoft.Foundation.Attachment; using System.IO; using System.Text; @@ -36,15 +35,6 @@ codeunit 6407 "E-Doc. DataExch. Purch Handler" implements IStructuredFormatReade Error(ViewNotImplementedErr); end; - procedure SupportsOrderResponse(EDocument: Record "E-Document"): Boolean - begin - exit(false); - end; - - procedure BuildOrderResponse(EDocument: Record "E-Document"; ResponseType: Enum "E-Doc. Response Type"; var TempBlob: Codeunit "Temp Blob") - begin - end; - #region Auto-Detection /// diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al index 0891f75a64..f9c5bbe74b 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al @@ -9,7 +9,6 @@ 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.Peppol.Response; using System.AI; using System.AI.DocumentIntelligence; using System.Text; @@ -240,12 +239,4 @@ codeunit 6174 "E-Document ADI Handler" implements IStructureReceivedEDocument, I end; #pragma warning restore AA0139 - procedure SupportsOrderResponse(EDocument: Record "E-Document"): Boolean - begin - exit(false); - end; - - procedure BuildOrderResponse(EDocument: Record "E-Document"; ResponseType: Enum "E-Doc. Response Type"; var TempBlob: Codeunit "Temp Blob") - begin - end; } diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentMLLMHandler.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentMLLMHandler.Codeunit.al index 2ee70b5060..43fd4c4f9d 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentMLLMHandler.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentMLLMHandler.Codeunit.al @@ -8,7 +8,6 @@ using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument.Processing.Interfaces; -using Microsoft.Peppol.Response; using System.AI; using System.Azure.KeyVault; using System.Telemetry; @@ -330,12 +329,4 @@ codeunit 6231 "E-Document MLLM Handler" implements IStructureReceivedEDocument, CopilotCapability.RegisterCapability(Enum::"Copilot Capability"::"E-Document MLLM Analysis", ''); end; - procedure SupportsOrderResponse(EDocument: Record "E-Document"): Boolean - begin - exit(false); - end; - - procedure BuildOrderResponse(EDocument: Record "E-Document"; ResponseType: Enum "E-Doc. Response Type"; var TempBlob: Codeunit "Temp Blob") - begin - end; } diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al index 7aa8b524ef..48ea3ad741 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentPEPPOLHandler.Codeunit.al @@ -19,7 +19,7 @@ using System.Utilities; /// Spec reference: https://docs.peppol.eu/poacc/billing/3.0/syntax/ubl-invoice/tree/ /// https://docs.peppol.eu/poacc/billing/3.0/syntax/ubl-creditnote/tree/ /// -codeunit 6173 "E-Document PEPPOL Handler" implements IStructuredFormatReader +codeunit 6173 "E-Document PEPPOL Handler" implements IStructuredFormatReader, IOrderResponseBuilder { Access = Internal; InherentEntitlements = X; @@ -28,7 +28,6 @@ codeunit 6173 "E-Document PEPPOL Handler" implements IStructuredFormatReader var PeppolUtility: Codeunit "E-Document PEPPOL Utility"; BillingReferenceEmptyTelemetryTxt: Label 'CreditNote BillingReference is empty - no originating invoice reference found.', Locked = true; - UnsupportedRootElementErr: Label 'Unsupported XML root element: %1. Only Invoice, CreditNote, Order, and OrderResponse are supported.', Comment = '%1 = XML root element name'; procedure ReadIntoDraft(EDocument: Record "E-Document"; TempBlob: Codeunit "Temp Blob"): Enum "E-Doc. Process Draft" var @@ -280,5 +279,4 @@ codeunit 6173 "E-Document PEPPOL Handler" implements IStructuredFormatReader exit("E-Doc. Response Type"::None); end; end; - } diff --git a/src/Apps/W1/EDocument/App/src/Processing/Interfaces/IOrderResponseBuilder.Interface.al b/src/Apps/W1/EDocument/App/src/Processing/Interfaces/IOrderResponseBuilder.Interface.al new file mode 100644 index 0000000000..d8d4791081 --- /dev/null +++ b/src/Apps/W1/EDocument/App/src/Processing/Interfaces/IOrderResponseBuilder.Interface.al @@ -0,0 +1,34 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.eServices.EDocument.Processing.Interfaces; + +using Microsoft.eServices.EDocument; +using Microsoft.Peppol.Response; +using System.Utilities; + +/// +/// Allows a format reader to build an outbound order response for a received e-document. +/// Separate from IStructuredFormatReader to avoid a breaking change on that interface. +/// +interface IOrderResponseBuilder +{ + + /// + /// Returns whether this format reader expects an order response to be sent back to the sender for the given E-Document. + /// Format apps that generate outbound response messages (e.g. PEPPOL Order Response) implement this to signal eligibility. + /// EDocument."Process Draft Impl." is already set when this is called, so implementations can inspect the draft type. + /// + /// The E-Document record with "Process Draft Impl." populated. + /// True if an order response should be generated; false otherwise. + procedure SupportsOrderResponse(EDocument: Record "E-Document"): Boolean; + + /// + /// Builds a format-specific order response XML blob into TempBlob. + /// Called by the framework after ReadIntoDraft (for AB) and after Sales Order release (for AC). + /// Implementations that return false from SupportsOrderResponse may leave TempBlob empty. + /// + procedure BuildOrderResponse(EDocument: Record "E-Document"; ResponseType: Enum "E-Doc. Response Type"; var TempBlob: Codeunit "Temp Blob"); + +} diff --git a/src/Apps/W1/EDocument/App/src/Processing/Interfaces/IStructuredFormatReader.Interface.al b/src/Apps/W1/EDocument/App/src/Processing/Interfaces/IStructuredFormatReader.Interface.al index 853998272d..fde42ac673 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Interfaces/IStructuredFormatReader.Interface.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Interfaces/IStructuredFormatReader.Interface.al @@ -6,7 +6,6 @@ namespace Microsoft.eServices.EDocument.Processing.Interfaces; using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Processing.Import; -using Microsoft.Peppol.Response; using System.Utilities; /// @@ -32,20 +31,4 @@ interface IStructuredFormatReader /// The temporary blob that contains the data to read procedure View(EDocument: Record "E-Document"; TempBlob: Codeunit "Temp Blob"); - /// - /// Returns whether this format reader expects an order response to be sent back to the sender for the given E-Document. - /// Format apps that generate outbound response messages (e.g. PEPPOL Order Response) implement this to signal eligibility. - /// EDocument."Process Draft Impl." is already set when this is called, so implementations can inspect the draft type. - /// - /// The E-Document record with "Process Draft Impl." populated. - /// True if an order response should be generated; false otherwise. - procedure SupportsOrderResponse(EDocument: Record "E-Document"): Boolean; - - /// - /// Builds a format-specific order response XML blob into TempBlob. - /// Called by the framework after ReadIntoDraft (for AB) and after Sales Order release (for AC). - /// Implementations that return false from SupportsOrderResponse may leave TempBlob empty. - /// - procedure BuildOrderResponse(EDocument: Record "E-Document"; ResponseType: Enum "E-Doc. Response Type"; var TempBlob: Codeunit "Temp Blob"); - } \ No newline at end of file diff --git a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageDraftHandler.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageDraftHandler.Codeunit.al index d31619dd48..17924dd4b2 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageDraftHandler.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageDraftHandler.Codeunit.al @@ -14,7 +14,7 @@ using Microsoft.Purchases.Vendor; /// When ReadIntoDraft returns "E-Document Message", the pipeline calls PrepareDraft which returns None /// so that FinishDraft exits early without creating a BC document. /// -codeunit 50005 "E-Doc. Message Draft Handler" implements IProcessStructuredData +codeunit 6435 "E-Doc. Message Draft Handler" implements IProcessStructuredData { Access = Internal; InherentEntitlements = X; diff --git a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageMgt.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageMgt.Codeunit.al index 573aac52bf..35e019958a 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageMgt.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageMgt.Codeunit.al @@ -12,7 +12,7 @@ using System.Utilities; /// Public API for creating and reading E-Document messages. /// Format apps call CreateMessage to store a response/message blob linked to an E-Document. /// -codeunit 50003 "E-Doc. Message Mgt." +codeunit 6433 "E-Doc. Message Mgt." { Access = Public; InherentEntitlements = X; @@ -65,12 +65,15 @@ codeunit 50003 "E-Doc. Message Mgt." end; /// - /// Loads the payload blob for the given message into TempBlob. + /// Loads the payload blob for the given message entry number into TempBlob. /// - procedure GetMessageBlob(EDocMessage: Record "E-Document Message"; var TempBlob: Codeunit "Temp Blob") + procedure GetMessageBlob(MessageEntryNo: Integer; var TempBlob: Codeunit "Temp Blob") var + EDocMessage: Record "E-Document Message"; EDocDataStorage: Record "E-Doc. Data Storage"; begin + if not EDocMessage.Get(MessageEntryNo) then + exit; if not EDocDataStorage.Get(EDocMessage."Data Storage Entry No.") then exit; TempBlob := EDocDataStorage.GetTempBlob(); diff --git a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageStatus.Enum.al b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageStatus.Enum.al index 171c549cc3..cd8140260d 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageStatus.Enum.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocMessageStatus.Enum.al @@ -7,7 +7,7 @@ namespace Microsoft.eServices.EDocument.Processing.Message; /// /// Operational status of an E-Document message record. /// -enum 50001 "E-Doc. Message Status" +enum 6431 "E-Doc. Message Status" { Extensible = true; diff --git a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocResponseTypeExt.EnumExt.al b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocResponseTypeExt.EnumExt.al index 2ac3165494..1d9ef286ad 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocResponseTypeExt.EnumExt.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocResponseTypeExt.EnumExt.al @@ -6,7 +6,7 @@ namespace Microsoft.eServices.EDocument.Processing.Message; using Microsoft.Peppol.Response; -enumextension 50005 "E-Doc. Response Type Core Ext" extends "E-Doc. Response Type" +enumextension 6410 "E-Doc. Response Type Core Ext" extends "E-Doc. Response Type" { value(0; None) { diff --git a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessage.Table.al b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessage.Table.al index 4917e9bc17..ce19e2939f 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessage.Table.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessage.Table.al @@ -11,7 +11,7 @@ using Microsoft.Peppol.Response; /// Stores a message (e.g. a PEPPOL Order Response) that relates to an existing E-Document. /// A message does not produce a BC document; it updates the lifecycle state of the parent E-Document. /// -table 50002 "E-Document Message" +table 6432 "E-Document Message" { Access = Internal; Caption = 'E-Document Message'; diff --git a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessageTypeExt.EnumExt.al b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessageTypeExt.EnumExt.al index 244ed085b4..d86c1a5995 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessageTypeExt.EnumExt.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessageTypeExt.EnumExt.al @@ -6,7 +6,7 @@ namespace Microsoft.eServices.EDocument.Processing.Message; using Microsoft.Peppol.Response; -enumextension 50000 "E-Doc. Message Type Core Ext" extends "E-Document Message Type" +enumextension 6430 "E-Doc. Message Type Core Ext" extends "E-Document Message Type" { value(0; Unknown) { diff --git a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessagesPart.Page.al b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessagesFactBox.Page.al similarity index 96% rename from src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessagesPart.Page.al rename to src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessagesFactBox.Page.al index a18e3e1d01..809410f873 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessagesPart.Page.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Message/EDocumentMessagesFactBox.Page.al @@ -10,7 +10,7 @@ using System.Utilities; /// Factbox listpart showing all messages related to an E-Document. /// Surfaced on the E-Document card page via SubPageLink on "E-Document Entry No.". /// -page 50004 "E-Document Messages Part" +page 6434 "E-Document Messages FactBox" { ApplicationArea = Basic, Suite; Caption = 'Messages'; @@ -70,7 +70,7 @@ page 50004 "E-Document Messages Part" InStr: InStream; FileName: Text; begin - EDocMessageMgt.GetMessageBlob(Rec, TempBlob); + EDocMessageMgt.GetMessageBlob(Rec."Entry No.", TempBlob); if not TempBlob.HasValue() then exit; TempBlob.CreateInStream(InStr); diff --git a/src/Apps/W1/EDocument/App/src/Service/EdocumentService.Page.al b/src/Apps/W1/EDocument/App/src/Service/EdocumentService.Page.al index e41be31d8b..ba607b422a 100644 --- a/src/Apps/W1/EDocument/App/src/Service/EdocumentService.Page.al +++ b/src/Apps/W1/EDocument/App/src/Service/EdocumentService.Page.al @@ -72,11 +72,7 @@ page 6133 "E-Document Service" field("Import Process"; Rec."Import Process") { ToolTip = 'Specifies the version of the import process to use for incoming e-documents.'; - // Visible = false; - } - field("Read into Draft Impl.";"Read into Draft Impl.") - { - ToolTip = 'Specifies whether incoming purchase invoice e-documents are read into purchase invoice drafts.'; + Visible = false; } group(PurchaseDraft) { diff --git a/src/Apps/W1/EDocument/Test/src/Processing/EDocMessageRespTests.Codeunit.al b/src/Apps/W1/EDocument/Test/src/Processing/EDocMessageResponseTests.Codeunit.al similarity index 98% rename from src/Apps/W1/EDocument/Test/src/Processing/EDocMessageRespTests.Codeunit.al rename to src/Apps/W1/EDocument/Test/src/Processing/EDocMessageResponseTests.Codeunit.al index 0daa318ed3..dea420da6d 100644 --- a/src/Apps/W1/EDocument/Test/src/Processing/EDocMessageRespTests.Codeunit.al +++ b/src/Apps/W1/EDocument/Test/src/Processing/EDocMessageResponseTests.Codeunit.al @@ -5,14 +5,13 @@ namespace Microsoft.eServices.EDocument.Test; using Microsoft.eServices.EDocument; +using Microsoft.eServices.EDocument.Integration; using Microsoft.eServices.EDocument.Processing.Message; using Microsoft.Peppol.Response; -using Microsoft.eServices.EDocument.Integration; using Microsoft.Sales.Customer; -using System.TestLibraries.Utilities; using System.Utilities; -codeunit 50200 "E-Doc. Message Response Tests" +codeunit 139898 "E-Doc. Message Response Tests" { Subtype = Test; TestType = IntegrationTest; diff --git a/src/Apps/W1/EDocument/Test/src/Processing/EDocPDFMock.Codeunit.al b/src/Apps/W1/EDocument/Test/src/Processing/EDocPDFMock.Codeunit.al index 3125e98822..6c7376fd77 100644 --- a/src/Apps/W1/EDocument/Test/src/Processing/EDocPDFMock.Codeunit.al +++ b/src/Apps/W1/EDocument/Test/src/Processing/EDocPDFMock.Codeunit.al @@ -5,7 +5,6 @@ namespace Microsoft.eServices.EDocument.Test; using Microsoft.eServices.EDocument; -using Microsoft.Peppol.Response; using Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument.Processing.Interfaces; @@ -51,12 +50,4 @@ codeunit 139782 "E-Doc PDF Mock" implements IStructureReceivedEDocument, IStruct exit("E-Doc. Read into Draft"::Unspecified); end; - procedure SupportsOrderResponse(EDocument: Record "E-Document"): Boolean - begin - exit(false); - end; - - procedure BuildOrderResponse(EDocument: Record "E-Document"; ResponseType: Enum "E-Doc. Response Type"; var TempBlob: Codeunit "Temp Blob") - begin - end; } diff --git a/src/Apps/W1/PEPPOL/App/src/Response/EDocResponseType.Enum.al b/src/Apps/W1/PEPPOL/App/src/Response/EDocResponseType.Enum.al index eefe723b93..38406ccee3 100644 --- a/src/Apps/W1/PEPPOL/App/src/Response/EDocResponseType.Enum.al +++ b/src/Apps/W1/PEPPOL/App/src/Response/EDocResponseType.Enum.al @@ -9,19 +9,19 @@ namespace Microsoft.Peppol.Response; /// UNCL4343 OrderResponseCode: AB = Acknowledged, AC = Accepted, RE = Rejected. /// Extend this enum in a format-specific app to declare additional response types. /// -enum 50005 "E-Doc. Response Type" +enum 37208 "E-Doc. Response Type" { Extensible = true; - value(50100; Acknowledged) + value(37223; Acknowledged) { Caption = 'Acknowledged'; } - value(50101; Accepted) + value(37224; Accepted) { Caption = 'Accepted'; } - value(50102; Rejected) + value(37225; Rejected) { Caption = 'Rejected'; } diff --git a/src/Apps/W1/PEPPOL/App/src/Response/EDocumentMessageType.Enum.al b/src/Apps/W1/PEPPOL/App/src/Response/EDocumentMessageType.Enum.al index cff5f96aeb..ed406f05ea 100644 --- a/src/Apps/W1/PEPPOL/App/src/Response/EDocumentMessageType.Enum.al +++ b/src/Apps/W1/PEPPOL/App/src/Response/EDocumentMessageType.Enum.al @@ -8,11 +8,11 @@ namespace Microsoft.Peppol.Response; /// Identifies the type of an E-Document message (e.g. a PEPPOL Order Response). /// Extend this enum in a format-specific app to declare new message types. /// -enum 50000 "E-Document Message Type" +enum 37207 "E-Document Message Type" { Extensible = true; - value(50100; "PEPPOL Order Response") + value(37210; "PEPPOL Order Response") { Caption = 'PEPPOL Order Response'; } diff --git a/src/Apps/W1/PEPPOL/App/src/Response/PEPPOLOrderRespBuilder.Codeunit.al b/src/Apps/W1/PEPPOL/App/src/Response/PEPPOLOrderRespBuilder.Codeunit.al index 94b351c7f7..3630c2fd1c 100644 --- a/src/Apps/W1/PEPPOL/App/src/Response/PEPPOLOrderRespBuilder.Codeunit.al +++ b/src/Apps/W1/PEPPOL/App/src/Response/PEPPOLOrderRespBuilder.Codeunit.al @@ -5,13 +5,12 @@ namespace Microsoft.Peppol.Response; using System.Utilities; -using System.Xml; /// /// Builds a minimal, well-formed PEPPOL BIS 28 Order Response (code AB = Acknowledged) /// for an inbound Sales Order that was read into draft. /// -codeunit 50102 "PEPPOL Order Resp. Builder" +codeunit 37209 "PEPPOL Order Resp. Builder" { Access = Public; InherentEntitlements = X;