From 693fe4249313386b1fe8b53cab900b85721635c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Wed, 8 Jul 2026 10:57:32 +0300 Subject: [PATCH 1/5] Adds E-document voucher enforcement Enables digital vouchers to require linked E-documents for purchase posting and to attach exported or posted E-documents as incoming attachments. It also adds setup validation for E-document check types and covers the flow with dedicated tests. --- src/Apps/W1/EDocument/App/app.json | 2 +- .../EDocIntegrationManagement.Codeunit.al | 9 + .../App/src/Logging/EDocDataStorage.Table.al | 3 +- .../EDocumentSubscribers.Codeunit.al | 8 +- .../DigitalVoucherObjects.PermissionSet.al | 1 + .../W1/EnforcedDigitalVouchers/app/app.json | 14 +- .../DigitalVoucherEntrySetup.Table.al | 17 + .../DigVoucherIncDocAttach.TableExt.al | 4 + .../DigitalVoucherCheckType.Enum.al | 5 + .../DigitalVoucherImpl.Codeunit.al | 157 ++++++- .../VoucherEDocumentCheck.Codeunit.al | 61 +++ .../W1/EnforcedDigitalVouchers/test/app.json | 16 + .../test/src/EDocAttachmentTests.Codeunit.al | 440 ++++++++++++++++++ 13 files changed, 726 insertions(+), 11 deletions(-) create mode 100644 src/Apps/W1/EnforcedDigitalVouchers/app/src/Implementation/VoucherEDocumentCheck.Codeunit.al create mode 100644 src/Apps/W1/EnforcedDigitalVouchers/test/src/EDocAttachmentTests.Codeunit.al diff --git a/src/Apps/W1/EDocument/App/app.json b/src/Apps/W1/EDocument/App/app.json index d35551d6c18..b1529550111 100644 --- a/src/Apps/W1/EDocument/App/app.json +++ b/src/Apps/W1/EDocument/App/app.json @@ -87,4 +87,4 @@ "features": [ "TranslationFile" ] -} \ No newline at end of file +} diff --git a/src/Apps/W1/EDocument/App/src/Integration/EDocIntegrationManagement.Codeunit.al b/src/Apps/W1/EDocument/App/src/Integration/EDocIntegrationManagement.Codeunit.al index 71b54953bb5..77134762a74 100644 --- a/src/Apps/W1/EDocument/App/src/Integration/EDocIntegrationManagement.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Integration/EDocIntegrationManagement.Codeunit.al @@ -600,6 +600,10 @@ codeunit 6134 "E-Doc. Integration Management" #endregion + internal procedure RunOnAfterUpdateToPostedPurchaseEDocument(var EDocument: Record "E-Document"; PostedRecord: Variant; PostedDocumentNo: Code[20]; DocumentType: Enum "E-Document Type") + begin + this.OnAfterUpdateToPostedPurchaseEDocument(EDocument, PostedRecord, PostedDocumentNo, DocumentType); + end; var EDocumentLog: Codeunit "E-Document Log"; EDocumentProcessing: Codeunit "E-Document Processing"; @@ -649,4 +653,9 @@ codeunit 6134 "E-Doc. Integration Management" local procedure OnAfterSendDocument(EDocuments: Record "E-Document"; EDocumentService: Record "E-Document Service"; HttpRequest: HttpRequestMessage; HttpResponse: HttpResponseMessage) begin end; + + [IntegrationEvent(false, false)] + local procedure OnAfterUpdateToPostedPurchaseEDocument(var EDocument: Record "E-Document"; PostedRecord: Variant; PostedDocumentNo: Code[20]; DocumentType: Enum "E-Document Type") + begin + end; } diff --git a/src/Apps/W1/EDocument/App/src/Logging/EDocDataStorage.Table.al b/src/Apps/W1/EDocument/App/src/Logging/EDocDataStorage.Table.al index 24baa6673fb..424dd114a93 100644 --- a/src/Apps/W1/EDocument/App/src/Logging/EDocDataStorage.Table.al +++ b/src/Apps/W1/EDocument/App/src/Logging/EDocDataStorage.Table.al @@ -72,9 +72,8 @@ table 6125 "E-Doc. Data Storage" } } - internal procedure GetTempBlob() TempBlob: Codeunit "Temp Blob" + procedure GetTempBlob() TempBlob: Codeunit "Temp Blob" begin TempBlob.FromRecord(Rec, Rec.FieldNo("Data Storage")); 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 168e38ccc7f..b4229806d24 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/EDocumentSubscribers.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/EDocumentSubscribers.Codeunit.al @@ -667,6 +667,7 @@ codeunit 6103 "E-Document Subscribers" EDocService: Record "E-Document Service"; EDocumentLog: Codeunit "E-Document Log"; EDocLogHelper: Codeunit "E-Document Log Helper"; + EDocIntegrationManagement: Codeunit "E-Doc. Integration Management"; PostedSourceDocumentHeader: RecordRef; begin PostedSourceDocumentHeader.GetTable(PostedRecord); @@ -676,7 +677,7 @@ codeunit 6103 "E-Document Subscribers" EDocument.Status := Enum::"E-Document Status"::Processed; EDocument.Modify(true); - OnAfterUpdateToPostedPurchaseEDocument(EDocument, PostedRecord, PostedDocumentNo, DocumentType); + EDocIntegrationManagement.RunOnAfterUpdateToPostedPurchaseEDocument(EDocument, PostedRecord, PostedDocumentNo, DocumentType); EDocService := EDocumentLog.GetLastServiceFromLog(EDocument); EDocLogHelper.InsertLog(EDocument, EDocService, Enum::"E-Document Service Status"::"Imported Document Created"); @@ -762,9 +763,4 @@ codeunit 6103 "E-Document Subscribers" TelemetryDimensions.Add(EDocImpSessionTelemetry.GetEDocLineSystemIdTok(), EDocImpSessionTelemetry.CreateSystemIdText(LineSystemId)); Telemetry.LogMessage('0000PYF', DraftChangeTok, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::All, TelemetryDimensions); end; - - [IntegrationEvent(false, false)] - local procedure OnAfterUpdateToPostedPurchaseEDocument(var EDocument: Record "E-Document"; PostedRecord: Variant; PostedDocumentNo: Code[20]; DocumentType: Enum "E-Document Type") - begin - end; } diff --git a/src/Apps/W1/EnforcedDigitalVouchers/app/Permissions/DigitalVoucherObjects.PermissionSet.al b/src/Apps/W1/EnforcedDigitalVouchers/app/Permissions/DigitalVoucherObjects.PermissionSet.al index b9ed6c23e53..ce2ab2b5ac2 100644 --- a/src/Apps/W1/EnforcedDigitalVouchers/app/Permissions/DigitalVoucherObjects.PermissionSet.al +++ b/src/Apps/W1/EnforcedDigitalVouchers/app/Permissions/DigitalVoucherObjects.PermissionSet.al @@ -17,6 +17,7 @@ permissionset 5583 "Digital Voucher - Objects" page "Digital Voucher Guide" = X, page "Voucher Entry Source Codes" = X, codeunit "Voucher Attach Or Note Check" = X, + codeunit "Voucher E-Document Check" = X, codeunit "Voucher Attachment Check" = X, codeunit "Voucher No Check" = X, codeunit "Voucher Unknown Check" = X, diff --git a/src/Apps/W1/EnforcedDigitalVouchers/app/app.json b/src/Apps/W1/EnforcedDigitalVouchers/app/app.json index 847f59711a6..16ca0aeeb07 100644 --- a/src/Apps/W1/EnforcedDigitalVouchers/app/app.json +++ b/src/Apps/W1/EnforcedDigitalVouchers/app/app.json @@ -11,7 +11,14 @@ "url": "https://go.microsoft.com/fwlink/?LinkId=724011", "logo": "ExtensionLogo.png", "contextSensitiveHelpUrl": "https://go.microsoft.com/fwlink/?linkid=2204541", - "dependencies": [], + "dependencies": [ + { + "id": "e1d97edc-c239-46b4-8d84-6368bdf67c8b", + "name": "E-Document Core", + "publisher": "Microsoft", + "version": "28.0.0.0" + } + ], "screenshots": [], "platform": "29.0.0.0", "internalsVisibleTo": [ @@ -20,6 +27,11 @@ "name": "Enforced Digital Vouchers Test Library", "publisher": "Microsoft" }, + { + "id": "f0eb8756-ea72-4ef8-b0de-686d2a44b259", + "name": "Enforced Digital Vouchers Tests", + "publisher": "Microsoft" + }, { "id": "bb837764-d7cc-4b7b-898a-3ea5a1fab62f", "name": "Enforced Digital Vouchers (DK)", diff --git a/src/Apps/W1/EnforcedDigitalVouchers/app/src/EntrySetup/DigitalVoucherEntrySetup.Table.al b/src/Apps/W1/EnforcedDigitalVouchers/app/src/EntrySetup/DigitalVoucherEntrySetup.Table.al index 270499c6475..3e622abc349 100644 --- a/src/Apps/W1/EnforcedDigitalVouchers/app/src/EntrySetup/DigitalVoucherEntrySetup.Table.al +++ b/src/Apps/W1/EnforcedDigitalVouchers/app/src/EntrySetup/DigitalVoucherEntrySetup.Table.al @@ -21,9 +21,26 @@ table 5579 "Digital Voucher Entry Setup" } field(2; "Check Type"; Enum "Digital Voucher Check Type") { + trigger OnValidate() + var + CheckTypeEDocErr: Label '%1 %2 is available only for %3 %4 or %5.', Comment = '%1 - Check type field caption, %2 - Check type value, %3 - Entry type field caption, %4 - Entry type value, %5 - Entry type value'; + begin + if "Check Type" = "Check Type"::"E-Document" then begin + if not ("Entry Type" in ["Entry Type"::"Sales Document", "Entry Type"::"Purchase Document"]) then + Error(CheckTypeEDocErr, FieldCaption("Check Type"), "Check Type"::"E-Document", FieldCaption("Entry Type"), "Entry Type"::"Sales Document", "Entry Type"::"Purchase Document"); + "Generate Automatically" := true; + end; + end; } field(3; "Generate Automatically"; Boolean) { + trigger OnValidate() + var + GenerateAutoMustBeEnabledErr: Label 'Generate Automatically must be enabled when %1 is %2.', Comment = '%1 - Check type field caption, %2 - Check type value'; + begin + if ("Check Type" = "Check Type"::"E-Document") and (not "Generate Automatically") then + Error(GenerateAutoMustBeEnabledErr, FieldCaption("Check Type"), "Check Type"::"E-Document"); + end; } field(4; "Skip If Manually Added"; Boolean) { diff --git a/src/Apps/W1/EnforcedDigitalVouchers/app/src/Extensions/DigVoucherIncDocAttach.TableExt.al b/src/Apps/W1/EnforcedDigitalVouchers/app/src/Extensions/DigVoucherIncDocAttach.TableExt.al index a86976dc865..aef35ff27ef 100644 --- a/src/Apps/W1/EnforcedDigitalVouchers/app/src/Extensions/DigVoucherIncDocAttach.TableExt.al +++ b/src/Apps/W1/EnforcedDigitalVouchers/app/src/Extensions/DigVoucherIncDocAttach.TableExt.al @@ -12,5 +12,9 @@ tableextension 5582 "Dig. Voucher Inc. Doc. Attach." extends "Incoming Document { DataClassification = SystemMetadata; } + field(5583; "Is E-Document"; Boolean) + { + DataClassification = SystemMetadata; + } } } \ No newline at end of file diff --git a/src/Apps/W1/EnforcedDigitalVouchers/app/src/Implementation/DigitalVoucherCheckType.Enum.al b/src/Apps/W1/EnforcedDigitalVouchers/app/src/Implementation/DigitalVoucherCheckType.Enum.al index e522f49d46a..528ffcba8c1 100644 --- a/src/Apps/W1/EnforcedDigitalVouchers/app/src/Implementation/DigitalVoucherCheckType.Enum.al +++ b/src/Apps/W1/EnforcedDigitalVouchers/app/src/Implementation/DigitalVoucherCheckType.Enum.al @@ -26,4 +26,9 @@ enum 5580 "Digital Voucher Check Type" implements "Digital Voucher Check" Caption = 'Attachment or Note'; Implementation = "Digital Voucher Check" = "Voucher Attach Or Note Check"; } + value(3; "E-Document") + { + Caption = 'E-Document'; + Implementation = "Digital Voucher Check" = "Voucher E-Document Check"; + } } diff --git a/src/Apps/W1/EnforcedDigitalVouchers/app/src/Implementation/DigitalVoucherImpl.Codeunit.al b/src/Apps/W1/EnforcedDigitalVouchers/app/src/Implementation/DigitalVoucherImpl.Codeunit.al index 1028e5eb706..f761e3b449c 100644 --- a/src/Apps/W1/EnforcedDigitalVouchers/app/src/Implementation/DigitalVoucherImpl.Codeunit.al +++ b/src/Apps/W1/EnforcedDigitalVouchers/app/src/Implementation/DigitalVoucherImpl.Codeunit.al @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. // ------------------------------------------------------------------------------------------------ @@ -8,6 +8,7 @@ using Microsoft.Finance.GeneralLedger.Journal; using Microsoft.Finance.GeneralLedger.Ledger; using Microsoft.Finance.GeneralLedger.Posting; using Microsoft.Finance.GeneralLedger.Reports; +using System.Telemetry; using Microsoft.Foundation.AuditCodes; using Microsoft.Foundation.Reporting; using Microsoft.Purchases.Document; @@ -24,6 +25,7 @@ using Microsoft.Service.History; using Microsoft.Service.Posting; using System.Email; using System.Environment.Configuration; +using System.IO; using System.Media; using System.Reflection; using System.Utilities; @@ -272,6 +274,143 @@ codeunit 5579 "Digital Voucher Impl." exit(not IncomingDocumentAttachment.IsEmpty()); end; + local procedure AttachIncomingEDocument(EDocument: Record "E-Document"; SourceDocumentHeader: RecordRef; var TempBlob: Codeunit "Temp Blob") + var + DigitalVoucherEntrySetup: Record "Digital Voucher Entry Setup"; + IncomingDocumentAttachment: Record "Incoming Document Attachment"; + EDocumentHelper: Codeunit "E-Document Processing"; + ImportAttachmentIncDoc: Codeunit "Import Attachment - Inc. Doc."; + FeatureTelemetry: Codeunit "Feature Telemetry"; + FileNameTok: Label 'E-Document_%1.xml', Locked = true; + RecordLinkTxt: Text; + begin + if not DigitalVoucherFeature.IsFeatureEnabled() then + exit; + + GetDigitalVoucherEntrySetup(DigitalVoucherEntrySetup, "Digital Voucher Entry Type"::"Sales Document"); + if DigitalVoucherEntrySetup."Check Type" <> DigitalVoucherEntrySetup."Check Type"::"E-Document" then + exit; + + if not (EDocument."Document Type" in [ + EDocument."Document Type"::"Sales Invoice", + EDocument."Document Type"::"Sales Credit Memo", + EDocument."Document Type"::"Sales Order", + EDocument."Document Type"::"Sales Quote", + EDocument."Document Type"::"Sales Return Order"]) then + exit; + + RecordLinkTxt := EDocumentHelper.GetRecordLinkText(EDocument); + IncomingDocumentAttachment.SetRange("Document No.", EDocument."Document No."); + IncomingDocumentAttachment.SetRange("Posting Date", EDocument."Posting Date"); + IncomingDocumentAttachment.SetContentFromBlob(TempBlob); + if not ImportAttachmentIncDoc.ImportAttachment(IncomingDocumentAttachment, StrSubstNo(FileNameTok, RecordLinkTxt), TempBlob) then begin + FeatureTelemetry.LogError('0000SMC', 'E-Documents', 'AttachIncomingEDocument', 'Cannot import attachment'); + exit; + end; + + IncomingDocumentAttachment."Is E-Document" := true; + IncomingDocumentAttachment.Modify(false); + end; + + local procedure AttachOutgoingEDocument(var EDocument: Record "E-Document"; PostedRecord: Variant) + var + DigitalVoucherEntrySetup: Record "Digital Voucher Entry Setup"; + VoucherEDocumentCheck: Codeunit "Voucher E-Document Check"; + RecRef: RecordRef; + DocNo: Code[20]; + PostingDate: Date; + DocType: Text; + begin + if not DigitalVoucherFeature.IsFeatureEnabled() then + exit; + + GetDigitalVoucherEntrySetup(DigitalVoucherEntrySetup, "Digital Voucher Entry Type"::"Purchase Document"); + if DigitalVoucherEntrySetup."Check Type" <> DigitalVoucherEntrySetup."Check Type"::"E-Document" then + exit; + + if not (EDocument."Document Type" in [ + EDocument."Document Type"::"Purchase Invoice", + EDocument."Document Type"::"Purchase Credit Memo", + EDocument."Document Type"::"Purchase Order", + EDocument."Document Type"::"Purchase Quote", + EDocument."Document Type"::"Purchase Return Order"]) then + exit; + + RecRef.GetTable(PostedRecord); + DigitalVoucherEntry.GetDocNoAndPostingDateFromRecRef(DocType, DocNo, PostingDate, RecRef); + AttachPurchaseEDocument(EDocument, DocNo, PostingDate); + end; + + local procedure AttachPurchaseEDocument(EDocument: Record "E-Document"; DocumentNo: Code[20]; PostingDate: Date) + var + EDocDataStorage: Record "E-Doc. Data Storage"; + IncomingDocumentAttachment: Record "Incoming Document Attachment"; + ImportAttachmentIncDoc: Codeunit "Import Attachment - Inc. Doc."; + TempBlob: Codeunit "Temp Blob"; + FeatureTelemetry: Codeunit "Feature Telemetry"; + EDocumentFileNameLbl: Label 'E-Document_%1.%2', Comment = '%1 = E-Document Entry No., %2 = File Format', Locked = true; + FileName: Text[250]; + begin + if EDocument."Unstructured Data Entry No." = 0 then + exit; + + if not EDocDataStorage.Get(EDocument."Unstructured Data Entry No.") then + exit; + + TempBlob := EDocDataStorage.GetTempBlob(); + if not TempBlob.HasValue() then + exit; + + if EDocument."File Name" <> '' then + FileName := CopyStr(EDocument."File Name", 1, MaxStrLen(FileName)) + else + FileName := StrSubstNo(EDocumentFileNameLbl, EDocument."Entry No", EDocDataStorage."File Format"); + + IncomingDocumentAttachment.SetRange("Document No.", DocumentNo); + IncomingDocumentAttachment.SetRange("Posting Date", PostingDate); + IncomingDocumentAttachment.SetContentFromBlob(TempBlob); + if not ImportAttachmentIncDoc.ImportAttachment(IncomingDocumentAttachment, FileName, TempBlob) then begin + FeatureTelemetry.LogError('0000SMD', 'E-Documents', 'AttachPurchaseEDocument', 'Cannot import attachment'); + exit; + end; + + IncomingDocumentAttachment."Is E-Document" := true; + IncomingDocumentAttachment.Modify(false); + + if EDocDataStorage."File Format" = EDocDataStorage."File Format"::PDF then begin + FileName := StrSubstNo(EDocumentFileNameLbl, EDocument."Entry No", EDocDataStorage."File Format"::XML); + ExtractXMLFromPDF(TempBlob, FileName, IncomingDocumentAttachment); + end; + end; + + local procedure ExtractXMLFromPDF(var TempBlob: Codeunit System.Utilities."Temp Blob"; FileName: Text[250]; var IncomingDocumentAttachment: Record "Incoming Document Attachment") + var + ImportAttachmentIncDoc: Codeunit "Import Attachment - Inc. Doc."; + PDFDocument: Codeunit "PDF Document"; + ExtractedXmlBlob: Codeunit "Temp Blob"; + FeatureTelemetry: Codeunit "Feature Telemetry"; + PdfInStream: InStream; + begin + TempBlob.CreateInStream(PdfInStream); + if not PDFDocument.GetDocumentAttachmentStream(PdfInStream, ExtractedXmlBlob) then begin + FeatureTelemetry.LogError('0000SME', 'E-Documents', 'ExtractXMLFromPDF', 'Cannot extract XML from PDF'); + exit; + end; + + if not ExtractedXmlBlob.HasValue() then + exit; + + IncomingDocumentAttachment.Default := false; + IncomingDocumentAttachment."Main Attachment" := false; + if not ImportAttachmentIncDoc.ImportAttachment(IncomingDocumentAttachment, FileName, ExtractedXmlBlob) then begin + FeatureTelemetry.LogError('0000SMF', 'E-Documents', 'ExtractXMLFromPDF', 'Cannot import extracted XML'); + exit; + end; + + IncomingDocumentAttachment."Is E-Document" := true; + IncomingDocumentAttachment.Modify(false); + end; + local procedure FilterIncomingDocumentRecordFromRecordRef(var IncomingDocumentAttachment: Record "Incoming Document Attachment"; var IncomingDocument: Record "Incoming Document"; MainRecordRef: RecordRef): Boolean begin Clear(IncomingDocument); @@ -717,6 +856,7 @@ codeunit 5579 "Digital Voucher Impl." local procedure ExcludeDigitalVouchersOnAttachIncomingDocumentsOnAfterSetFilter(var IncomingDocumentAttachment: Record "Incoming Document Attachment") begin IncomingDocumentAttachment.SetRange("Is Digital Voucher", false); + IncomingDocumentAttachment.SetRange("Is E-Document", false); end; [EventSubscriber(ObjectType::Table, Database::"Digital Voucher Setup", 'OnBeforeDeleteEvent', '', false, false)] @@ -739,6 +879,21 @@ codeunit 5579 "Digital Voucher Impl." CopyDigitalVoucherToCorrectiveDocument("Digital Voucher Entry Type"::"Sales Document", SalesInvoiceHeader, SalesHeader."No.", SalesHeader."Posting Date"); end; + [EventSubscriber(ObjectType::Codeunit, Codeunit::"E-Doc. Export", OnExportEDocumentAfterCreateEDocument, '', false, false)] + local procedure EDocExportOnExportEDocumentAfterCreateEDocument(EDocument: Record "E-Document"; SourceDocumentHeaderMapped: RecordRef; SourceDocumentLineMapped: RecordRef; var TempBlob: Codeunit "Temp Blob"; Success: Boolean) + begin + if not Success then + exit; + + AttachIncomingEDocument(EDocument, SourceDocumentHeaderMapped, TempBlob); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"E-Doc. Integration Management", OnAfterUpdateToPostedPurchaseEDocument, '', false, false)] + local procedure EDocumentSubscribers_OnAfterUpdateToPostedPurchaseEDocument(var EDocument: Record "E-Document"; PostedRecord: Variant; DocumentType: Enum "E-Document Type") + begin + AttachOutgoingEDocument(EDocument, PostedRecord); + end; + [IntegrationEvent(false, false)] local procedure OnGenerateDigitalVoucherForDocumentOnCaseElse(RecRef: RecordRef) begin diff --git a/src/Apps/W1/EnforcedDigitalVouchers/app/src/Implementation/VoucherEDocumentCheck.Codeunit.al b/src/Apps/W1/EnforcedDigitalVouchers/app/src/Implementation/VoucherEDocumentCheck.Codeunit.al new file mode 100644 index 00000000000..c35d3ba8df9 --- /dev/null +++ b/src/Apps/W1/EnforcedDigitalVouchers/app/src/Implementation/VoucherEDocumentCheck.Codeunit.al @@ -0,0 +1,61 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.EServices.EDocument; + +using System.Utilities; + +codeunit 5588 "Voucher E-Document Check" implements "Digital Voucher Check" +{ + Access = Internal; + + /// + /// Validates that an E-Document is attached to the document before posting. + /// Only applies to Purchase Documents when the Digital Voucher feature is enabled and Check Type is set to E-Document. + /// + /// Error message management for logging validation errors. + /// The type of digital voucher entry being validated. + /// Record reference to the document being validated. + procedure CheckVoucherIsAttachedToDocument(var ErrorMessageMgt: Codeunit "Error Message Management"; DigitalVoucherEntryType: Enum "Digital Voucher Entry Type"; RecRef: RecordRef) + var + DigitalVoucherEntrySetup: Record "Digital Voucher Entry Setup"; + EDocument: Record "E-Document"; + DigitalVoucherFeature: Codeunit "Digital Voucher Feature"; + DigitalVoucherImpl: Codeunit "Digital Voucher Impl."; + NotPossibleToPostWithoutEDocumentErr: Label 'Not possible to post without linking an E-Document.'; + begin + if not DigitalVoucherFeature.IsFeatureEnabled() then + exit; + + if DigitalVoucherEntryType <> DigitalVoucherEntryType::"Purchase Document" then + exit; + + DigitalVoucherImpl.GetDigitalVoucherEntrySetup(DigitalVoucherEntrySetup, DigitalVoucherEntryType); + if DigitalVoucherEntrySetup."Check Type" <> DigitalVoucherEntrySetup."Check Type"::"E-Document" then + exit; + + EDocument.SetRange("Document Record ID", RecRef.RecordId()); + if EDocument.IsEmpty() then begin + ErrorMessageMgt.LogSimpleErrorMessage(NotPossibleToPostWithoutEDocumentErr); + exit; + end; + end; + + /// + /// Generates a digital voucher for a posted document by delegating to the Attachment check type implementation. + /// This procedure retrieves the digital voucher entry setup and invokes the attachment-based voucher generation. + /// + /// The type of digital voucher entry for the posted document. + /// Record reference to the posted document. + procedure GenerateDigitalVoucherForPostedDocument(DigitalVoucherEntryType: Enum "Digital Voucher Entry Type"; RecRef: RecordRef) + var + DigitalVoucherEntrySetup: Record "Digital Voucher Entry Setup"; + DigitalVoucherImpl: Codeunit "Digital Voucher Impl."; + DigitalVoucherCheck: Interface "Digital Voucher Check"; + begin + DigitalVoucherImpl.GetDigitalVoucherEntrySetup(DigitalVoucherEntrySetup, DigitalVoucherEntryType); + DigitalVoucherCheck := DigitalVoucherEntrySetup."Check Type"::Attachment; + DigitalVoucherCheck.GenerateDigitalVoucherForPostedDocument(DigitalVoucherEntrySetup."Entry Type", RecRef); + end; +} diff --git a/src/Apps/W1/EnforcedDigitalVouchers/test/app.json b/src/Apps/W1/EnforcedDigitalVouchers/test/app.json index 4aea32cba38..fa07d34751a 100644 --- a/src/Apps/W1/EnforcedDigitalVouchers/test/app.json +++ b/src/Apps/W1/EnforcedDigitalVouchers/test/app.json @@ -19,6 +19,18 @@ "publisher": "Microsoft", "version": "29.0.0.0" }, + { + "id": "e1d97edc-c239-46b4-8d84-6368bdf67c8b", + "name": "E-Document Core", + "publisher": "Microsoft", + "version": "28.0.0.0" + }, + { + "id": "e1d97edc-c239-46b4-8d84-6368bdf67c8c", + "name": "E-Document Core Tests", + "publisher": "Microsoft", + "version": "29.0.0.0" + }, { "id": "928f7b70-0dbd-431a-beb5-f45c4adbd361", "name": "Enforced Digital Vouchers Test Library", @@ -48,6 +60,10 @@ { "from": 139515, "to": 139516 + }, + { + "from": 139649, + "to": 139649 } ], "contextSensitiveHelpUrl": "https://go.microsoft.com/fwlink/?linkid=2141039", diff --git a/src/Apps/W1/EnforcedDigitalVouchers/test/src/EDocAttachmentTests.Codeunit.al b/src/Apps/W1/EnforcedDigitalVouchers/test/src/EDocAttachmentTests.Codeunit.al new file mode 100644 index 00000000000..f1bd8be9116 --- /dev/null +++ b/src/Apps/W1/EnforcedDigitalVouchers/test/src/EDocAttachmentTests.Codeunit.al @@ -0,0 +1,440 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.Test.EServices.EDocument; + +using Microsoft.eServices.EDocument; +using Microsoft.eServices.EDocument.Integration; +using Microsoft.eServices.EDocument.Test; +using Microsoft.Foundation.Address; +using Microsoft.Purchases.Document; +using Microsoft.Purchases.History; +using Microsoft.Sales.Customer; +using Microsoft.Sales.Document; +using Microsoft.Sales.History; +using Microsoft.Tests.EServices.EDocument; + +codeunit 139649 "E-Doc. Attachment Tests" +{ + Subtype = Test; + TestPermissions = Disabled; + + var + Assert: Codeunit Assert; + LibraryERM: Codeunit "Library - ERM"; + LibraryInventory: Codeunit "Library - Inventory"; + LibraryPurchase: Codeunit "Library - Purchase"; + LibraryRandom: Codeunit "Library - Random"; + LibrarySales: Codeunit "Library - Sales"; + LibraryTestInitialize: Codeunit "Library - Test Initialize"; + IsInitialized: Boolean; + DialogErrorCodeTok: Label 'Dialog', Locked = true; + NotPossibleToPostWithoutEDocumentErr: Label 'Not possible to post without linking an E-Document.', Locked = true; + EDocumentTok: Label 'E-Document', Locked = true; + + [Test] + procedure GenerateAutomaticallyEnabledWhenCheckTypeSetToEDocument() + var + DigitalVoucherEntrySetup: Record "Digital Voucher Entry Setup"; + begin + // [FEATURE] [Digital Voucher] [E-Document] + // [SCENARIO] Generate Automatically is auto-enabled when Check Type is set to E-Document + Initialize(); + + // [GIVEN] Setup record for Purchase Document with Generate Automatically = false + DigitalVoucherEntrySetup."Entry Type" := DigitalVoucherEntrySetup."Entry Type"::"Purchase Document"; + DigitalVoucherEntrySetup."Generate Automatically" := false; + + // [WHEN] Validate Check Type = E-Document + DigitalVoucherEntrySetup.Validate("Check Type", DigitalVoucherEntrySetup."Check Type"::"E-Document"); + + // [THEN] Generate Automatically is automatically set to true + DigitalVoucherEntrySetup.TestField("Generate Automatically", true); + end; + + [Test] + procedure GenerateAutomaticallyCannotBeDisabledForEDocument() + var + DigitalVoucherEntrySetup: Record "Digital Voucher Entry Setup"; + GenerateAutomaticallyErrorTok: Label 'Generate Automatically must be enabled', Locked = true; + begin + // [FEATURE] [Digital Voucher] [E-Document] + // [SCENARIO] Generate Automatically cannot be disabled when Check Type = E-Document + Initialize(); + + // [GIVEN] Entry Type = Purchase Document, Check Type = E-Document + DigitalVoucherEntrySetup."Entry Type" := DigitalVoucherEntrySetup."Entry Type"::"Purchase Document"; + DigitalVoucherEntrySetup.Validate("Check Type", DigitalVoucherEntrySetup."Check Type"::"E-Document"); + + // [WHEN] Try to disable Generate Automatically + asserterror DigitalVoucherEntrySetup.Validate("Generate Automatically", false); + + // [THEN] Error: Generate Automatically must be enabled when Check Type is E-Document + Assert.ExpectedError(GenerateAutomaticallyErrorTok); + end; + + [Test] + procedure EDocumentCheckTypeNotAllowedForGeneralJournal() + var + DigitalVoucherEntrySetup: Record "Digital Voucher Entry Setup"; + begin + // [FEATURE] [Digital Voucher] [E-Document] + // [SCENARIO] E-Document check type is not allowed for General Journal + Initialize(); + + // [GIVEN] Entry Type = General Journal + DigitalVoucherEntrySetup."Entry Type" := DigitalVoucherEntrySetup."Entry Type"::"General Journal"; + + // [WHEN] Validate Check Type = E-Document + asserterror DigitalVoucherEntrySetup.Validate("Check Type", DigitalVoucherEntrySetup."Check Type"::"E-Document"); + + // [THEN] Error mentioning E-Document requires Sales Document or Purchase Document + Assert.ExpectedError(EDocumentTok); + end; + + [Test] + procedure EDocumentCheckTypeNotAllowedForSalesJournal() + var + DigitalVoucherEntrySetup: Record "Digital Voucher Entry Setup"; + begin + // [FEATURE] [Digital Voucher] [E-Document] + // [SCENARIO] E-Document check type is not allowed for Sales Journal + Initialize(); + + // [GIVEN] Entry Type = Sales Journal + DigitalVoucherEntrySetup."Entry Type" := DigitalVoucherEntrySetup."Entry Type"::"Sales Journal"; + + // [WHEN] Validate Check Type = E-Document + asserterror DigitalVoucherEntrySetup.Validate("Check Type", DigitalVoucherEntrySetup."Check Type"::"E-Document"); + + // [THEN] Error mentioning E-Document requires Sales Document or Purchase Document + Assert.ExpectedError(EDocumentTok); + end; + + [Test] + procedure EDocumentCheckTypeNotAllowedForPurchaseJournal() + var + DigitalVoucherEntrySetup: Record "Digital Voucher Entry Setup"; + begin + // [FEATURE] [Digital Voucher] [E-Document] + // [SCENARIO] E-Document check type is not allowed for Purchase Journal + Initialize(); + + // [GIVEN] Entry Type = Purchase Journal + DigitalVoucherEntrySetup."Entry Type" := DigitalVoucherEntrySetup."Entry Type"::"Purchase Journal"; + + // [WHEN] Validate Check Type = E-Document + asserterror DigitalVoucherEntrySetup.Validate("Check Type", DigitalVoucherEntrySetup."Check Type"::"E-Document"); + + // [THEN] Error mentioning E-Document requires Sales Document or Purchase Document + Assert.ExpectedError(EDocumentTok); + end; + + [Test] + procedure PurchaseInvoiceCannotPostWithoutEDocument() + var + PurchaseHeader: Record "Purchase Header"; + DigVouchersDisableEnforce: Codeunit "Dig. Vouchers Disable Enforce"; + begin + // [FEATURE] [Digital Voucher] [E-Document] + // [SCENARIO] Purchase invoice posting fails when E-Document check is enabled but no e-document linked + Initialize(); + BindSubscription(DigVouchersDisableEnforce); + EnableDigitalVoucherFeature(); + + // [GIVEN] Digital voucher entry setup for purchase document is "E-Document" + InitSetupEDocument("Digital Voucher Entry Type"::"Purchase Document"); + + // [GIVEN] Purchase invoice WITHOUT linked e-document + CreatePurchaseDocument(PurchaseHeader, "Purchase Document Type"::Invoice); + + // [WHEN] Post purchase invoice + asserterror LibraryPurchase.PostPurchaseDocument(PurchaseHeader, true, true); + + // [THEN] Error: Not possible to post without linking an E-Document + Assert.ExpectedErrorCode(DialogErrorCodeTok); + Assert.ExpectedError(NotPossibleToPostWithoutEDocumentErr); + + UnbindSubscription(DigVouchersDisableEnforce); + end; + + [Test] + procedure PurchaseInvoiceEDocumentAttachedAfterPosting() + var + EDocument: Record "E-Document"; + PurchInvHeader: Record "Purch. Inv. Header"; + PurchaseHeader: Record "Purchase Header"; + DigVouchersDisableEnforce: Codeunit "Dig. Vouchers Disable Enforce"; + DocNo: Code[20]; + begin + // [FEATURE] [Digital Voucher] [E-Document] + // [SCENARIO] Purchase invoice with e-document creates incoming attachment with Is E-Document = true + Initialize(); + BindSubscription(DigVouchersDisableEnforce); + EnableDigitalVoucherFeature(); + + // [GIVEN] Digital voucher entry setup for purchase document is "E-Document" + InitSetupEDocument("Digital Voucher Entry Type"::"Purchase Document"); + + // [GIVEN] Purchase invoice with linked e-document + CreatePurchaseDocumentWithEDocument(PurchaseHeader, EDocument, "Purchase Document Type"::Invoice); + + // [WHEN] Post purchase invoice (e-document attachment happens automatically) + DocNo := LibraryPurchase.PostPurchaseDocument(PurchaseHeader, true, true); + + // [THEN] Incoming attachment exists with "Is E-Document" = true + PurchInvHeader.Get(DocNo); + AssertEDocumentIncomingAttachmentExists(PurchInvHeader."Posting Date", PurchInvHeader."No."); + + UnbindSubscription(DigVouchersDisableEnforce); + end; + + [Test] + procedure PurchaseCreditMemoEDocumentAttachedAfterPosting() + var + EDocument: Record "E-Document"; + PurchCrMemoHdr: Record "Purch. Cr. Memo Hdr."; + PurchaseHeader: Record "Purchase Header"; + DigVouchersDisableEnforce: Codeunit "Dig. Vouchers Disable Enforce"; + DocNo: Code[20]; + begin + // [FEATURE] [Digital Voucher] [E-Document] + // [SCENARIO] Purchase credit memo with e-document creates incoming attachment with Is E-Document = true + Initialize(); + BindSubscription(DigVouchersDisableEnforce); + EnableDigitalVoucherFeature(); + + // [GIVEN] Digital voucher entry setup for purchase document is "E-Document" + InitSetupEDocument("Digital Voucher Entry Type"::"Purchase Document"); + + // [GIVEN] Purchase credit memo with linked e-document + CreatePurchaseDocumentWithEDocument(PurchaseHeader, EDocument, "Purchase Document Type"::"Credit Memo"); + + // [WHEN] Post purchase credit memo (e-document attachment happens automatically) + DocNo := LibraryPurchase.PostPurchaseDocument(PurchaseHeader, true, true); + + // [THEN] Incoming attachment exists with "Is E-Document" = true + PurchCrMemoHdr.Get(DocNo); + AssertEDocumentIncomingAttachmentExists(PurchCrMemoHdr."Posting Date", PurchCrMemoHdr."No."); + + UnbindSubscription(DigVouchersDisableEnforce); + end; + + [Test] + procedure SalesInvoiceEDocumentAttachedAfterExport() + var + SalesHeader: Record "Sales Header"; + SalesInvoiceHeader: Record "Sales Invoice Header"; + DigVouchersDisableEnforce: Codeunit "Dig. Vouchers Disable Enforce"; + EDocImplState: Codeunit "E-Doc. Impl. State"; + DocNo: Code[20]; + begin + // [FEATURE] [Digital Voucher] [E-Document] + // [SCENARIO] Sales invoice e-document export creates incoming attachment with Is E-Document = true + Initialize(); + BindSubscription(DigVouchersDisableEnforce); + BindSubscription(EDocImplState); + EnableDigitalVoucherFeature(); + + // [GIVEN] Digital voucher entry setup for sales document is "E-Document" + InitSetupEDocument("Digital Voucher Entry Type"::"Sales Document"); + + // [WHEN] Posted sales invoice + DocNo := CreateSalesDocumentWithEDocRequirements(SalesHeader, "Sales Document Type"::Invoice); + SalesInvoiceHeader.Get(DocNo); + + // [THEN] Incoming attachment exists with "Is E-Document" = true + AssertEDocumentIncomingAttachmentExists(SalesInvoiceHeader."Posting Date", SalesInvoiceHeader."No."); + + UnbindSubscription(DigVouchersDisableEnforce); + UnbindSubscription(EDocImplState); + end; + + [Test] + procedure SalesCreditMemoEDocumentAttachedAfterExport() + var + SalesCrMemoHeader: Record "Sales Cr.Memo Header"; + SalesHeader: Record "Sales Header"; + DigVouchersDisableEnforce: Codeunit "Dig. Vouchers Disable Enforce"; + EDocImplState: Codeunit "E-Doc. Impl. State"; + DocNo: Code[20]; + begin + // [FEATURE] [Digital Voucher] [E-Document] + // [SCENARIO] Sales credit memo e-document export creates incoming attachment with Is E-Document = true + Initialize(); + BindSubscription(DigVouchersDisableEnforce); + BindSubscription(EDocImplState); + EnableDigitalVoucherFeature(); + + // [GIVEN] Digital voucher entry setup for sales document is "E-Document" + InitSetupEDocument("Digital Voucher Entry Type"::"Sales Document"); + + // [WHEN] Posted sales credit memo + DocNo := CreateSalesDocumentWithEDocRequirements(SalesHeader, "Sales Document Type"::"Credit Memo"); + SalesCrMemoHeader.Get(DocNo); + + // [THEN] Incoming attachment exists with "Is E-Document" = true + AssertEDocumentIncomingAttachmentExists(SalesCrMemoHeader."Posting Date", SalesCrMemoHeader."No."); + + UnbindSubscription(DigVouchersDisableEnforce); + UnbindSubscription(EDocImplState); + end; + + local procedure Initialize() + begin + LibraryTestInitialize.OnTestInitialize(Codeunit::"E-Doc. Attachment Tests"); + if IsInitialized then + exit; + LibraryTestInitialize.OnBeforeTestSuiteInitialize(Codeunit::"E-Doc. Attachment Tests"); + + IsInitialized := true; + Commit(); + LibraryTestInitialize.OnAfterTestSuiteInitialize(Codeunit::"E-Doc. Attachment Tests"); + end; + + local procedure EnableDigitalVoucherFeature() + var + DigitalVoucherSetup: Record "Digital Voucher Setup"; + begin + if DigitalVoucherSetup.Get() then + DigitalVoucherSetup.Delete(false); + DigitalVoucherSetup.Enabled := true; + DigitalVoucherSetup.Insert(false); + end; + + local procedure InitSetupEDocument(EntryType: Enum "Digital Voucher Entry Type") + var + DigitalVoucherEntrySetup: Record "Digital Voucher Entry Setup"; + begin + DigitalVoucherEntrySetup.SetRange("Entry Type", EntryType); + DigitalVoucherEntrySetup.DeleteAll(false); + DigitalVoucherEntrySetup."Entry Type" := EntryType; + DigitalVoucherEntrySetup.Validate("Check Type", DigitalVoucherEntrySetup."Check Type"::"E-Document"); + DigitalVoucherEntrySetup.Insert(false); + end; + + local procedure CreatePurchaseDocumentWithEDocument(var PurchaseHeader: Record "Purchase Header"; var EDocument: Record "E-Document"; DocumentType: Enum "Purchase Document Type") + var + EDocumentService: Record "E-Document Service"; + EDocDataStorage: Record "E-Doc. Data Storage"; + EDocLogHelper: Codeunit "E-Document Log Helper"; + OutStream: OutStream; + EDocType: Enum "E-Document Type"; + XmlRootTag: Text; + FileName: Text; + begin + case DocumentType of + "Purchase Document Type"::Invoice: + begin + EDocType := EDocType::"Purchase Invoice"; + XmlRootTag := 'Invoice'; + FileName := 'test-invoice.xml'; + end; + "Purchase Document Type"::"Credit Memo": + begin + EDocType := EDocType::"Purchase Credit Memo"; + XmlRootTag := 'CreditNote'; + FileName := 'test-credit-memo.xml'; + end; + end; + + CreateService(EDocumentService); + + EDocDataStorage.Init(); + EDocDataStorage."File Format" := EDocDataStorage."File Format"::XML; + EDocDataStorage."Data Storage".CreateOutStream(OutStream); + OutStream.WriteText(StrSubstNo('<%1>%2', XmlRootTag, PurchaseHeader."No.")); + EDocDataStorage.Insert(true); + + EDocument.Init(); + EDocument.Service := EDocumentService.Code; + EDocument.Direction := EDocument.Direction::Incoming; + EDocument."Document Type" := EDocType; + EDocument."Unstructured Data Entry No." := EDocDataStorage."Entry No."; + EDocument."File Name" := FileName; + EDocument.Insert(true); + EDocLogHelper.InsertLog(EDocument, EDocumentService, Enum::"E-Document Service Status"::Created); + CreatePurchaseDocument(PurchaseHeader, DocumentType, EDocument); + end; + + local procedure AssertEDocumentIncomingAttachmentExists(PostingDate: Date; DocNo: Code[20]) + var + IncomingDocument: Record "Incoming Document"; + IncomingDocumentAttachment: Record "Incoming Document Attachment"; + NoIncomingDocumentTxt: Label 'No incoming document found for %1 on %2', Comment = '%1 = Document No., %2 = Posting Date'; + begin + Assert.IsTrue( + IncomingDocument.FindByDocumentNoAndPostingDate(IncomingDocument, DocNo, Format(PostingDate)), + StrSubstNo(NoIncomingDocumentTxt, DocNo, PostingDate)); + + IncomingDocumentAttachment.SetRange("Incoming Document Entry No.", IncomingDocument."Entry No."); + IncomingDocumentAttachment.SetRange("Is E-Document", true); + + Assert.RecordIsNotEmpty(IncomingDocumentAttachment); + end; + + local procedure CreateSalesDocumentWithEDocRequirements(var SalesHeader: Record "Sales Header"; DocumentType: Enum "Sales Document Type"): Code[20] + var + Customer: Record Customer; + EDocumentService: Record "E-Document Service"; + LibraryEDocument: Codeunit "Library - E-Document"; + PostCode: Record "Post Code"; + begin + LibraryEDocument.SetupStandardVAT(); + LibraryEDocument.SetupStandardSalesScenario(Customer, EDocumentService, Enum::"E-Document Format"::Mock, Enum::"Service Integration"::Mock); + + LibraryEDocument.CreateSalesHeaderWithItem(Customer, SalesHeader, DocumentType); + + SalesHeader.Validate("Bill-to Address", LibraryRandom.RandText(MaxStrLen(SalesHeader."Bill-to Address"))); + LibraryERM.CreatePostCode(PostCode); + SalesHeader.Validate("Bill-to Post Code", PostCode.Code); + SalesHeader.Validate("Your Reference", LibraryRandom.RandText(MaxStrLen(SalesHeader."Your Reference"))); + + exit(LibrarySales.PostSalesDocument(SalesHeader, true, true)); + end; + + local procedure CreateService(var EDocService: Record "E-Document Service") + var + LibraryUtility: Codeunit "Library - Utility"; + begin + EDocService.Init(); + EDocService.Code := LibraryUtility.GenerateRandomCode20(EDocService.FieldNo(Code), Database::"E-Document Service"); + EDocService.Insert(false); + end; + + local procedure CreatePurchaseDocument(var PurchaseHeader: Record "Purchase Header"; DocumentType: Enum "Purchase Document Type") + var + PurchaseLine: Record "Purchase Line"; + begin + LibraryPurchase.CreatePurchHeader(PurchaseHeader, DocumentType, LibraryPurchase.CreateVendorNo()); + LibraryPurchase.CreatePurchaseLine( + PurchaseLine, PurchaseHeader, PurchaseLine.Type::Item, + LibraryInventory.CreateItemNo(), LibraryRandom.RandInt(10)); + PurchaseLine.Validate("Direct Unit Cost", LibraryRandom.RandDec(100, 2)); + PurchaseLine.Modify(true); + PurchaseHeader.CalcFields(Amount, "Amount Including VAT"); + PurchaseHeader.Validate("Doc. Amount Incl. VAT", PurchaseHeader."Amount Including VAT"); + PurchaseHeader.Modify(false); + end; + + local procedure CreatePurchaseDocument(var PurchaseHeader: Record "Purchase Header"; DocumentType: Enum "Purchase Document Type"; var EDocument: Record "E-Document") + var + PurchaseLine: Record "Purchase Line"; + begin + LibraryPurchase.CreatePurchHeader(PurchaseHeader, DocumentType, LibraryPurchase.CreateVendorNo()); + LibraryPurchase.CreatePurchaseLine( + PurchaseLine, PurchaseHeader, PurchaseLine.Type::Item, + LibraryInventory.CreateItemNo(), LibraryRandom.RandInt(10)); + PurchaseLine.Validate("Direct Unit Cost", LibraryRandom.RandDec(100, 2)); + PurchaseLine.Modify(true); + PurchaseHeader.CalcFields(Amount, "Amount Including VAT"); + PurchaseHeader.Validate("Doc. Amount Incl. VAT", PurchaseHeader."Amount Including VAT"); + PurchaseHeader.Validate("E-Document Link", EDocument.SystemId); + PurchaseHeader.Modify(false); + EDocument."Document No." := PurchaseHeader."No."; + EDocument."Posting Date" := PurchaseHeader."Posting Date"; + EDocument."Document Record ID" := PurchaseHeader.RecordId(); + EDocument.Modify(false); + end; +} From 07e79554aac390d6905bf50740c1b13bc957f9bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Tue, 28 Jul 2026 16:39:43 +0300 Subject: [PATCH 2/5] Removes obsolete integration event Cleans up an unused event hook to simplify processing and avoid maintaining an irrelevant integration point. --- .../App/src/Processing/EDocumentSubscribers.Codeunit.al | 5 ----- 1 file changed, 5 deletions(-) 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 46f9337e09d..4f84c8e0c54 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/EDocumentSubscribers.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/EDocumentSubscribers.Codeunit.al @@ -790,11 +790,6 @@ codeunit 6103 "E-Document Subscribers" Telemetry.LogMessage('0000PYF', DraftChangeTok, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::All, TelemetryDimensions); end; - [IntegrationEvent(false, false)] - 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 From 84733d1aff790a6ff7e761c80d3cbb78a73c94da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Wed, 29 Jul 2026 09:11:12 +0300 Subject: [PATCH 3/5] Simplifies e-document attachment handling Removes unused parameters and references from the attachment flow to keep voucher processing lean and avoid unnecessary dependencies. --- .../app/src/Implementation/DigitalVoucherImpl.Codeunit.al | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Apps/W1/EnforcedDigitalVouchers/app/src/Implementation/DigitalVoucherImpl.Codeunit.al b/src/Apps/W1/EnforcedDigitalVouchers/app/src/Implementation/DigitalVoucherImpl.Codeunit.al index f761e3b449c..8012b7c8c78 100644 --- a/src/Apps/W1/EnforcedDigitalVouchers/app/src/Implementation/DigitalVoucherImpl.Codeunit.al +++ b/src/Apps/W1/EnforcedDigitalVouchers/app/src/Implementation/DigitalVoucherImpl.Codeunit.al @@ -8,7 +8,6 @@ using Microsoft.Finance.GeneralLedger.Journal; using Microsoft.Finance.GeneralLedger.Ledger; using Microsoft.Finance.GeneralLedger.Posting; using Microsoft.Finance.GeneralLedger.Reports; -using System.Telemetry; using Microsoft.Foundation.AuditCodes; using Microsoft.Foundation.Reporting; using Microsoft.Purchases.Document; @@ -28,6 +27,7 @@ using System.Environment.Configuration; using System.IO; using System.Media; using System.Reflection; +using System.Telemetry; using System.Utilities; codeunit 5579 "Digital Voucher Impl." @@ -274,7 +274,7 @@ codeunit 5579 "Digital Voucher Impl." exit(not IncomingDocumentAttachment.IsEmpty()); end; - local procedure AttachIncomingEDocument(EDocument: Record "E-Document"; SourceDocumentHeader: RecordRef; var TempBlob: Codeunit "Temp Blob") + local procedure AttachIncomingEDocument(EDocument: Record "E-Document"; var TempBlob: Codeunit "Temp Blob") var DigitalVoucherEntrySetup: Record "Digital Voucher Entry Setup"; IncomingDocumentAttachment: Record "Incoming Document Attachment"; @@ -315,7 +315,6 @@ codeunit 5579 "Digital Voucher Impl." local procedure AttachOutgoingEDocument(var EDocument: Record "E-Document"; PostedRecord: Variant) var DigitalVoucherEntrySetup: Record "Digital Voucher Entry Setup"; - VoucherEDocumentCheck: Codeunit "Voucher E-Document Check"; RecRef: RecordRef; DocNo: Code[20]; PostingDate: Date; @@ -885,7 +884,7 @@ codeunit 5579 "Digital Voucher Impl." if not Success then exit; - AttachIncomingEDocument(EDocument, SourceDocumentHeaderMapped, TempBlob); + AttachIncomingEDocument(EDocument, TempBlob); end; [EventSubscriber(ObjectType::Codeunit, Codeunit::"E-Doc. Integration Management", OnAfterUpdateToPostedPurchaseEDocument, '', false, false)] From 9744bbdc9c0f05f2a2523e48fe2e213bf0e39e38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Fri, 31 Jul 2026 16:40:13 +0300 Subject: [PATCH 4/5] Adds TestType = IntegrationTest to E-Doc. Attachment Tests codeunit --- .../test/src/EDocAttachmentTests.Codeunit.al | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Apps/W1/EnforcedDigitalVouchers/test/src/EDocAttachmentTests.Codeunit.al b/src/Apps/W1/EnforcedDigitalVouchers/test/src/EDocAttachmentTests.Codeunit.al index f1bd8be9116..8bc877c5415 100644 --- a/src/Apps/W1/EnforcedDigitalVouchers/test/src/EDocAttachmentTests.Codeunit.al +++ b/src/Apps/W1/EnforcedDigitalVouchers/test/src/EDocAttachmentTests.Codeunit.al @@ -19,6 +19,7 @@ codeunit 139649 "E-Doc. Attachment Tests" { Subtype = Test; TestPermissions = Disabled; + TestType = IntegrationTest; var Assert: Codeunit Assert; From 669f54409558a5486c956d8dbc1c6d02a6363b1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Andrulevi=C4=8Dius?= Date: Fri, 7 Aug 2026 15:38:39 +0300 Subject: [PATCH 5/5] Revert "Check Type" on "Sales Document" because this value will be needed in test ChangeGenerateAutomaticallyForSalesDocumentWhenEnforced --- .../test/src/DigitalVouchersTestsDK.Codeunit.al | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Apps/DK/EnforcedDigitalVouchersDK/test/src/DigitalVouchersTestsDK.Codeunit.al b/src/Apps/DK/EnforcedDigitalVouchersDK/test/src/DigitalVouchersTestsDK.Codeunit.al index 1350fe9ac37..770d5f25b6a 100644 --- a/src/Apps/DK/EnforcedDigitalVouchersDK/test/src/DigitalVouchersTestsDK.Codeunit.al +++ b/src/Apps/DK/EnforcedDigitalVouchersDK/test/src/DigitalVouchersTestsDK.Codeunit.al @@ -196,6 +196,11 @@ codeunit 148016 "Digital Vouchers Tests DK" DigitalVoucherEntrySetupPage."Check Type".SetValue(Format(DigitalVoucherEntrySetup."Check Type"::"No Check")); asserterror DigitalVoucherEntrySetupPage.Close(); Assert.ExpectedError(NotAllowedToChangeWhenEnforcedErr); + Clear(DigitalVoucherEntrySetupPage); + DigitalVoucherEntrySetupPage.OpenEdit(); + DigitalVoucherEntrySetupPage.Filter.SetFilter("Entry Type", Format(DigitalVoucherEntrySetup."Entry Type"::"Sales Document")); + DigitalVoucherEntrySetupPage."Check Type".SetValue(Format(DigitalVoucherEntrySetup."Check Type"::"Attachment")); + DigitalVoucherEntrySetupPage.Close(); UnbindSubscription(DigVouchersEnableEnforce); end;