[Enforced Digital Vouchers] - Attaching E-Documents when using Digital Vouchers#29793
[Enforced Digital Vouchers] - Attaching E-Documents when using Digital Vouchers#29793GMatuleviciute wants to merge 15 commits intomicrosoft:mainfrom
Conversation
Introduces E-Document as a new check type for digital vouchers, enforcing automatic generation and linking for purchase documents. Updates UI logic to ensure consistent behavior and validation. Extends data structures and event handling to support E-Document attachment and integration with related processes. Enables more robust compliance with regulatory requirements for electronic document management.
Adds feature flag and setup validations before attaching digital vouchers to documents, ensuring attachments only occur when the functionality is enabled and properly configured. Expands handling for purchase credit memos and improves type checks for both sales and purchase documents.
Streamlines digital voucher validation by generalizing E-Document attachment to support more document types and rely on record references rather than hardcoded field access or document types. Improves maintainability and feature extensibility by extracting document details via a dedicated method and refining permission set object ordering. Enhances code clarity and robustness in preparation for feature expansion.
Introduces comprehensive automated tests covering E-Document attachment behavior for sales and purchase documents, ensuring correct enforcement and attachment logic. Updates dependency declarations for better test isolation, removes unused variables, and improves error checking and file name handling for E-Document processing. Enhances reliability and maintainability of digital voucher and E-Document integration.
…herEntrySetup.Table.al Co-authored-by: Grasiele Matuleviciute <131970463+GMatuleviciute@users.noreply.github.com>
…herEntrySetup.Table.al Co-authored-by: Grasiele Matuleviciute <131970463+GMatuleviciute@users.noreply.github.com>
Refactors E-Document file attachment for purchase documents by relocating the logic from the check codeunit to the main implementation codeunit, improving code structure and responsibility separation. Updates test object ranges, enhances test reliability, and adjusts error handling to use labels for better localization. Clarifies and simplifies validation around E-Document requirements and streamlines page variable initialization. Improves maintainability and prepares for future extensibility.
…implements "Digital Voucher Check"
|
Could not find linked issues in the pull request description. Please make sure the pull request description contains a line that contains 'Fixes #' followed by the issue number being fixed. Use that pattern for every issue you want to link. |
| IncomingDocumentAttachment.SetRange("Posting Date", EDocument."Posting Date"); | ||
| IncomingDocumentAttachment.SetContentFromBlob(TempBlob); | ||
| if not ImportAttachmentIncDoc.ImportAttachment(IncomingDocumentAttachment, StrSubstNo(FileNameTok, RecordLinkTxt), TempBlob) then | ||
| exit; |
There was a problem hiding this comment.
Add telemetry to see when we fail
| IncomingDocumentAttachment.SetRange("Posting Date", PostingDate); | ||
| IncomingDocumentAttachment.SetContentFromBlob(TempBlob); | ||
| if not ImportAttachmentIncDoc.ImportAttachment(IncomingDocumentAttachment, FileName, TempBlob) then | ||
| exit; |
| begin | ||
| TempBlob.CreateInStream(PdfInStream); | ||
| if not PDFDocument.GetDocumentAttachmentStream(PdfInStream, ExtractedXmlBlob) then | ||
| exit; |
| PurchCrMemoHeader: Record "Purch. Cr. Memo Hdr."; | ||
| PurchInvHeader: Record "Purch. Inv. Header"; | ||
| ReportSelections: Record "Report Selections"; | ||
| SalesCrMemoHeader: Record "Sales Cr.Memo Header"; | ||
| SalesInvHeader: Record "Sales Invoice Header"; | ||
| ServCrMemoHeader: Record "Service Cr.Memo Header"; | ||
| ServInvHeader: Record "Service Invoice Header"; |
| IncomingDocumentAttachment.Default := false; | ||
| IncomingDocumentAttachment."Main Attachment" := false; | ||
| if not ImportAttachmentIncDoc.ImportAttachment(IncomingDocumentAttachment, FileName, ExtractedXmlBlob) then | ||
| exit; |
| ServCrMemoHeader: Record "Service Cr.Memo Header"; | ||
| ServInvHeader: Record "Service Invoice Header"; | ||
| RecVar: Variant; |
| @@ -653,8 +780,8 @@ codeunit 5579 "Digital Voucher Impl." | |||
| [EventSubscriber(ObjectType::Table, Database::"Incoming Document", 'OnBeforeRemoveReferencedRecords', '', false, false)] | |||
| local procedure CheckVoucherOnBeforeRemoveReferencedRecords(IncomingDocument: Record "Incoming Document"; var IsHandled: Boolean) | |||
| var | |||
| RecRef: RecordRef; | |||
| RelatedRecordID: RecordId; | |||
| RecRef: RecordRef; | |||
| if (DigitalVoucherEntryType in [DigitalVoucherEntryType::"General Journal", DigitalVoucherEntryType::"Purchase Journal", DigitalVoucherEntryType::"Sales Journal"]) or | ||
| (RecRef.Number() = Database::"Service Header") | ||
| then | ||
| error(NotPossibleToPostWithoutVoucherErr); |
| if EDocument."Entry No" <> 0 then begin | ||
| EDocument."Document No." := PurchaseHeader."No."; | ||
| EDocument."Posting Date" := PurchaseHeader."Posting Date"; | ||
| EDocument."Document Record ID" := PurchaseHeader.RecordId(); | ||
| EDocument.Modify(false); | ||
| end; |
There was a problem hiding this comment.
Let's avoid IF statements in tests if possible as it make it harded to read. Can we split this procedure into two, one only create the purchase header and the other do both. The latter will call the first one to remove duplicatio.
Improves observability by logging errors via feature telemetry when e-document attachment operations fail, including XML extraction from PDFs and incoming document imports. This helps diagnose issues in production without relying solely on user-reported errors. Refactors a purchase document test helper to separate the case where no e-document link is needed, removing an unnecessary guard condition and simplifying the overload that requires an e-document record. Also includes minor code style normalizations (variable reordering, lowercase `error()` calls) for consistency.
AndriusAndrulevicius
left a comment
There was a problem hiding this comment.
Comments resolved
| IncomingDocumentAttachment.SetRange("Posting Date", EDocument."Posting Date"); | ||
| IncomingDocumentAttachment.SetContentFromBlob(TempBlob); | ||
| if not ImportAttachmentIncDoc.ImportAttachment(IncomingDocumentAttachment, StrSubstNo(FileNameTok, RecordLinkTxt), TempBlob) then | ||
| exit; |
| IncomingDocumentAttachment.SetRange("Posting Date", PostingDate); | ||
| IncomingDocumentAttachment.SetContentFromBlob(TempBlob); | ||
| if not ImportAttachmentIncDoc.ImportAttachment(IncomingDocumentAttachment, FileName, TempBlob) then | ||
| exit; |
| begin | ||
| TempBlob.CreateInStream(PdfInStream); | ||
| if not PDFDocument.GetDocumentAttachmentStream(PdfInStream, ExtractedXmlBlob) then | ||
| exit; |
| IncomingDocumentAttachment.Default := false; | ||
| IncomingDocumentAttachment."Main Attachment" := false; | ||
| if not ImportAttachmentIncDoc.ImportAttachment(IncomingDocumentAttachment, FileName, ExtractedXmlBlob) then | ||
| exit; |
| PurchCrMemoHeader: Record "Purch. Cr. Memo Hdr."; | ||
| PurchInvHeader: Record "Purch. Inv. Header"; | ||
| ReportSelections: Record "Report Selections"; | ||
| SalesCrMemoHeader: Record "Sales Cr.Memo Header"; | ||
| SalesInvHeader: Record "Sales Invoice Header"; | ||
| ServCrMemoHeader: Record "Service Cr.Memo Header"; | ||
| ServInvHeader: Record "Service Invoice Header"; |
| ServCrMemoHeader: Record "Service Cr.Memo Header"; | ||
| ServInvHeader: Record "Service Invoice Header"; | ||
| RecVar: Variant; |
| @@ -653,8 +780,8 @@ codeunit 5579 "Digital Voucher Impl." | |||
| [EventSubscriber(ObjectType::Table, Database::"Incoming Document", 'OnBeforeRemoveReferencedRecords', '', false, false)] | |||
| local procedure CheckVoucherOnBeforeRemoveReferencedRecords(IncomingDocument: Record "Incoming Document"; var IsHandled: Boolean) | |||
| var | |||
| RecRef: RecordRef; | |||
| RelatedRecordID: RecordId; | |||
| RecRef: RecordRef; | |||
| if (DigitalVoucherEntryType in [DigitalVoucherEntryType::"General Journal", DigitalVoucherEntryType::"Purchase Journal", DigitalVoucherEntryType::"Sales Journal"]) or | ||
| (RecRef.Number() = Database::"Service Header") | ||
| then | ||
| error(NotPossibleToPostWithoutVoucherErr); |
| if EDocument."Entry No" <> 0 then begin | ||
| EDocument."Document No." := PurchaseHeader."No."; | ||
| EDocument."Posting Date" := PurchaseHeader."Posting Date"; | ||
| EDocument."Document Record ID" := PurchaseHeader.RecordId(); | ||
| EDocument.Modify(false); | ||
| end; |
| codeunit "Digital Voucher Entry" = X, | ||
| codeunit "Digital Voucher Feature" = X, | ||
| codeunit "Digital Voucher Impl." = X, | ||
| codeunit "Voucher Attachment Check" = X, | ||
| codeunit "Voucher Attach Or Note Check" = X, | ||
| codeunit "Voucher E-Document Check" = X, | ||
| codeunit "Voucher No Check" = X, | ||
| codeunit "Voucher Unknown Check" = X, | ||
| codeunit "Digital Voucher Impl." = X, | ||
| codeunit "Digital Voucher Feature" = X, | ||
| codeunit "Digital Voucher Entry" = X; | ||
| page "Digital Voucher Entry Setup" = X, | ||
| page "Digital Voucher Guide" = X, | ||
| page "Digital Voucher Setup" = X, | ||
| page "Voucher Entry Source Codes" = X; |
There was a problem hiding this comment.
Revert unnecessary changes related to ordering
| } | ||
| } | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
Revert unnecessary change in the last line
| IncomingDocumentAttachment.SetRange("Posting Date", EDocument."Posting Date"); | ||
| IncomingDocumentAttachment.SetContentFromBlob(TempBlob); | ||
| if not ImportAttachmentIncDoc.ImportAttachment(IncomingDocumentAttachment, StrSubstNo(FileNameTok, RecordLinkTxt), TempBlob) then begin | ||
| FeatureTelemetry.LogError('', 'E-Documents', 'AttachIncomingEDocument', GetLastErrorText(true), GetLastErrorCallStack()); |
There was a problem hiding this comment.
We cannot write the error text and erros tack to telemetry as it may potentially contain the customer data. Just have hardcoded lines there - "Cannot import attachment" or something like that. We will be able to find the error by telemetry tag, so the text won't add much value.
| IncomingDocumentAttachment.SetRange("Posting Date", PostingDate); | ||
| IncomingDocumentAttachment.SetContentFromBlob(TempBlob); | ||
| if not ImportAttachmentIncDoc.ImportAttachment(IncomingDocumentAttachment, FileName, TempBlob) then begin | ||
| FeatureTelemetry.LogError('', 'E-Documents', 'AttachPurchaseEDocument', GetLastErrorText(true), GetLastErrorCallStack()); |
There was a problem hiding this comment.
Do not write error text and call stack to telemetry, only hardcoded text
| begin | ||
| TempBlob.CreateInStream(PdfInStream); | ||
| if not PDFDocument.GetDocumentAttachmentStream(PdfInStream, ExtractedXmlBlob) then begin | ||
| FeatureTelemetry.LogError('', 'E-Documents', 'ExtractXMLFromPDF', GetLastErrorText(true), GetLastErrorCallStack()); |
There was a problem hiding this comment.
Replace with hardcoded text to avoid writing customer data to telemetry
| IncomingDocumentAttachment.Default := false; | ||
| IncomingDocumentAttachment."Main Attachment" := false; | ||
| if not ImportAttachmentIncDoc.ImportAttachment(IncomingDocumentAttachment, FileName, ExtractedXmlBlob) then begin | ||
| FeatureTelemetry.LogError('', 'E-Documents', 'ExtractXMLFromPDF', GetLastErrorText(true), GetLastErrorCallStack()); |
| "TranslationFile" | ||
| ] | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
Revert unnecessary change in the last line
| "application": "29.0.0.0", | ||
| "target": "Cloud" | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
Revert the change for the last line
This pull request does not have a related issue as it's part of the delivery for development agreed directly with @altotovi @Groenbech96
Implementation
With this update enhanced automation for Digital Vouchers in sales and purchase document posting. When enabled, the system will automatically generate and attach E-Documents and related files to posted documents, streamlining document management and compliance.
Functional Changes
When posting sales or purchase documents, if setup is set to use E-Documents the system will attach related E-Document files to the Incoming Document Files of the posted sales/purchase document.
Impact
Reduces manual effort in attaching compliance documents.
Ensures all relevant files are consistently linked to posted documents.
Important
This Pull request related with microsoft/BCApps#7006
Fixes #