From fcdf9755be7cffdbf04c2e4eefcff49bfbaec16b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Sat, 17 Jan 2026 12:41:41 +0100 Subject: [PATCH 1/9] Clean up page and add action --- .../Extensions/EDocPurchaseHeader.TableExt.al | 5 + .../EDocumentProcessing.Codeunit.al | 65 +++++ .../Import/EDocImportParameters.Table.al | 8 + .../EDocCreatePurchaseInvoice.Codeunit.al | 15 +- .../Purchase/EDocumentPurchaseDraft.Page.al | 207 ++++++++++------ .../Processing/EDocProcessTest.Codeunit.al | 224 ++++++++++++++++++ 6 files changed, 448 insertions(+), 76 deletions(-) diff --git a/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchaseHeader.TableExt.al b/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchaseHeader.TableExt.al index 1dcdb89d79..9c879f406c 100644 --- a/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchaseHeader.TableExt.al +++ b/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchaseHeader.TableExt.al @@ -19,6 +19,11 @@ tableextension 6169 "E-Doc. Purchase Header" extends "Purchase Header" AutoFormatExpression = Rec."Currency Code"; AutoFormatType = 1; } + field(6102; "Created from E-Document"; Boolean) + { + DataClassification = SystemMetadata; + Editable = false; + } } keys { 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 f99890bac4..e4b401ad63 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/EDocumentProcessing.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/EDocumentProcessing.Codeunit.al @@ -687,6 +687,71 @@ codeunit 6108 "E-Document Processing" exit(RecCaption); end; + procedure GetPurchaseDocTypeFilter(EDocumentType: Enum "E-Document Type") PurchaseDocType: Enum "Purchase Document Type" + begin + case EDocumentType of + EDocumentType::"Purchase Invoice": + exit(PurchaseDocType::Invoice); + EDocumentType::"Purchase Credit Memo": + exit(PurchaseDocType::"Credit Memo"); + EDocumentType::"Purchase Order": + exit(PurchaseDocType::Order); + end; + end; + + procedure LinkToExistingPurchaseDocument(EDocument: Record "E-Document"; PurchaseHeader: Record "Purchase Header"): Boolean + var + EDocImportParameters: Record "E-Doc. Import Parameters"; + ConfirmDialogMgt: Codeunit "Confirm Management"; + EDocImport: Codeunit "E-Doc. Import"; + LinkToExistingDocumentQst: Label 'Do you want to link this e-document to %1 %2? This will mark the e-document as processed.', Comment = '%1 = Document Type, %2 = Document No.'; + begin + if not ConfirmDialogMgt.GetResponseOrDefault(StrSubstNo(LinkToExistingDocumentQst, PurchaseHeader."Document Type", PurchaseHeader."No."), true) then + exit(false); + + EDocImportParameters."Link To Existing Doc. Rec. ID" := PurchaseHeader.RecordId(); + EDocImportParameters."Step to Run" := "Import E-Document Steps"::"Finish draft"; + exit(EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParameters)); + end; + + procedure OpenPurchaseDocumentList(EDocumentType: Enum "E-Document Type"; var PurchaseHeader: Record "Purchase Header"): Boolean + var + PurchaseInvoices: Page "Purchase Invoices"; + PurchaseOrders: Page "Purchase Orders"; + PurchaseCreditMemos: Page "Purchase Credit Memos"; + begin + case EDocumentType of + EDocumentType::"Purchase Invoice": + begin + PurchaseInvoices.SetTableView(PurchaseHeader); + PurchaseInvoices.LookupMode := true; + if PurchaseInvoices.RunModal() = Action::LookupOK then begin + PurchaseInvoices.GetRecord(PurchaseHeader); + exit(true); + end; + end; + EDocumentType::"Purchase Credit Memo": + begin + PurchaseCreditMemos.SetTableView(PurchaseHeader); + PurchaseCreditMemos.LookupMode := true; + if PurchaseCreditMemos.RunModal() = Action::LookupOK then begin + PurchaseCreditMemos.GetRecord(PurchaseHeader); + exit(true); + end; + end; + EDocumentType::"Purchase Order": + begin + PurchaseOrders.SetTableView(PurchaseHeader); + PurchaseOrders.LookupMode := true; + if PurchaseOrders.RunModal() = Action::LookupOK then begin + PurchaseOrders.GetRecord(PurchaseHeader); + exit(true); + end; + end; + end; + exit(false); + 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/Import/EDocImportParameters.Table.al b/src/Apps/W1/EDocument/App/src/Processing/Import/EDocImportParameters.Table.al index c0cdec5f7c..d98986e679 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/EDocImportParameters.Table.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/EDocImportParameters.Table.al @@ -57,5 +57,13 @@ table 6106 "E-Doc. Import Parameters" { } #endregion + + /// + /// Specifies an existing purchase document to link to instead of creating a new one. + /// When set, the ApplyDraftToBC step will link the e-document to this existing document rather than creating a new purchase invoice. + /// + field(10; "Link To Existing Doc. Rec. ID"; RecordId) + { + } } } \ No newline at end of file diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al index cef186eb24..bd0c579e5a 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al @@ -38,6 +38,7 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft, TempPOMatchWarnings: Record "E-Doc PO Match Warning" temporary; EDocPOMatching: Codeunit "E-Doc. PO Matching"; DocumentAttachmentMgt: Codeunit "Document Attachment Mgmt"; + EmptyRecordId: RecordId; IEDocumentFinishPurchaseDraft: Interface IEDocumentCreatePurchaseInvoice; YourMatchedLinesAreNotValidErr: Label 'The purchase invoice cannot be created because one or more of its matched lines are not valid matches. Review if your configuration allows for receiving at invoice.'; SomeLinesNotYetReceivedErr: Label 'Some of the matched purchase order lines have not yet been received, you need to either receive the lines or remove the matches.'; @@ -54,16 +55,20 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft, Error(SomeLinesNotYetReceivedErr); IEDocumentFinishPurchaseDraft := EDocImportParameters."Processing Customizations"; - PurchaseHeader := IEDocumentFinishPurchaseDraft.CreatePurchaseInvoice(EDocument); + if EDocImportParameters."Link To Existing Doc. Rec. ID" <> EmptyRecordId then begin + EDocImpSessionTelemetry.SetBool('LinkedToExisting', true); + PurchaseHeader.Get(EDocImportParameters."Link To Existing Doc. Rec. ID"); + end else + PurchaseHeader := IEDocumentFinishPurchaseDraft.CreatePurchaseInvoice(EDocument); EDocPOMatching.TransferPOMatchesFromEDocumentToInvoice(EDocument); PurchaseHeader.SetRecFilter(); PurchaseHeader.FindFirst(); PurchaseHeader."Doc. Amount Incl. VAT" := EDocumentPurchaseHeader.Total; PurchaseHeader."Doc. Amount VAT" := EDocumentPurchaseHeader."Total VAT"; - PurchaseHeader.TestField("Document Type", "Purchase Document Type"::Invoice); PurchaseHeader.TestField("No."); PurchaseHeader."E-Document Link" := EDocument.SystemId; + PurchaseHeader."Created from E-Document" := EDocImportParameters."Link To Existing Doc. Rec. ID" = EmptyRecordId; PurchaseHeader.Modify(); // Post document creation @@ -85,12 +90,16 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft, PurchaseHeader.SetRange("E-Document Link", EDocument.SystemId); if not PurchaseHeader.FindFirst() then exit; + EDocPOMatching.TransferPOMatchesFromInvoiceToEDocument(PurchaseHeader); DocumentAttachmentMgt.CopyAttachments(PurchaseHeader, EDocument); DocumentAttachmentMgt.DeleteAttachedDocuments(PurchaseHeader); + PurchaseHeader.TestField("Document Type", "Purchase Document Type"::Invoice); Clear(PurchaseHeader."E-Document Link"); - PurchaseHeader.Delete(true); + + if PurchaseHeader."Created from E-Document" then + PurchaseHeader.Delete(true); end; procedure CreatePurchaseInvoice(EDocument: Record "E-Document"): Record "Purchase Header" diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al index 02a1b01010..d2e15ed3a8 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al @@ -7,6 +7,7 @@ namespace Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Processing.Import; using Microsoft.Foundation.Attachment; +using Microsoft.Purchases.Document; using Microsoft.Purchases.Vendor; using System.Feedback; using System.Telemetry; @@ -264,82 +265,115 @@ page 6181 "E-Document Purchase Draft" { area(Processing) { - action(CreateDocument) + group(ProcessDocument) { - ApplicationArea = Basic, Suite; - Caption = 'Finalize draft'; - ToolTip = 'Process the electronic document into a business central document'; - Image = CreateDocument; - Visible = ShowFinalizeDraftAction; - - trigger OnAction() - begin - FinalizeEDocument(); - end; - } - action(ResetDraftDocument) - { - ApplicationArea = Basic, Suite; - Caption = 'Reset draft'; - ToolTip = 'Resets the draft document. Any changes made to the draft document will be lost.'; - Image = Restore; - Visible = true; - trigger OnAction() - begin - ResetDraft(); - end; - } - action(AnalyzeDocument) - { - ApplicationArea = Basic, Suite; - Caption = 'Analyze document'; - ToolTip = 'Analyze the selected electronic document'; - Image = SendAsPDF; - Visible = ShowAnalyzeDocumentAction; - - trigger OnAction() - begin - AnalyzeEDocument(); - end; - } - action(ViewFile) - { - ApplicationArea = Basic, Suite; - Caption = 'View pdf'; - ToolTip = 'View pdf.'; - Image = ViewDetails; - Visible = HasPDFSource; - - trigger OnAction() - begin - Rec.ViewSourceFile(); - end; + Caption = 'Process'; + Image = Process; + + action(CreateDocument) + { + ApplicationArea = Basic, Suite; + Caption = 'Finalize draft'; + ToolTip = 'Process the electronic document into a business central document'; + Image = CreateDocument; + Visible = ShowFinalizeDraftAction; + + trigger OnAction() + var + EDocImportParameters: Record "E-Doc. Import Parameters"; + begin + Session.LogMessage('0000PCO', FinalizeDraftInvokedTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::All, 'Category', EDocumentPurchaseHeader.FeatureName()); + FinalizeEDocument(EDocImportParameters); + end; + } + action(LinkToExistingDocument) + { + ApplicationArea = Basic, Suite; + Caption = 'Link to existing document'; + ToolTip = 'Link this electronic document to an existing purchase document in Business Central. Use this when you have already created the document manually and want to attach the e-document to it.'; + Image = Links; + + trigger OnAction() + begin + DoLinkToExistingDocument(); + end; + } + action(ResetDraftDocument) + { + ApplicationArea = Basic, Suite; + Caption = 'Reset draft'; + ToolTip = 'Resets the draft document. Any changes made to the draft document will be lost.'; + Image = Restore; + + trigger OnAction() + begin + ResetDraft(); + end; + } + action(AnalyzeDocument) + { + ApplicationArea = Basic, Suite; + Caption = 'Analyze document'; + ToolTip = 'Analyze the selected electronic document'; + Image = SendAsPDF; + Visible = ShowAnalyzeDocumentAction; + + trigger OnAction() + begin + AnalyzeEDocument(); + end; + } } - action(ViewExtractedDocumentData) + group(ViewDocument) { - ApplicationArea = Basic, Suite; - Caption = 'View extracted data'; - ToolTip = 'View the extracted data from the source file.'; - Image = ViewRegisteredOrder; - - trigger OnAction() - var - EDocImport: Codeunit "E-Doc. Import"; - begin - EDocImport.ViewExtractedData(Rec); - end; + Caption = 'View'; + Image = View; + + action(ViewFile) + { + ApplicationArea = Basic, Suite; + Caption = 'View pdf'; + ToolTip = 'View pdf.'; + Image = ViewDetails; + Visible = HasPDFSource; + + trigger OnAction() + begin + Rec.ViewSourceFile(); + end; + } + action(ViewExtractedDocumentData) + { + ApplicationArea = Basic, Suite; + Caption = 'View extracted data'; + ToolTip = 'View the extracted data from the source file.'; + Image = ViewRegisteredOrder; + + trigger OnAction() + var + EDocImport: Codeunit "E-Doc. Import"; + begin + EDocImport.ViewExtractedData(Rec); + end; + } } - action(GetFeedback) + group(Help) { - ApplicationArea = Basic, Suite; - Caption = 'Provide feedback'; - ToolTip = 'Provide feedback on the Payables Agent experience.'; + Caption = 'Help'; Image = Help; - trigger OnAction() - begin - ProvideFeedback(); - end; + action(GetFeedback) + { + ApplicationArea = Basic, Suite; + Caption = 'Provide feedback'; + ToolTip = 'Provide feedback on the Payables Agent experience.'; + Image = Help; + + trigger OnAction() + begin + ProvideFeedback(); + end; + } } } area(Navigation) @@ -388,19 +422,32 @@ page 6181 "E-Document Purchase Draft" } } } + area(Promoted) { group(Category_Process) { + Caption = 'Process'; actionref(Promoted_CreateDocument; CreateDocument) { } actionref(Promoted_AnalyseDocument; AnalyzeDocument) { } + } + group(Category_View) + { + Caption = 'View'; actionref(Promoted_ViewFile; ViewFile) { } + actionref(Promoted_ViewExtractedData; ViewExtractedDocumentData) + { + } + } + group(Category_Help) + { + Caption = 'Help'; actionref(Promoted_GetFeedback; GetFeedback) { } @@ -534,18 +581,15 @@ page 6181 "E-Document Purchase Draft" CurrPage.ErrorMessagesFactBox.Page.Update(false); end; - local procedure FinalizeEDocument() + local procedure FinalizeEDocument(EDocImportParameters: Record "E-Doc. Import Parameters") var TempErrorMessage: Record "Error Message" temporary; ErrorMessage: Record "Error Message"; - EDocImportParameters: Record "E-Doc. Import Parameters"; EDocImport: Codeunit "E-Doc. Import"; EDocImpSessionTelemetry: Codeunit "E-Doc. Imp. Session Telemetry"; Telemetry: Codeunit Telemetry; CustomDimensions: Dictionary of [Text, Text]; begin - Session.LogMessage('0000PCO', FinalizeDraftInvokedTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::All, 'Category', EDocumentPurchaseHeader.FeatureName()); - if not GlobalEDocumentHelper.EnsureInboundEDocumentHasService(Rec) then exit; @@ -659,6 +703,23 @@ page 6181 "E-Document Purchase Draft" end; end; + local procedure DoLinkToExistingDocument() + var + PurchaseHeader: Record "Purchase Header"; + EDocImportParameters: Record "E-Doc. Import Parameters"; + ConfirmDialogMgt: Codeunit "Confirm Management"; + LinkToExistingDocumentQst: Label 'Do you want to link this e-document to %1 %2? This will mark the e-document as processed.', Comment = '%1 = Document Type, %2 = Document No.'; + begin + if not EDocumentProcessing.OpenPurchaseDocumentList(Rec."Document Type", PurchaseHeader) then + exit; + + if not ConfirmDialogMgt.GetResponseOrDefault(StrSubstNo(LinkToExistingDocumentQst, PurchaseHeader."Document Type", PurchaseHeader."No."), true) then + exit; + + EDocImportParameters."Link To Existing Doc. Rec. ID" := PurchaseHeader.RecordId(); + FinalizeEDocument(EDocImportParameters); + end; + var EDocumentPurchaseHeader: Record "E-Document Purchase Header"; EDocumentServiceStatus: Record "E-Document Service Status"; diff --git a/src/Apps/W1/EDocument/Test/src/Processing/EDocProcessTest.Codeunit.al b/src/Apps/W1/EDocument/Test/src/Processing/EDocProcessTest.Codeunit.al index 25d665dc35..5752a618cf 100644 --- a/src/Apps/W1/EDocument/Test/src/Processing/EDocProcessTest.Codeunit.al +++ b/src/Apps/W1/EDocument/Test/src/Processing/EDocProcessTest.Codeunit.al @@ -332,6 +332,230 @@ codeunit 139883 "E-Doc Process Test" Assert.RecordIsEmpty(PurchaseHeader); end; + #region LinkToExistingDocument + + [Test] + procedure LinkToExistingDocumentSetsEDocumentLink() + var + EDocument: Record "E-Document"; + EDocImportParameters: Record "E-Doc. Import Parameters"; + ExistingPurchaseHeader: Record "Purchase Header"; + LinkedPurchaseHeader: Record "Purchase Header"; + EDocLogRecord: Record "E-Document Log"; + EDocImport: Codeunit "E-Doc. Import"; + EDocumentLog: Codeunit "E-Document Log"; + EDocumentProcessing: Codeunit "E-Document Processing"; + begin + // [SCENARIO] When linking an e-document to an existing purchase document, the E-Document Link is set on the purchase document + Initialize(Enum::"Service Integration"::"Mock"); + + // [GIVEN] An inbound e-document in Draft Ready state + LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); + EDocument."Document Type" := "E-Document Type"::"Purchase Invoice"; + EDocument.Modify(); + EDocumentService."Import Process" := "E-Document Import Process"::"Version 2.0"; + EDocumentService.Modify(); + + EDocumentLog.SetBlob('Test', Enum::"E-Doc. File Format"::XML, 'Data'); + EDocumentLog.SetFields(EDocument, EDocumentService); + EDocLogRecord := EDocumentLog.InsertLog(Enum::"E-Document Service Status"::Imported, Enum::"Import E-Doc. Proc. Status"::Readable); + EDocument."Structured Data Entry No." := EDocLogRecord."E-Doc. Data Storage Entry No."; + EDocument.Modify(); + EDocumentProcessing.ModifyEDocumentProcessingStatus(EDocument, "Import E-Doc. Proc. Status"::"Draft Ready"); + + // [GIVEN] An existing purchase invoice + ExistingPurchaseHeader."No." := 'EXISTING-001'; + ExistingPurchaseHeader."Document Type" := "Purchase Document Type"::Invoice; + ExistingPurchaseHeader.Insert(); + + // [WHEN] Finishing draft with Link To Existing Doc. Rec. ID set + EDocImportParameters."Step to Run" := "Import E-Document Steps"::"Finish draft"; + EDocImportParameters."Link To Existing Doc. Rec. ID" := ExistingPurchaseHeader.RecordId(); + EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParameters); + + // [THEN] The existing purchase document has E-Document Link set + LinkedPurchaseHeader.SetRange("E-Document Link", EDocument.SystemId); + Assert.RecordCount(LinkedPurchaseHeader, 1); + LinkedPurchaseHeader.FindFirst(); + Assert.AreEqual(ExistingPurchaseHeader."No.", LinkedPurchaseHeader."No.", 'The linked document should be the existing one'); + + // Cleanup + ExistingPurchaseHeader.Delete(); + end; + + [Test] + procedure LinkToExistingDocumentDoesNotCreateNewPurchaseInvoice() + var + EDocument: Record "E-Document"; + EDocImportParameters: Record "E-Doc. Import Parameters"; + ExistingPurchaseHeader: Record "Purchase Header"; + AllPurchaseHeaders: Record "Purchase Header"; + EDocLogRecord: Record "E-Document Log"; + EDocImport: Codeunit "E-Doc. Import"; + EDocumentLog: Codeunit "E-Document Log"; + EDocumentProcessing: Codeunit "E-Document Processing"; + InitialCount: Integer; + begin + // [SCENARIO] When linking an e-document to an existing purchase document, no new purchase invoice is created + Initialize(Enum::"Service Integration"::"Mock"); + + // [GIVEN] An inbound e-document in Draft Ready state + LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); + EDocument."Document Type" := "E-Document Type"::"Purchase Invoice"; + EDocument.Modify(); + EDocumentService."Import Process" := "E-Document Import Process"::"Version 2.0"; + EDocumentService.Modify(); + + EDocumentLog.SetBlob('Test', Enum::"E-Doc. File Format"::XML, 'Data'); + EDocumentLog.SetFields(EDocument, EDocumentService); + EDocLogRecord := EDocumentLog.InsertLog(Enum::"E-Document Service Status"::Imported, Enum::"Import E-Doc. Proc. Status"::Readable); + EDocument."Structured Data Entry No." := EDocLogRecord."E-Doc. Data Storage Entry No."; + EDocument.Modify(); + EDocumentProcessing.ModifyEDocumentProcessingStatus(EDocument, "Import E-Doc. Proc. Status"::"Draft Ready"); + + // [GIVEN] An existing purchase invoice and count of all purchase headers + ExistingPurchaseHeader."No." := 'EXISTING-002'; + ExistingPurchaseHeader."Document Type" := "Purchase Document Type"::Invoice; + ExistingPurchaseHeader.Insert(); + InitialCount := AllPurchaseHeaders.Count(); + + // [WHEN] Finishing draft with Link To Existing Doc. Rec. ID set + EDocImportParameters."Step to Run" := "Import E-Document Steps"::"Finish draft"; + EDocImportParameters."Link To Existing Doc. Rec. ID" := ExistingPurchaseHeader.RecordId(); + EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParameters); + + // [THEN] No new purchase header was created + Assert.AreEqual(InitialCount, AllPurchaseHeaders.Count(), 'No new purchase document should be created when linking to existing'); + + // Cleanup + ExistingPurchaseHeader.Delete(); + end; + + [Test] + procedure LinkToExistingDocumentMarksEDocumentAsProcessed() + var + EDocument: Record "E-Document"; + EDocImportParameters: Record "E-Doc. Import Parameters"; + ExistingPurchaseHeader: Record "Purchase Header"; + EDocLogRecord: Record "E-Document Log"; + EDocImport: Codeunit "E-Doc. Import"; + EDocumentLog: Codeunit "E-Document Log"; + EDocumentProcessing: Codeunit "E-Document Processing"; + begin + // [SCENARIO] When linking an e-document to an existing purchase document, the e-document is marked as processed + Initialize(Enum::"Service Integration"::"Mock"); + + // [GIVEN] An inbound e-document in Draft Ready state + LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); + EDocument."Document Type" := "E-Document Type"::"Purchase Invoice"; + EDocument.Modify(); + EDocumentService."Import Process" := "E-Document Import Process"::"Version 2.0"; + EDocumentService.Modify(); + + EDocumentLog.SetBlob('Test', Enum::"E-Doc. File Format"::XML, 'Data'); + EDocumentLog.SetFields(EDocument, EDocumentService); + EDocLogRecord := EDocumentLog.InsertLog(Enum::"E-Document Service Status"::Imported, Enum::"Import E-Doc. Proc. Status"::Readable); + EDocument."Structured Data Entry No." := EDocLogRecord."E-Doc. Data Storage Entry No."; + EDocument.Modify(); + EDocumentProcessing.ModifyEDocumentProcessingStatus(EDocument, "Import E-Doc. Proc. Status"::"Draft Ready"); + + // [GIVEN] An existing purchase invoice + ExistingPurchaseHeader."No." := 'EXISTING-003'; + ExistingPurchaseHeader."Document Type" := "Purchase Document Type"::Invoice; + ExistingPurchaseHeader.Insert(); + + // [WHEN] Finishing draft with Link To Existing Doc. Rec. ID set + EDocImportParameters."Step to Run" := "Import E-Document Steps"::"Finish draft"; + EDocImportParameters."Link To Existing Doc. Rec. ID" := ExistingPurchaseHeader.RecordId(); + EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParameters); + + // [THEN] The e-document import processing status is Processed + EDocument.CalcFields("Import Processing Status"); + Assert.AreEqual(Enum::"Import E-Doc. Proc. Status"::Processed, EDocument."Import Processing Status", 'The e-document should be marked as processed after linking'); + + // Cleanup + ExistingPurchaseHeader.Delete(); + end; + + [Test] + procedure LinkToExistingDocumentUpdatesDocumentAmounts() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocImportParameters: Record "E-Doc. Import Parameters"; + ExistingPurchaseHeader: Record "Purchase Header"; + EDocLogRecord: Record "E-Document Log"; + EDocImport: Codeunit "E-Doc. Import"; + EDocumentLog: Codeunit "E-Document Log"; + EDocumentProcessing: Codeunit "E-Document Processing"; + begin + // [SCENARIO] When linking an e-document to an existing purchase document, document amounts are updated on the purchase document + Initialize(Enum::"Service Integration"::"Mock"); + + // [GIVEN] An inbound e-document in Draft Ready state with total amounts + LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); + EDocument."Document Type" := "E-Document Type"::"Purchase Invoice"; + EDocument.Modify(); + EDocumentService."Import Process" := "E-Document Import Process"::"Version 2.0"; + EDocumentService.Modify(); + + EDocumentLog.SetBlob('Test', Enum::"E-Doc. File Format"::XML, 'Data'); + EDocumentLog.SetFields(EDocument, EDocumentService); + EDocLogRecord := EDocumentLog.InsertLog(Enum::"E-Document Service Status"::Imported, Enum::"Import E-Doc. Proc. Status"::Readable); + EDocument."Structured Data Entry No." := EDocLogRecord."E-Doc. Data Storage Entry No."; + EDocument.Modify(); + EDocumentProcessing.ModifyEDocumentProcessingStatus(EDocument, "Import E-Doc. Proc. Status"::"Draft Ready"); + + // Set up e-document purchase header with amounts + EDocumentPurchaseHeader."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseHeader.Total := 1000; + EDocumentPurchaseHeader."Total VAT" := 200; + if not EDocumentPurchaseHeader.Insert() then + EDocumentPurchaseHeader.Modify(); + + // [GIVEN] An existing purchase invoice + ExistingPurchaseHeader."No." := 'EXISTING-004'; + ExistingPurchaseHeader."Document Type" := "Purchase Document Type"::Invoice; + ExistingPurchaseHeader.Insert(); + + // [WHEN] Finishing draft with Link To Existing Doc. Rec. ID set + EDocImportParameters."Step to Run" := "Import E-Document Steps"::"Finish draft"; + EDocImportParameters."Link To Existing Doc. Rec. ID" := ExistingPurchaseHeader.RecordId(); + EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParameters); + + // [THEN] The purchase document has the e-document amounts + ExistingPurchaseHeader.Get(ExistingPurchaseHeader."Document Type", ExistingPurchaseHeader."No."); + Assert.AreEqual(1000, ExistingPurchaseHeader."Doc. Amount Incl. VAT", 'The document amount incl. VAT should be set from e-document'); + Assert.AreEqual(200, ExistingPurchaseHeader."Doc. Amount VAT", 'The document VAT amount should be set from e-document'); + + // Cleanup + ExistingPurchaseHeader.Delete(); + end; + + [Test] + procedure GetPurchaseDocTypeFilterReturnsCorrectType() + var + EDocumentProcessing: Codeunit "E-Document Processing"; + PurchaseDocType: Enum "Purchase Document Type"; + begin + // [SCENARIO] GetPurchaseDocTypeFilter correctly maps E-Document types to Purchase Document types + Initialize(Enum::"Service Integration"::"Mock"); + + // [WHEN/THEN] Purchase Invoice maps to Invoice + PurchaseDocType := EDocumentProcessing.GetPurchaseDocTypeFilter(Enum::"E-Document Type"::"Purchase Invoice"); + Assert.AreEqual(Enum::"Purchase Document Type"::Invoice, PurchaseDocType, 'Purchase Invoice should map to Invoice'); + + // [WHEN/THEN] Purchase Credit Memo maps to Credit Memo + PurchaseDocType := EDocumentProcessing.GetPurchaseDocTypeFilter(Enum::"E-Document Type"::"Purchase Credit Memo"); + Assert.AreEqual(Enum::"Purchase Document Type"::"Credit Memo", PurchaseDocType, 'Purchase Credit Memo should map to Credit Memo'); + + // [WHEN/THEN] Purchase Order maps to Order + PurchaseDocType := EDocumentProcessing.GetPurchaseDocTypeFilter(Enum::"E-Document Type"::"Purchase Order"); + Assert.AreEqual(Enum::"Purchase Document Type"::Order, PurchaseDocType, 'Purchase Order should map to Order'); + end; + + #endregion + #region HistoricalMatchingTest [Test] From 756b59762d82a4d973ae19ee759c36f6ca2ec063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Wed, 21 Jan 2026 12:44:05 +0100 Subject: [PATCH 2/9] Minor changes --- .../Extensions/EDocPurchaseHeader.TableExt.al | 20 ++++++++++++++++++- .../EDocumentProcessing.Codeunit.al | 14 ++++++++++++- .../Import/EDocImportParameters.Table.al | 2 +- .../EDocCreatePurchaseInvoice.Codeunit.al | 6 +++--- .../Purchase/EDocumentPurchaseDraft.Page.al | 5 ++++- 5 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchaseHeader.TableExt.al b/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchaseHeader.TableExt.al index 9c879f406c..976631f7b1 100644 --- a/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchaseHeader.TableExt.al +++ b/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchaseHeader.TableExt.al @@ -1,4 +1,12 @@ -#pragma warning disable AA0247 +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.Purchases.Document; + +using Microsoft.eServices.EDocument; +using Microsoft.Utilities; + tableextension 6169 "E-Doc. Purchase Header" extends "Purchase Header" { @@ -32,6 +40,16 @@ tableextension 6169 "E-Doc. Purchase Header" extends "Purchase Header" } } + internal procedure CalculateTotalAmountInclVAT(): Decimal + var + TotalPurchaseLine: Record "Purchase Line"; + DocumentTotals: Codeunit "Document Totals"; + VATAmount: Decimal; + begin + DocumentTotals.CalculatePurchaseTotals(TotalPurchaseLine, VATAmount, PurchLine); + exit(TotalPurchaseLine."Amount Including VAT"); + end; + internal procedure IsLinkedToEDoc(EDocumentToExclude: Record "E-Document"): Boolean begin exit(not IsNullGuid("E-Document Link") and ("E-Document Link" <> EDocumentToExclude.SystemId)); 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 e4b401ad63..82cee508da 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/EDocumentProcessing.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/EDocumentProcessing.Codeunit.al @@ -5,6 +5,7 @@ namespace Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Processing.Import; +using Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.Finance.GeneralLedger.Journal; using Microsoft.Finance.GeneralLedger.Ledger; using Microsoft.Foundation.Reporting; @@ -709,11 +710,22 @@ codeunit 6108 "E-Document Processing" if not ConfirmDialogMgt.GetResponseOrDefault(StrSubstNo(LinkToExistingDocumentQst, PurchaseHeader."Document Type", PurchaseHeader."No."), true) then exit(false); - EDocImportParameters."Link To Existing Doc. Rec. ID" := PurchaseHeader.RecordId(); + EDocImportParameters."Existing Doc. RecordId" := PurchaseHeader.RecordId(); EDocImportParameters."Step to Run" := "Import E-Document Steps"::"Finish draft"; exit(EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParameters)); end; + internal procedure ErrorIfNotAllowedToLinkToExistingDoc(EDocument: Record "E-Document"; EDocumentPurchaseHeader: Record "E-Document Purchase Header") + var + Vendor: Record Vendor; + NoVendorErr: Label 'Cannot link e-document to existing purchase document because vendor number is missing in e-document purchase header.'; + begin + if EDocumentPurchaseHeader."[BC] Vendor No." = '' then + Error(NoVendorErr); + if Vendor.Get(EDocumentPurchaseHeader."[BC] Vendor No.") then + Vendor.TestField("IC Partner Code"); + end; + procedure OpenPurchaseDocumentList(EDocumentType: Enum "E-Document Type"; var PurchaseHeader: Record "Purchase Header"): Boolean var PurchaseInvoices: Page "Purchase Invoices"; diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/EDocImportParameters.Table.al b/src/Apps/W1/EDocument/App/src/Processing/Import/EDocImportParameters.Table.al index d98986e679..dcfbcf8264 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/EDocImportParameters.Table.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/EDocImportParameters.Table.al @@ -62,7 +62,7 @@ table 6106 "E-Doc. Import Parameters" /// Specifies an existing purchase document to link to instead of creating a new one. /// When set, the ApplyDraftToBC step will link the e-document to this existing document rather than creating a new purchase invoice. /// - field(10; "Link To Existing Doc. Rec. ID"; RecordId) + field(10; "Existing Doc. RecordId"; RecordId) { } } diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al index bd0c579e5a..b9da0a9e64 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al @@ -55,9 +55,9 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft, Error(SomeLinesNotYetReceivedErr); IEDocumentFinishPurchaseDraft := EDocImportParameters."Processing Customizations"; - if EDocImportParameters."Link To Existing Doc. Rec. ID" <> EmptyRecordId then begin + if EDocImportParameters."Existing Doc. RecordId" <> EmptyRecordId then begin EDocImpSessionTelemetry.SetBool('LinkedToExisting', true); - PurchaseHeader.Get(EDocImportParameters."Link To Existing Doc. Rec. ID"); + PurchaseHeader.Get(EDocImportParameters."Existing Doc. RecordId"); end else PurchaseHeader := IEDocumentFinishPurchaseDraft.CreatePurchaseInvoice(EDocument); @@ -68,7 +68,7 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft, PurchaseHeader."Doc. Amount VAT" := EDocumentPurchaseHeader."Total VAT"; PurchaseHeader.TestField("No."); PurchaseHeader."E-Document Link" := EDocument.SystemId; - PurchaseHeader."Created from E-Document" := EDocImportParameters."Link To Existing Doc. Rec. ID" = EmptyRecordId; + PurchaseHeader."Created from E-Document" := EDocImportParameters."Existing Doc. RecordId" = EmptyRecordId; PurchaseHeader.Modify(); // Post document creation diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al index e22c22d61d..f1179d2ff0 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al @@ -716,13 +716,16 @@ page 6181 "E-Document Purchase Draft" ConfirmDialogMgt: Codeunit "Confirm Management"; LinkToExistingDocumentQst: Label 'Do you want to link this e-document to %1 %2? This will mark the e-document as processed.', Comment = '%1 = Document Type, %2 = Document No.'; begin + EDocumentProcessing.ErrorIfNotAllowedToLinkToExistingDoc(Rec, EDocumentPurchaseHeader); + PurchaseHeader.SetRange("Buy-from Vendor No.", EDocumentPurchaseHeader."[BC] Vendor No."); + PurchaseHeader.SetRange("Doc. Amount Incl. VAT", EDocumentPurchaseHeader.Total); if not EDocumentProcessing.OpenPurchaseDocumentList(Rec."Document Type", PurchaseHeader) then exit; if not ConfirmDialogMgt.GetResponseOrDefault(StrSubstNo(LinkToExistingDocumentQst, PurchaseHeader."Document Type", PurchaseHeader."No."), true) then exit; - EDocImportParameters."Link To Existing Doc. Rec. ID" := PurchaseHeader.RecordId(); + EDocImportParameters."Existing Doc. RecordId" := PurchaseHeader.RecordId(); FinalizeEDocument(EDocImportParameters); end; From 68cea97ced4aeadd8b7931d2e4ec99aef4556c0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Wed, 21 Jan 2026 14:53:01 +0100 Subject: [PATCH 3/9] Tests. --- .../EDocCreatePurchaseInvoice.Codeunit.al | 4 +- .../Purchase/EDocumentPurchaseDraft.Page.al | 7 +- .../Test/src/LibraryEDocument.Codeunit.al | 2 + .../EDocLinkToExistingTest.Codeunit.al | 604 ++++++++++++++++++ .../Processing/EDocProcessTest.Codeunit.al | 199 ------ 5 files changed, 612 insertions(+), 204 deletions(-) create mode 100644 src/Apps/W1/EDocument/Test/src/Processing/EDocLinkToExistingTest.Codeunit.al diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al index b9da0a9e64..9df72b6437 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al @@ -97,9 +97,7 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft, PurchaseHeader.TestField("Document Type", "Purchase Document Type"::Invoice); Clear(PurchaseHeader."E-Document Link"); - - if PurchaseHeader."Created from E-Document" then - PurchaseHeader.Delete(true); + PurchaseHeader.Modify(); end; procedure CreatePurchaseInvoice(EDocument: Record "E-Document"): Record "Purchase Header" diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al index f1179d2ff0..8089f1a823 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al @@ -714,7 +714,9 @@ page 6181 "E-Document Purchase Draft" PurchaseHeader: Record "Purchase Header"; EDocImportParameters: Record "E-Doc. Import Parameters"; ConfirmDialogMgt: Codeunit "Confirm Management"; - LinkToExistingDocumentQst: Label 'Do you want to link this e-document to %1 %2? This will mark the e-document as processed.', Comment = '%1 = Document Type, %2 = Document No.'; + LinkToExistingDocumentQst: Label 'Do you want to link this e-document to %1 %2?', Comment = '%1 = Document Type, %2 = Document No.'; + RelinkToExistingDocumentQst: Label 'This e-document is already linked to a document. Do you want to unlink the existing document and link to %1 %2 instead? The previously linked document will need to be cleaned up manually.', Comment = '%1 = Document Type, %2 = Document No.'; + ConfirmQst: Text; begin EDocumentProcessing.ErrorIfNotAllowedToLinkToExistingDoc(Rec, EDocumentPurchaseHeader); PurchaseHeader.SetRange("Buy-from Vendor No.", EDocumentPurchaseHeader."[BC] Vendor No."); @@ -722,7 +724,8 @@ page 6181 "E-Document Purchase Draft" if not EDocumentProcessing.OpenPurchaseDocumentList(Rec."Document Type", PurchaseHeader) then exit; - if not ConfirmDialogMgt.GetResponseOrDefault(StrSubstNo(LinkToExistingDocumentQst, PurchaseHeader."Document Type", PurchaseHeader."No."), true) then + ConfirmQst := StrSubstNo(Rec.Status = Rec.Status::Processed ? RelinkToExistingDocumentQst : LinkToExistingDocumentQst, PurchaseHeader."Document Type", PurchaseHeader."No."); + if not ConfirmDialogMgt.GetResponseOrDefault(ConfirmQst, Rec.Status <> Rec.Status::Processed) then exit; EDocImportParameters."Existing Doc. RecordId" := PurchaseHeader.RecordId(); diff --git a/src/Apps/W1/EDocument/Test/src/LibraryEDocument.Codeunit.al b/src/Apps/W1/EDocument/Test/src/LibraryEDocument.Codeunit.al index af223e470b..047fc89157 100644 --- a/src/Apps/W1/EDocument/Test/src/LibraryEDocument.Codeunit.al +++ b/src/Apps/W1/EDocument/Test/src/LibraryEDocument.Codeunit.al @@ -253,6 +253,8 @@ codeunit 139629 "Library - E-Document" else EntryNo := EDocument."Entry No"; EDocument."Entry No" := EntryNo; + EDocument.Direction := EDocument.Direction::Incoming; + EDocument.Service := EDocService.Code; EDocument.Insert(); EDocumentServiceStatus."E-Document Entry No" := EntryNo; EDocumentServiceStatus."E-Document Service Code" := EDocService.Code; diff --git a/src/Apps/W1/EDocument/Test/src/Processing/EDocLinkToExistingTest.Codeunit.al b/src/Apps/W1/EDocument/Test/src/Processing/EDocLinkToExistingTest.Codeunit.al new file mode 100644 index 0000000000..d2412b0528 --- /dev/null +++ b/src/Apps/W1/EDocument/Test/src/Processing/EDocLinkToExistingTest.Codeunit.al @@ -0,0 +1,604 @@ +// ------------------------------------------------------------------------------------------------ +// 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.Integration; +using Microsoft.eServices.EDocument.Processing.Import; +using Microsoft.eServices.EDocument.Processing.Import.Purchase; +using Microsoft.Intercompany.Partner; +using Microsoft.Purchases.Document; +using Microsoft.Purchases.Setup; +using Microsoft.Purchases.Vendor; +using System.TestLibraries.Utilities; + + +codeunit 139886 "E-Doc Link To Existing Test" +{ + Subtype = Test; + TestType = IntegrationTest; + + #region Validation Tests + + [Test] + procedure LinkToExisting_ErrorWhenVendorNoIsMissing() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocPurchaseDraftTestPage: TestPage "E-Document Purchase Draft"; + NoVendorErr: Label 'Cannot link e-document to existing purchase document because vendor number is missing in e-document purchase header.'; + begin + // [SCENARIO] Should error when EDocumentPurchaseHeader."[BC] Vendor No." is empty + Initialize(); + + // [GIVEN] An inbound e-document with draft prepared but no vendor linked + LibraryEDocument.CreateInboundEDocument(EDocument, EDocumentService); + EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); + EDocumentPurchaseHeader."[BC] Vendor No." := ''; + EDocumentPurchaseHeader.Modify(); + + // [WHEN] Opening the draft page and clicking Link to Existing Document + EDocPurchaseDraftTestPage.OpenEdit(); + EDocPurchaseDraftTestPage.GoToRecord(EDocument); + + // [THEN] An error is thrown indicating vendor number is missing + asserterror EDocPurchaseDraftTestPage.LinkToExistingDocument.Invoke(); + Assert.ExpectedError(NoVendorErr); + end; + + [Test] + procedure LinkToExisting_ErrorWhenVendorHasNoICPartnerCode() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocPurchaseDraftTestPage: TestPage "E-Document Purchase Draft"; + begin + // [SCENARIO] Should error when vendor exists but IC Partner Code is blank + Initialize(); + + // [GIVEN] An inbound e-document with draft prepared and vendor linked, but vendor has no IC Partner Code + LibraryEDocument.CreateInboundEDocument(EDocument, EDocumentService); + EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); + EDocumentPurchaseHeader."[BC] Vendor No." := Vendor."No."; + EDocumentPurchaseHeader.Modify(); + + // [GIVEN] The vendor has no IC Partner Code + Vendor."IC Partner Code" := ''; + Vendor.Modify(); + + // [WHEN] Opening the draft page and clicking Link to Existing Document + EDocPurchaseDraftTestPage.OpenEdit(); + EDocPurchaseDraftTestPage.GoToRecord(EDocument); + + // [THEN] An error is thrown indicating IC Partner Code must have a value + asserterror EDocPurchaseDraftTestPage.LinkToExistingDocument.Invoke(); + Assert.ExpectedErrorCode('TestField'); + Assert.ExpectedError('IC Partner Code'); + end; + + [Test] + [HandlerFunctions('PurchaseInvoicesModalPageHandler,ConfirmHandler,PIModalPageHandler')] + procedure LinkToExisting_SuccessWhenVendorHasICPartnerCode() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + PurchaseHeader: Record "Purchase Header"; + ICPartner: Record "IC Partner"; + EDocPurchaseDraftTestPage: TestPage "E-Document Purchase Draft"; + begin + // [SCENARIO] Should succeed when vendor has IC Partner Code set + Initialize(); + SetupICPartner(ICPartner); + + // [GIVEN] An inbound e-document with draft prepared and vendor linked + LibraryEDocument.CreateInboundEDocument(EDocument, EDocumentService); + EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); + EDocumentPurchaseHeader."[BC] Vendor No." := Vendor."No."; + EDocumentPurchaseHeader.Total := 1000; + EDocumentPurchaseHeader.Modify(); + + // [GIVEN] An existing purchase invoice from the same vendor + LibraryPurchase.CreatePurchHeader(PurchaseHeader, PurchaseHeader."Document Type"::Invoice, Vendor."No."); + PurchaseHeader."Doc. Amount Incl. VAT" := 1000; + PurchaseHeader.Modify(); + LibraryVariableStorage.Enqueue(true); // Signal handler to select + LibraryVariableStorage.Enqueue(PurchaseHeader."No."); // For modal handler + + // [WHEN] Opening the draft page and clicking Link to Existing Document + EDocPurchaseDraftTestPage.OpenEdit(); + EDocPurchaseDraftTestPage.GoToRecord(EDocument); + EDocPurchaseDraftTestPage.LinkToExistingDocument.Invoke(); + + // [THEN] The e-document is linked to the existing purchase document + PurchaseHeader.Get(PurchaseHeader."Document Type", PurchaseHeader."No."); + Assert.AreEqual(EDocument.SystemId, PurchaseHeader."E-Document Link", 'E-Document Link should be set on purchase header'); + + // [THEN] The e-document is marked as processed + EDocument.Get(EDocument."Entry No"); + Assert.AreEqual(Enum::"E-Document Status"::Processed, EDocument.Status, 'E-Document should be marked as processed'); + end; + + + #endregion + + #region Document Selection Tests + + [Test] + [HandlerFunctions('PurchaseInvoicesModalPageHandler')] + procedure LinkToExisting_OpensInvoiceListForPurchaseInvoice() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + ICPartner: Record "IC Partner"; + EDocPurchaseDraftTestPage: TestPage "E-Document Purchase Draft"; + begin + // [SCENARIO] When e-document type is Purchase Invoice, the Purchase Invoices page opens + Initialize(); + SetupICPartner(ICPartner); + + // [GIVEN] An inbound e-document of type Purchase Invoice + LibraryEDocument.CreateInboundEDocument(EDocument, EDocumentService); + EDocument."Document Type" := Enum::"E-Document Type"::"Purchase Invoice"; + EDocument.Modify(); + EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); + EDocumentPurchaseHeader."[BC] Vendor No." := Vendor."No."; + EDocumentPurchaseHeader.Modify(); + + // [WHEN] Opening the draft page and clicking Link to Existing Document + EDocPurchaseDraftTestPage.OpenEdit(); + EDocPurchaseDraftTestPage.GoToRecord(EDocument); + LibraryVariableStorage.Enqueue(false); // Signal handler to cancel (no document to select) + EDocPurchaseDraftTestPage.LinkToExistingDocument.Invoke(); + + // [THEN] The Purchase Invoices page is opened (verified by handler being called) + // Handler is called - test passes if no error about missing handler + end; + + [Test] + [HandlerFunctions('PurchaseCreditMemosModalPageHandler')] + procedure LinkToExisting_OpensCreditMemoListForPurchaseCreditMemo() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + ICPartner: Record "IC Partner"; + EDocPurchaseDraftTestPage: TestPage "E-Document Purchase Draft"; + begin + // [SCENARIO] When e-document type is Purchase Credit Memo, the Purchase Credit Memos page opens + Initialize(); + SetupICPartner(ICPartner); + + // [GIVEN] An inbound e-document of type Purchase Credit Memo + LibraryEDocument.CreateInboundEDocument(EDocument, EDocumentService); + EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); + EDocument."Document Type" := Enum::"E-Document Type"::"Purchase Credit Memo"; + EDocument.Modify(); + EDocumentPurchaseHeader."[BC] Vendor No." := Vendor."No."; + EDocumentPurchaseHeader.Modify(); + + // [WHEN] Opening the draft page and clicking Link to Existing Document + EDocPurchaseDraftTestPage.OpenEdit(); + EDocPurchaseDraftTestPage.GoToRecord(EDocument); + EDocPurchaseDraftTestPage.LinkToExistingDocument.Invoke(); + + // [THEN] The Purchase Credit Memos page is opened (verified by handler being called) + // Handler is called - test passes if no error about missing handler + end; + + [Test] + [HandlerFunctions('PurchaseInvoicesVerifyVendorFilterHandler')] + procedure LinkToExisting_FiltersDocumentsByVendor() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + PurchaseHeaderMatchingVendor: Record "Purchase Header"; + PurchaseHeaderOtherVendor: Record "Purchase Header"; + OtherVendor: Record Vendor; + ICPartner: Record "IC Partner"; + EDocPurchaseDraftTestPage: TestPage "E-Document Purchase Draft"; + begin + // [SCENARIO] The document list should be pre-filtered by vendor no. + Initialize(); + SetupICPartner(ICPartner); + + // [GIVEN] An inbound e-document with vendor linked + LibraryEDocument.CreateInboundEDocument(EDocument, EDocumentService); + EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); + EDocumentPurchaseHeader."[BC] Vendor No." := Vendor."No."; + EDocumentPurchaseHeader.Total := 1000; + EDocumentPurchaseHeader.Modify(); + + // [GIVEN] A purchase invoice from the matching vendor + LibraryPurchase.CreatePurchHeader(PurchaseHeaderMatchingVendor, PurchaseHeaderMatchingVendor."Document Type"::Invoice, Vendor."No."); + PurchaseHeaderMatchingVendor."Doc. Amount Incl. VAT" := 1000; + PurchaseHeaderMatchingVendor.Modify(); + + // [GIVEN] A purchase invoice from a different vendor + LibraryPurchase.CreateVendor(OtherVendor); + LibraryPurchase.CreatePurchHeader(PurchaseHeaderOtherVendor, PurchaseHeaderOtherVendor."Document Type"::Invoice, OtherVendor."No."); + PurchaseHeaderOtherVendor."Doc. Amount Incl. VAT" := 1000; + PurchaseHeaderOtherVendor.Modify(); + + // [WHEN] Opening the draft page and clicking Link to Existing Document + EDocPurchaseDraftTestPage.OpenEdit(); + EDocPurchaseDraftTestPage.GoToRecord(EDocument); + LibraryVariableStorage.Enqueue(Vendor."No."); // Expected vendor filter + LibraryVariableStorage.Enqueue(PurchaseHeaderMatchingVendor."No."); // Should be visible + LibraryVariableStorage.Enqueue(PurchaseHeaderOtherVendor."No."); // Should NOT be visible + EDocPurchaseDraftTestPage.LinkToExistingDocument.Invoke(); + + // [THEN] The handler verifies the vendor filter is applied + end; + + [Test] + [HandlerFunctions('PurchaseInvoicesVerifyAmountFilterHandler')] + procedure LinkToExisting_FiltersDocumentsByAmount() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + PurchaseHeaderMatchingAmount: Record "Purchase Header"; + PurchaseHeaderOtherAmount: Record "Purchase Header"; + ICPartner: Record "IC Partner"; + EDocPurchaseDraftTestPage: TestPage "E-Document Purchase Draft"; + begin + // [SCENARIO] The document list should be pre-filtered by Doc. Amount Incl. VAT matching e-doc total + Initialize(); + SetupICPartner(ICPartner); + + // [GIVEN] An inbound e-document with total amount set + LibraryEDocument.CreateInboundEDocument(EDocument, EDocumentService); + EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); + EDocumentPurchaseHeader."[BC] Vendor No." := Vendor."No."; + EDocumentPurchaseHeader.Total := 1500; + EDocumentPurchaseHeader.Modify(); + + // [GIVEN] A purchase invoice with matching amount + LibraryPurchase.CreatePurchHeader(PurchaseHeaderMatchingAmount, PurchaseHeaderMatchingAmount."Document Type"::Invoice, Vendor."No."); + PurchaseHeaderMatchingAmount."Doc. Amount Incl. VAT" := 1500; + PurchaseHeaderMatchingAmount.Modify(); + + // [GIVEN] A purchase invoice with different amount + LibraryPurchase.CreatePurchHeader(PurchaseHeaderOtherAmount, PurchaseHeaderOtherAmount."Document Type"::Invoice, Vendor."No."); + PurchaseHeaderOtherAmount."Doc. Amount Incl. VAT" := 2000; + PurchaseHeaderOtherAmount.Modify(); + + // [WHEN] Opening the draft page and clicking Link to Existing Document + EDocPurchaseDraftTestPage.OpenEdit(); + EDocPurchaseDraftTestPage.GoToRecord(EDocument); + LibraryVariableStorage.Enqueue(1500); // Expected amount filter + LibraryVariableStorage.Enqueue(PurchaseHeaderMatchingAmount."No."); // Should be visible + LibraryVariableStorage.Enqueue(PurchaseHeaderOtherAmount."No."); // Should NOT be visible + EDocPurchaseDraftTestPage.LinkToExistingDocument.Invoke(); + + // [THEN] The handler verifies the amount filter is applied + end; + + #endregion + + #region Post-Link Behavior Tests + + [Test] + [HandlerFunctions('PurchaseInvoicesModalPageHandler,ConfirmHandler,PIModalPageHandler')] + procedure LinkToExisting_VerifyPostLinkBehavior() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + PurchaseHeader: Record "Purchase Header"; + AllPurchaseHeaders: Record "Purchase Header"; + ICPartner: Record "IC Partner"; + EDocPurchaseDraftTestPage: TestPage "E-Document Purchase Draft"; + InitialPurchaseHeaderCount: Integer; + begin + // [SCENARIO] Verify all post-link behaviors: + // - E-Document Link is set on purchase document + // - No new document is created + // - E-Document status becomes Processed + // - Doc amounts are transferred to purchase document + // - Created from E-Document is false (since it wasn't created from e-doc) + Initialize(); + SetupICPartner(ICPartner); + + // [GIVEN] An inbound e-document with draft prepared and specific amounts + LibraryEDocument.CreateInboundEDocument(EDocument, EDocumentService); + EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); + EDocumentPurchaseHeader."[BC] Vendor No." := Vendor."No."; + EDocumentPurchaseHeader.Total := 1500; + EDocumentPurchaseHeader."Total VAT" := 300; + EDocumentPurchaseHeader.Modify(); + + // [GIVEN] An existing purchase invoice from the same vendor + LibraryPurchase.CreatePurchHeader(PurchaseHeader, PurchaseHeader."Document Type"::Invoice, Vendor."No."); + PurchaseHeader."Doc. Amount Incl. VAT" := 1500; + PurchaseHeader.Modify(); + + // [GIVEN] Count of existing purchase headers + InitialPurchaseHeaderCount := AllPurchaseHeaders.Count(); + + LibraryVariableStorage.Enqueue(true); // Signal handler to select + LibraryVariableStorage.Enqueue(PurchaseHeader."No."); // For modal handler + + // [WHEN] Opening the draft page and clicking Link to Existing Document + EDocPurchaseDraftTestPage.OpenEdit(); + EDocPurchaseDraftTestPage.GoToRecord(EDocument); + EDocPurchaseDraftTestPage.LinkToExistingDocument.Invoke(); + + // [THEN] E-Document Link is set on purchase document + PurchaseHeader.Get(PurchaseHeader."Document Type", PurchaseHeader."No."); + Assert.AreEqual(EDocument.SystemId, PurchaseHeader."E-Document Link", 'E-Document Link should be set on purchase header'); + + // [THEN] No new purchase document was created + Assert.AreEqual(InitialPurchaseHeaderCount, AllPurchaseHeaders.Count(), 'No new purchase document should be created when linking to existing'); + + // [THEN] E-Document status becomes Processed + EDocument.Get(EDocument."Entry No"); + Assert.AreEqual(Enum::"E-Document Status"::Processed, EDocument.Status, 'E-Document should be marked as processed'); + + // [THEN] Doc amounts are transferred to purchase document + Assert.AreEqual(1500, PurchaseHeader."Doc. Amount Incl. VAT", 'Doc. Amount Incl. VAT should be set from e-document'); + Assert.AreEqual(300, PurchaseHeader."Doc. Amount VAT", 'Doc. Amount VAT should be set from e-document'); + + // [THEN] Created from E-Document is false (document existed before linking) + Assert.IsFalse(PurchaseHeader."Created from E-Document", 'Created from E-Document should be false since document was not created from e-doc'); + end; + + [Test] + [HandlerFunctions('PurchaseInvoicesModalPageHandler,ConfirmHandler,PIModalPageHandler')] + procedure LinkToExisting_RelinkToAnotherDocumentUnlinksFirst() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + PurchaseHeaderA: Record "Purchase Header"; + PurchaseHeaderB: Record "Purchase Header"; + ICPartner: Record "IC Partner"; + EDocPurchaseDraftTestPage: TestPage "E-Document Purchase Draft"; + EmptyGuid: Guid; + begin + // [SCENARIO] When relinking an e-document to a different document: + // - The first document (A) is unlinked + // - The second document (B) is linked + // - Neither document is deleted since they weren't created from e-doc + Initialize(); + SetupICPartner(ICPartner); + + // [GIVEN] An inbound e-document with draft prepared + LibraryEDocument.CreateInboundEDocument(EDocument, EDocumentService); + EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); + EDocumentPurchaseHeader."[BC] Vendor No." := Vendor."No."; + EDocumentPurchaseHeader.Total := 1000; + EDocumentPurchaseHeader."Total VAT" := 200; + EDocumentPurchaseHeader.Modify(); + + // [GIVEN] Two existing purchase invoices from the same vendor + LibraryPurchase.CreatePurchHeader(PurchaseHeaderA, PurchaseHeaderA."Document Type"::Invoice, Vendor."No."); + PurchaseHeaderA."Doc. Amount Incl. VAT" := 1000; + PurchaseHeaderA.Modify(); + + LibraryPurchase.CreatePurchHeader(PurchaseHeaderB, PurchaseHeaderB."Document Type"::Invoice, Vendor."No."); + PurchaseHeaderB."Doc. Amount Incl. VAT" := 1000; + PurchaseHeaderB.Modify(); + + // [WHEN] Linking to document A first + LibraryVariableStorage.Enqueue(true); + LibraryVariableStorage.Enqueue(PurchaseHeaderA."No."); + EDocPurchaseDraftTestPage.OpenEdit(); + EDocPurchaseDraftTestPage.GoToRecord(EDocument); + EDocPurchaseDraftTestPage.LinkToExistingDocument.Invoke(); + EDocPurchaseDraftTestPage.Close(); + + // [THEN] Document A is linked + PurchaseHeaderA.Get(PurchaseHeaderA."Document Type", PurchaseHeaderA."No."); + Assert.AreEqual(EDocument.SystemId, PurchaseHeaderA."E-Document Link", 'Document A should be linked to e-document'); + + // [WHEN] Relinking to document B + LibraryVariableStorage.Enqueue(true); + LibraryVariableStorage.Enqueue(PurchaseHeaderB."No."); + EDocPurchaseDraftTestPage.OpenEdit(); + EDocPurchaseDraftTestPage.GoToRecord(EDocument); + EDocPurchaseDraftTestPage.LinkToExistingDocument.Invoke(); + EDocPurchaseDraftTestPage.Close(); + + // [THEN] Document A is unlinked (E-Document Link is cleared) + PurchaseHeaderA.Get(PurchaseHeaderA."Document Type", PurchaseHeaderA."No."); + Assert.AreEqual(EmptyGuid, PurchaseHeaderA."E-Document Link", 'Document A should be unlinked after relinking to B'); + Assert.IsFalse(PurchaseHeaderA."Created from E-Document", 'Document A Created from E-Document should remain false'); + + // [THEN] Document B is now linked + PurchaseHeaderB.Get(PurchaseHeaderB."Document Type", PurchaseHeaderB."No."); + Assert.AreEqual(EDocument.SystemId, PurchaseHeaderB."E-Document Link", 'Document B should now be linked to e-document'); + Assert.IsFalse(PurchaseHeaderB."Created from E-Document", 'Document B Created from E-Document should be false'); + + // [THEN] Both documents still exist (neither was deleted) + Assert.IsTrue(PurchaseHeaderA.Get(PurchaseHeaderA."Document Type", PurchaseHeaderA."No."), 'Document A should still exist'); + Assert.IsTrue(PurchaseHeaderB.Get(PurchaseHeaderB."Document Type", PurchaseHeaderB."No."), 'Document B should still exist'); + end; + + [Test] + [HandlerFunctions('PurchaseInvoicesModalPageHandler,ConfirmHandler,PIModalPageHandler')] + procedure LinkToExisting_UnlinksDocumentCreatedFromEDoc() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + CreatedPurchaseHeader: Record "Purchase Header"; + ExistingPurchaseHeader: Record "Purchase Header"; + ICPartner: Record "IC Partner"; + EDocImportParameters: Record "E-Doc. Import Parameters"; + EDocImport: Codeunit "E-Doc. Import"; + EDocPurchaseDraftTestPage: TestPage "E-Document Purchase Draft"; + CreatedDocNo: Code[20]; + EmptyGuid: Guid; + begin + // [SCENARIO] When linking to an existing document after a PI was already created from e-doc: + // - The originally created PI (created from e-doc) is unlinked but not deleted + // - The existing document is linked + Initialize(); + SetupICPartner(ICPartner); + + // [GIVEN] An inbound e-document with draft prepared + LibraryEDocument.CreateInboundEDocument(EDocument, EDocumentService); + EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); + EDocumentPurchaseHeader."[BC] Vendor No." := Vendor."No."; + EDocumentPurchaseHeader.Total := 2000; + EDocumentPurchaseHeader."Total VAT" := 400; + EDocumentPurchaseHeader.Modify(); + + // [GIVEN] Finalize draft to create a new PI from e-document + EDocImportParameters."Step to Run" := Enum::"Import E-Document Steps"::"Finish draft"; + EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParameters); + EDocument.Get(EDocument."Entry No"); + + // [GIVEN] Find the created purchase invoice + CreatedPurchaseHeader.SetRange("E-Document Link", EDocument.SystemId); + CreatedPurchaseHeader.FindFirst(); + CreatedDocNo := CreatedPurchaseHeader."No."; + Assert.IsTrue(CreatedPurchaseHeader."Created from E-Document", 'Created PI should have Created from E-Document = true'); + + // [GIVEN] An existing purchase invoice (not created from e-doc) + LibraryPurchase.CreatePurchHeader(ExistingPurchaseHeader, ExistingPurchaseHeader."Document Type"::Invoice, Vendor."No."); + ExistingPurchaseHeader."Doc. Amount Incl. VAT" := 2000; + ExistingPurchaseHeader.Modify(); + + // [WHEN] Linking to the existing document + LibraryVariableStorage.Enqueue(true); + LibraryVariableStorage.Enqueue(ExistingPurchaseHeader."No."); + EDocPurchaseDraftTestPage.OpenEdit(); + EDocPurchaseDraftTestPage.GoToRecord(EDocument); + EDocPurchaseDraftTestPage.LinkToExistingDocument.Invoke(); + EDocPurchaseDraftTestPage.Close(); + + // [THEN] The originally created PI is unlinked but still exists + Assert.IsTrue(CreatedPurchaseHeader.Get(CreatedPurchaseHeader."Document Type"::Invoice, CreatedDocNo), 'The PI created from e-doc should still exist'); + Assert.AreEqual(EmptyGuid, CreatedPurchaseHeader."E-Document Link", 'The PI created from e-doc should be unlinked'); + + // [THEN] The existing document is now linked + ExistingPurchaseHeader.Get(ExistingPurchaseHeader."Document Type", ExistingPurchaseHeader."No."); + Assert.AreEqual(EDocument.SystemId, ExistingPurchaseHeader."E-Document Link", 'Existing document should be linked'); + Assert.IsFalse(ExistingPurchaseHeader."Created from E-Document", 'Existing document Created from E-Document should be false'); + end; + + #endregion + + [ModalPageHandler] + procedure PurchaseInvoicesModalPageHandler(var PurchaseInvoices: TestPage "Purchase Invoices") + var + PurchaseInvoiceNo: Code[20]; + ShouldSelect: Boolean; + begin + if LibraryVariableStorage.Length() > 0 then begin + ShouldSelect := LibraryVariableStorage.DequeueBoolean(); + if not ShouldSelect then + exit; // Cancel selection by not selecting anything + + PurchaseInvoiceNo := CopyStr(LibraryVariableStorage.DequeueText(), 1, MaxStrLen(PurchaseInvoiceNo)); + PurchaseInvoices.Filter.SetFilter("No.", PurchaseInvoiceNo); + PurchaseInvoices.First(); + PurchaseInvoices.OK().Invoke(); + end; + end; + + [ModalPageHandler] + procedure PurchaseCreditMemosModalPageHandler(var PurchaseCreditMemos: TestPage "Purchase Credit Memos") + begin + // Handler confirms the page opened - no selection needed for this test + end; + + [ModalPageHandler] + procedure PurchaseOrdersModalPageHandler(var PurchaseOrders: TestPage "Purchase Orders") + begin + // Handler confirms the page opened - no selection needed for this test + end; + + [ModalPageHandler] + procedure PurchaseInvoicesVerifyVendorFilterHandler(var PurchaseInvoices: TestPage "Purchase Invoices") + var + VisibleDocNo: Code[20]; + NotVisibleDocNo: Code[20]; + begin + LibraryVariableStorage.DequeueText(); // Discard expected vendor no. (not needed here) + VisibleDocNo := CopyStr(LibraryVariableStorage.DequeueText(), 1, MaxStrLen(VisibleDocNo)); + NotVisibleDocNo := CopyStr(LibraryVariableStorage.DequeueText(), 1, MaxStrLen(NotVisibleDocNo)); + + // Verify the matching vendor document is visible + PurchaseInvoices.Filter.SetFilter("No.", VisibleDocNo); + Assert.IsTrue(PurchaseInvoices.First(), 'Document from matching vendor should be visible'); + + // Verify the other vendor document is NOT visible (filter should exclude it) + PurchaseInvoices.Filter.SetFilter("No.", NotVisibleDocNo); + Assert.IsFalse(PurchaseInvoices.First(), 'Document from different vendor should not be visible due to vendor filter'); + end; + + [ModalPageHandler] + procedure PurchaseInvoicesVerifyAmountFilterHandler(var PurchaseInvoices: TestPage "Purchase Invoices") + var + VisibleDocNo: Code[20]; + NotVisibleDocNo: Code[20]; + begin + LibraryVariableStorage.DequeueDecimal(); + VisibleDocNo := CopyStr(LibraryVariableStorage.DequeueText(), 1, MaxStrLen(VisibleDocNo)); + NotVisibleDocNo := CopyStr(LibraryVariableStorage.DequeueText(), 1, MaxStrLen(NotVisibleDocNo)); + + // Verify the matching amount document is visible + PurchaseInvoices.Filter.SetFilter("No.", VisibleDocNo); + Assert.IsTrue(PurchaseInvoices.First(), 'Document with matching amount should be visible'); + + // Verify the different amount document is NOT visible (filter should exclude it) + PurchaseInvoices.Filter.SetFilter("No.", NotVisibleDocNo); + Assert.IsFalse(PurchaseInvoices.First(), 'Document with different amount should not be visible due to amount filter'); + end; + + [PageHandler] + procedure PIModalPageHandler(var PurchaseInvoice: TestPage "Purchase Invoice") + begin + PurchaseInvoice.Close(); // Opens after finalize draft + end; + + [ConfirmHandler] + procedure ConfirmHandler(Question: Text[1024]; var Reply: Boolean) + begin + Reply := true; + end; + + + local procedure Initialize() + begin + LibraryLowerPermission.SetOutsideO365Scope(); + LibraryPurchase.SetOrderNoSeriesInSetup(); + LibraryPurchase.SetPostedNoSeriesInSetup(); + SetInvoiceNoSeriesInSetup(); + + LibraryEDocument.SetupStandardVAT(); + LibraryEDocument.SetupStandardPurchaseScenario(Vendor, EDocumentService, Enum::"E-Document Format"::Mock, Enum::"Service Integration"::Mock, Enum::"E-Document Import Process"::"Version 2.0"); + Commit(); + end; + + local procedure SetInvoiceNoSeriesInSetup() + var + PurchasesPayablesSetup: Record "Purchases & Payables Setup"; + begin + PurchasesPayablesSetup.Get(); + PurchasesPayablesSetup.Validate("Invoice Nos.", LibraryERM.CreateNoSeriesCode()); + PurchasesPayablesSetup.Modify(); + end; + + local procedure SetupICPartner(var ICPartner: Record "IC Partner") + begin + if not ICPartner.Get('ICTEST') then begin + ICPartner.Init(); + ICPartner.Code := 'ICTEST'; + ICPartner.Name := 'IC Partner Test'; + ICPartner.Insert(); + end; + Vendor."IC Partner Code" := ICPartner.Code; + Vendor.Modify(); + end; + + var + EDocumentService: Record "E-Document Service"; + Vendor: Record Vendor; + Assert: Codeunit Assert; + LibraryEDocument: Codeunit "Library - E-Document"; + LibraryLowerPermission: Codeunit "Library - Lower Permissions"; + LibraryPurchase: Codeunit "Library - Purchase"; + LibraryERM: Codeunit "Library - ERM"; + LibraryVariableStorage: Codeunit "Library - Variable Storage"; +} \ No newline at end of file diff --git a/src/Apps/W1/EDocument/Test/src/Processing/EDocProcessTest.Codeunit.al b/src/Apps/W1/EDocument/Test/src/Processing/EDocProcessTest.Codeunit.al index 5752a618cf..f758465218 100644 --- a/src/Apps/W1/EDocument/Test/src/Processing/EDocProcessTest.Codeunit.al +++ b/src/Apps/W1/EDocument/Test/src/Processing/EDocProcessTest.Codeunit.al @@ -332,205 +332,6 @@ codeunit 139883 "E-Doc Process Test" Assert.RecordIsEmpty(PurchaseHeader); end; - #region LinkToExistingDocument - - [Test] - procedure LinkToExistingDocumentSetsEDocumentLink() - var - EDocument: Record "E-Document"; - EDocImportParameters: Record "E-Doc. Import Parameters"; - ExistingPurchaseHeader: Record "Purchase Header"; - LinkedPurchaseHeader: Record "Purchase Header"; - EDocLogRecord: Record "E-Document Log"; - EDocImport: Codeunit "E-Doc. Import"; - EDocumentLog: Codeunit "E-Document Log"; - EDocumentProcessing: Codeunit "E-Document Processing"; - begin - // [SCENARIO] When linking an e-document to an existing purchase document, the E-Document Link is set on the purchase document - Initialize(Enum::"Service Integration"::"Mock"); - - // [GIVEN] An inbound e-document in Draft Ready state - LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); - EDocument."Document Type" := "E-Document Type"::"Purchase Invoice"; - EDocument.Modify(); - EDocumentService."Import Process" := "E-Document Import Process"::"Version 2.0"; - EDocumentService.Modify(); - - EDocumentLog.SetBlob('Test', Enum::"E-Doc. File Format"::XML, 'Data'); - EDocumentLog.SetFields(EDocument, EDocumentService); - EDocLogRecord := EDocumentLog.InsertLog(Enum::"E-Document Service Status"::Imported, Enum::"Import E-Doc. Proc. Status"::Readable); - EDocument."Structured Data Entry No." := EDocLogRecord."E-Doc. Data Storage Entry No."; - EDocument.Modify(); - EDocumentProcessing.ModifyEDocumentProcessingStatus(EDocument, "Import E-Doc. Proc. Status"::"Draft Ready"); - - // [GIVEN] An existing purchase invoice - ExistingPurchaseHeader."No." := 'EXISTING-001'; - ExistingPurchaseHeader."Document Type" := "Purchase Document Type"::Invoice; - ExistingPurchaseHeader.Insert(); - - // [WHEN] Finishing draft with Link To Existing Doc. Rec. ID set - EDocImportParameters."Step to Run" := "Import E-Document Steps"::"Finish draft"; - EDocImportParameters."Link To Existing Doc. Rec. ID" := ExistingPurchaseHeader.RecordId(); - EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParameters); - - // [THEN] The existing purchase document has E-Document Link set - LinkedPurchaseHeader.SetRange("E-Document Link", EDocument.SystemId); - Assert.RecordCount(LinkedPurchaseHeader, 1); - LinkedPurchaseHeader.FindFirst(); - Assert.AreEqual(ExistingPurchaseHeader."No.", LinkedPurchaseHeader."No.", 'The linked document should be the existing one'); - - // Cleanup - ExistingPurchaseHeader.Delete(); - end; - - [Test] - procedure LinkToExistingDocumentDoesNotCreateNewPurchaseInvoice() - var - EDocument: Record "E-Document"; - EDocImportParameters: Record "E-Doc. Import Parameters"; - ExistingPurchaseHeader: Record "Purchase Header"; - AllPurchaseHeaders: Record "Purchase Header"; - EDocLogRecord: Record "E-Document Log"; - EDocImport: Codeunit "E-Doc. Import"; - EDocumentLog: Codeunit "E-Document Log"; - EDocumentProcessing: Codeunit "E-Document Processing"; - InitialCount: Integer; - begin - // [SCENARIO] When linking an e-document to an existing purchase document, no new purchase invoice is created - Initialize(Enum::"Service Integration"::"Mock"); - - // [GIVEN] An inbound e-document in Draft Ready state - LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); - EDocument."Document Type" := "E-Document Type"::"Purchase Invoice"; - EDocument.Modify(); - EDocumentService."Import Process" := "E-Document Import Process"::"Version 2.0"; - EDocumentService.Modify(); - - EDocumentLog.SetBlob('Test', Enum::"E-Doc. File Format"::XML, 'Data'); - EDocumentLog.SetFields(EDocument, EDocumentService); - EDocLogRecord := EDocumentLog.InsertLog(Enum::"E-Document Service Status"::Imported, Enum::"Import E-Doc. Proc. Status"::Readable); - EDocument."Structured Data Entry No." := EDocLogRecord."E-Doc. Data Storage Entry No."; - EDocument.Modify(); - EDocumentProcessing.ModifyEDocumentProcessingStatus(EDocument, "Import E-Doc. Proc. Status"::"Draft Ready"); - - // [GIVEN] An existing purchase invoice and count of all purchase headers - ExistingPurchaseHeader."No." := 'EXISTING-002'; - ExistingPurchaseHeader."Document Type" := "Purchase Document Type"::Invoice; - ExistingPurchaseHeader.Insert(); - InitialCount := AllPurchaseHeaders.Count(); - - // [WHEN] Finishing draft with Link To Existing Doc. Rec. ID set - EDocImportParameters."Step to Run" := "Import E-Document Steps"::"Finish draft"; - EDocImportParameters."Link To Existing Doc. Rec. ID" := ExistingPurchaseHeader.RecordId(); - EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParameters); - - // [THEN] No new purchase header was created - Assert.AreEqual(InitialCount, AllPurchaseHeaders.Count(), 'No new purchase document should be created when linking to existing'); - - // Cleanup - ExistingPurchaseHeader.Delete(); - end; - - [Test] - procedure LinkToExistingDocumentMarksEDocumentAsProcessed() - var - EDocument: Record "E-Document"; - EDocImportParameters: Record "E-Doc. Import Parameters"; - ExistingPurchaseHeader: Record "Purchase Header"; - EDocLogRecord: Record "E-Document Log"; - EDocImport: Codeunit "E-Doc. Import"; - EDocumentLog: Codeunit "E-Document Log"; - EDocumentProcessing: Codeunit "E-Document Processing"; - begin - // [SCENARIO] When linking an e-document to an existing purchase document, the e-document is marked as processed - Initialize(Enum::"Service Integration"::"Mock"); - - // [GIVEN] An inbound e-document in Draft Ready state - LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); - EDocument."Document Type" := "E-Document Type"::"Purchase Invoice"; - EDocument.Modify(); - EDocumentService."Import Process" := "E-Document Import Process"::"Version 2.0"; - EDocumentService.Modify(); - - EDocumentLog.SetBlob('Test', Enum::"E-Doc. File Format"::XML, 'Data'); - EDocumentLog.SetFields(EDocument, EDocumentService); - EDocLogRecord := EDocumentLog.InsertLog(Enum::"E-Document Service Status"::Imported, Enum::"Import E-Doc. Proc. Status"::Readable); - EDocument."Structured Data Entry No." := EDocLogRecord."E-Doc. Data Storage Entry No."; - EDocument.Modify(); - EDocumentProcessing.ModifyEDocumentProcessingStatus(EDocument, "Import E-Doc. Proc. Status"::"Draft Ready"); - - // [GIVEN] An existing purchase invoice - ExistingPurchaseHeader."No." := 'EXISTING-003'; - ExistingPurchaseHeader."Document Type" := "Purchase Document Type"::Invoice; - ExistingPurchaseHeader.Insert(); - - // [WHEN] Finishing draft with Link To Existing Doc. Rec. ID set - EDocImportParameters."Step to Run" := "Import E-Document Steps"::"Finish draft"; - EDocImportParameters."Link To Existing Doc. Rec. ID" := ExistingPurchaseHeader.RecordId(); - EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParameters); - - // [THEN] The e-document import processing status is Processed - EDocument.CalcFields("Import Processing Status"); - Assert.AreEqual(Enum::"Import E-Doc. Proc. Status"::Processed, EDocument."Import Processing Status", 'The e-document should be marked as processed after linking'); - - // Cleanup - ExistingPurchaseHeader.Delete(); - end; - - [Test] - procedure LinkToExistingDocumentUpdatesDocumentAmounts() - var - EDocument: Record "E-Document"; - EDocumentPurchaseHeader: Record "E-Document Purchase Header"; - EDocImportParameters: Record "E-Doc. Import Parameters"; - ExistingPurchaseHeader: Record "Purchase Header"; - EDocLogRecord: Record "E-Document Log"; - EDocImport: Codeunit "E-Doc. Import"; - EDocumentLog: Codeunit "E-Document Log"; - EDocumentProcessing: Codeunit "E-Document Processing"; - begin - // [SCENARIO] When linking an e-document to an existing purchase document, document amounts are updated on the purchase document - Initialize(Enum::"Service Integration"::"Mock"); - - // [GIVEN] An inbound e-document in Draft Ready state with total amounts - LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); - EDocument."Document Type" := "E-Document Type"::"Purchase Invoice"; - EDocument.Modify(); - EDocumentService."Import Process" := "E-Document Import Process"::"Version 2.0"; - EDocumentService.Modify(); - - EDocumentLog.SetBlob('Test', Enum::"E-Doc. File Format"::XML, 'Data'); - EDocumentLog.SetFields(EDocument, EDocumentService); - EDocLogRecord := EDocumentLog.InsertLog(Enum::"E-Document Service Status"::Imported, Enum::"Import E-Doc. Proc. Status"::Readable); - EDocument."Structured Data Entry No." := EDocLogRecord."E-Doc. Data Storage Entry No."; - EDocument.Modify(); - EDocumentProcessing.ModifyEDocumentProcessingStatus(EDocument, "Import E-Doc. Proc. Status"::"Draft Ready"); - - // Set up e-document purchase header with amounts - EDocumentPurchaseHeader."E-Document Entry No." := EDocument."Entry No"; - EDocumentPurchaseHeader.Total := 1000; - EDocumentPurchaseHeader."Total VAT" := 200; - if not EDocumentPurchaseHeader.Insert() then - EDocumentPurchaseHeader.Modify(); - - // [GIVEN] An existing purchase invoice - ExistingPurchaseHeader."No." := 'EXISTING-004'; - ExistingPurchaseHeader."Document Type" := "Purchase Document Type"::Invoice; - ExistingPurchaseHeader.Insert(); - - // [WHEN] Finishing draft with Link To Existing Doc. Rec. ID set - EDocImportParameters."Step to Run" := "Import E-Document Steps"::"Finish draft"; - EDocImportParameters."Link To Existing Doc. Rec. ID" := ExistingPurchaseHeader.RecordId(); - EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParameters); - - // [THEN] The purchase document has the e-document amounts - ExistingPurchaseHeader.Get(ExistingPurchaseHeader."Document Type", ExistingPurchaseHeader."No."); - Assert.AreEqual(1000, ExistingPurchaseHeader."Doc. Amount Incl. VAT", 'The document amount incl. VAT should be set from e-document'); - Assert.AreEqual(200, ExistingPurchaseHeader."Doc. Amount VAT", 'The document VAT amount should be set from e-document'); - - // Cleanup - ExistingPurchaseHeader.Delete(); - end; [Test] procedure GetPurchaseDocTypeFilterReturnsCorrectType() From ff3ea229ac009cbb665c9bfbded33aad26d0f303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Wed, 21 Jan 2026 14:54:43 +0100 Subject: [PATCH 4/9] unused code --- .../App/src/Extensions/EDocPurchaseHeader.TableExt.al | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchaseHeader.TableExt.al b/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchaseHeader.TableExt.al index 976631f7b1..1172144105 100644 --- a/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchaseHeader.TableExt.al +++ b/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchaseHeader.TableExt.al @@ -5,7 +5,6 @@ namespace Microsoft.Purchases.Document; using Microsoft.eServices.EDocument; -using Microsoft.Utilities; tableextension 6169 "E-Doc. Purchase Header" extends "Purchase Header" { @@ -40,16 +39,6 @@ tableextension 6169 "E-Doc. Purchase Header" extends "Purchase Header" } } - internal procedure CalculateTotalAmountInclVAT(): Decimal - var - TotalPurchaseLine: Record "Purchase Line"; - DocumentTotals: Codeunit "Document Totals"; - VATAmount: Decimal; - begin - DocumentTotals.CalculatePurchaseTotals(TotalPurchaseLine, VATAmount, PurchLine); - exit(TotalPurchaseLine."Amount Including VAT"); - end; - internal procedure IsLinkedToEDoc(EDocumentToExclude: Record "E-Document"): Boolean begin exit(not IsNullGuid("E-Document Link") and ("E-Document Link" <> EDocumentToExclude.SystemId)); From 1f7b7dd3ab188f1591e897503cea328d7803d239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Wed, 21 Jan 2026 15:06:00 +0100 Subject: [PATCH 5/9] Fix --- .../FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al | 7 +++++-- .../Import/Purchase/EDocumentPurchaseDraft.Page.al | 2 +- .../src/Processing/EDocLinkToExistingTest.Codeunit.al | 10 ++++------ .../Test/src/Processing/EDocProcessTest.Codeunit.al | 1 - 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al index 9df72b6437..996d936449 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al @@ -97,7 +97,11 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft, PurchaseHeader.TestField("Document Type", "Purchase Document Type"::Invoice); Clear(PurchaseHeader."E-Document Link"); - PurchaseHeader.Modify(); + + if PurchaseHeader."Created from E-Document" then + PurchaseHeader.Delete(true) + else + PurchaseHeader.Modify(); end; procedure CreatePurchaseInvoice(EDocument: Record "E-Document"): Record "Purchase Header" @@ -271,5 +275,4 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft, if PurchaseLine.FindLast() then exit(PurchaseLine."Line No."); end; - } diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al index 8089f1a823..752928dfbf 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al @@ -715,7 +715,7 @@ page 6181 "E-Document Purchase Draft" EDocImportParameters: Record "E-Doc. Import Parameters"; ConfirmDialogMgt: Codeunit "Confirm Management"; LinkToExistingDocumentQst: Label 'Do you want to link this e-document to %1 %2?', Comment = '%1 = Document Type, %2 = Document No.'; - RelinkToExistingDocumentQst: Label 'This e-document is already linked to a document. Do you want to unlink the existing document and link to %1 %2 instead? The previously linked document will need to be cleaned up manually.', Comment = '%1 = Document Type, %2 = Document No.'; + RelinkToExistingDocumentQst: Label 'This e-document is already linked to a document. Linking to %1 %2 will unlink the currently linked document. If it was created from this e-document, it will be deleted. Do you want to continue?', Comment = '%1 = Document Type, %2 = Document No.'; ConfirmQst: Text; begin EDocumentProcessing.ErrorIfNotAllowedToLinkToExistingDoc(Rec, EDocumentPurchaseHeader); diff --git a/src/Apps/W1/EDocument/Test/src/Processing/EDocLinkToExistingTest.Codeunit.al b/src/Apps/W1/EDocument/Test/src/Processing/EDocLinkToExistingTest.Codeunit.al index d2412b0528..f34aa0e8ab 100644 --- a/src/Apps/W1/EDocument/Test/src/Processing/EDocLinkToExistingTest.Codeunit.al +++ b/src/Apps/W1/EDocument/Test/src/Processing/EDocLinkToExistingTest.Codeunit.al @@ -415,7 +415,7 @@ codeunit 139886 "E-Doc Link To Existing Test" [Test] [HandlerFunctions('PurchaseInvoicesModalPageHandler,ConfirmHandler,PIModalPageHandler')] - procedure LinkToExisting_UnlinksDocumentCreatedFromEDoc() + procedure LinkToExisting_DeletesDocumentCreatedFromEDoc() var EDocument: Record "E-Document"; EDocumentPurchaseHeader: Record "E-Document Purchase Header"; @@ -426,10 +426,9 @@ codeunit 139886 "E-Doc Link To Existing Test" EDocImport: Codeunit "E-Doc. Import"; EDocPurchaseDraftTestPage: TestPage "E-Document Purchase Draft"; CreatedDocNo: Code[20]; - EmptyGuid: Guid; begin // [SCENARIO] When linking to an existing document after a PI was already created from e-doc: - // - The originally created PI (created from e-doc) is unlinked but not deleted + // - The originally created PI (created from e-doc) is deleted // - The existing document is linked Initialize(); SetupICPartner(ICPartner); @@ -466,9 +465,8 @@ codeunit 139886 "E-Doc Link To Existing Test" EDocPurchaseDraftTestPage.LinkToExistingDocument.Invoke(); EDocPurchaseDraftTestPage.Close(); - // [THEN] The originally created PI is unlinked but still exists - Assert.IsTrue(CreatedPurchaseHeader.Get(CreatedPurchaseHeader."Document Type"::Invoice, CreatedDocNo), 'The PI created from e-doc should still exist'); - Assert.AreEqual(EmptyGuid, CreatedPurchaseHeader."E-Document Link", 'The PI created from e-doc should be unlinked'); + // [THEN] The originally created PI is deleted + Assert.IsFalse(CreatedPurchaseHeader.Get(CreatedPurchaseHeader."Document Type"::Invoice, CreatedDocNo), 'The PI created from e-doc should be deleted'); // [THEN] The existing document is now linked ExistingPurchaseHeader.Get(ExistingPurchaseHeader."Document Type", ExistingPurchaseHeader."No."); diff --git a/src/Apps/W1/EDocument/Test/src/Processing/EDocProcessTest.Codeunit.al b/src/Apps/W1/EDocument/Test/src/Processing/EDocProcessTest.Codeunit.al index f758465218..a6b7164928 100644 --- a/src/Apps/W1/EDocument/Test/src/Processing/EDocProcessTest.Codeunit.al +++ b/src/Apps/W1/EDocument/Test/src/Processing/EDocProcessTest.Codeunit.al @@ -355,7 +355,6 @@ codeunit 139883 "E-Doc Process Test" Assert.AreEqual(Enum::"Purchase Document Type"::Order, PurchaseDocType, 'Purchase Order should map to Order'); end; - #endregion #region HistoricalMatchingTest From ecea3c7da5691ea74dcb441e9d98a94fbe5b3735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Wed, 21 Jan 2026 15:33:15 +0100 Subject: [PATCH 6/9] Update src/Apps/W1/EDocument/App/src/Processing/EDocumentProcessing.Codeunit.al Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Processing/EDocumentProcessing.Codeunit.al | 15 --------------- 1 file changed, 15 deletions(-) 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 82cee508da..253a917386 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/EDocumentProcessing.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/EDocumentProcessing.Codeunit.al @@ -700,21 +700,6 @@ codeunit 6108 "E-Document Processing" end; end; - procedure LinkToExistingPurchaseDocument(EDocument: Record "E-Document"; PurchaseHeader: Record "Purchase Header"): Boolean - var - EDocImportParameters: Record "E-Doc. Import Parameters"; - ConfirmDialogMgt: Codeunit "Confirm Management"; - EDocImport: Codeunit "E-Doc. Import"; - LinkToExistingDocumentQst: Label 'Do you want to link this e-document to %1 %2? This will mark the e-document as processed.', Comment = '%1 = Document Type, %2 = Document No.'; - begin - if not ConfirmDialogMgt.GetResponseOrDefault(StrSubstNo(LinkToExistingDocumentQst, PurchaseHeader."Document Type", PurchaseHeader."No."), true) then - exit(false); - - EDocImportParameters."Existing Doc. RecordId" := PurchaseHeader.RecordId(); - EDocImportParameters."Step to Run" := "Import E-Document Steps"::"Finish draft"; - exit(EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParameters)); - end; - internal procedure ErrorIfNotAllowedToLinkToExistingDoc(EDocument: Record "E-Document"; EDocumentPurchaseHeader: Record "E-Document Purchase Header") var Vendor: Record Vendor; From e8963d102588e4b3fbddc286facb8dd8c0975966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Thu, 22 Jan 2026 10:16:23 +0100 Subject: [PATCH 7/9] Change to not delete. --- .../Extensions/EDocPurchaseHeader.TableExt.al | 5 ----- .../EDocCreatePurchaseInvoice.Codeunit.al | 7 +------ .../Purchase/EDocumentPurchaseDraft.Page.al | 2 +- .../EDocLinkToExistingTest.Codeunit.al | 17 ++++++----------- 4 files changed, 8 insertions(+), 23 deletions(-) diff --git a/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchaseHeader.TableExt.al b/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchaseHeader.TableExt.al index 1172144105..5f53d989a3 100644 --- a/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchaseHeader.TableExt.al +++ b/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchaseHeader.TableExt.al @@ -26,11 +26,6 @@ tableextension 6169 "E-Doc. Purchase Header" extends "Purchase Header" AutoFormatExpression = Rec."Currency Code"; AutoFormatType = 1; } - field(6102; "Created from E-Document"; Boolean) - { - DataClassification = SystemMetadata; - Editable = false; - } } keys { diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al index 996d936449..146435436d 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al @@ -68,7 +68,6 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft, PurchaseHeader."Doc. Amount VAT" := EDocumentPurchaseHeader."Total VAT"; PurchaseHeader.TestField("No."); PurchaseHeader."E-Document Link" := EDocument.SystemId; - PurchaseHeader."Created from E-Document" := EDocImportParameters."Existing Doc. RecordId" = EmptyRecordId; PurchaseHeader.Modify(); // Post document creation @@ -97,11 +96,7 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft, PurchaseHeader.TestField("Document Type", "Purchase Document Type"::Invoice); Clear(PurchaseHeader."E-Document Link"); - - if PurchaseHeader."Created from E-Document" then - PurchaseHeader.Delete(true) - else - PurchaseHeader.Modify(); + PurchaseHeader.Modify(); end; procedure CreatePurchaseInvoice(EDocument: Record "E-Document"): Record "Purchase Header" diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al index 752928dfbf..4905732652 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al @@ -715,7 +715,7 @@ page 6181 "E-Document Purchase Draft" EDocImportParameters: Record "E-Doc. Import Parameters"; ConfirmDialogMgt: Codeunit "Confirm Management"; LinkToExistingDocumentQst: Label 'Do you want to link this e-document to %1 %2?', Comment = '%1 = Document Type, %2 = Document No.'; - RelinkToExistingDocumentQst: Label 'This e-document is already linked to a document. Linking to %1 %2 will unlink the currently linked document. If it was created from this e-document, it will be deleted. Do you want to continue?', Comment = '%1 = Document Type, %2 = Document No.'; + RelinkToExistingDocumentQst: Label 'This e-document is already linked to a document. Linking to %1 %2 will unlink the currently linked document. You will need to manually clean up that document. Do you want to continue?', Comment = '%1 = Document Type, %2 = Document No.'; ConfirmQst: Text; begin EDocumentProcessing.ErrorIfNotAllowedToLinkToExistingDoc(Rec, EDocumentPurchaseHeader); diff --git a/src/Apps/W1/EDocument/Test/src/Processing/EDocLinkToExistingTest.Codeunit.al b/src/Apps/W1/EDocument/Test/src/Processing/EDocLinkToExistingTest.Codeunit.al index f34aa0e8ab..700fa146e3 100644 --- a/src/Apps/W1/EDocument/Test/src/Processing/EDocLinkToExistingTest.Codeunit.al +++ b/src/Apps/W1/EDocument/Test/src/Processing/EDocLinkToExistingTest.Codeunit.al @@ -337,9 +337,6 @@ codeunit 139886 "E-Doc Link To Existing Test" // [THEN] Doc amounts are transferred to purchase document Assert.AreEqual(1500, PurchaseHeader."Doc. Amount Incl. VAT", 'Doc. Amount Incl. VAT should be set from e-document'); Assert.AreEqual(300, PurchaseHeader."Doc. Amount VAT", 'Doc. Amount VAT should be set from e-document'); - - // [THEN] Created from E-Document is false (document existed before linking) - Assert.IsFalse(PurchaseHeader."Created from E-Document", 'Created from E-Document should be false since document was not created from e-doc'); end; [Test] @@ -401,12 +398,10 @@ codeunit 139886 "E-Doc Link To Existing Test" // [THEN] Document A is unlinked (E-Document Link is cleared) PurchaseHeaderA.Get(PurchaseHeaderA."Document Type", PurchaseHeaderA."No."); Assert.AreEqual(EmptyGuid, PurchaseHeaderA."E-Document Link", 'Document A should be unlinked after relinking to B'); - Assert.IsFalse(PurchaseHeaderA."Created from E-Document", 'Document A Created from E-Document should remain false'); // [THEN] Document B is now linked PurchaseHeaderB.Get(PurchaseHeaderB."Document Type", PurchaseHeaderB."No."); Assert.AreEqual(EDocument.SystemId, PurchaseHeaderB."E-Document Link", 'Document B should now be linked to e-document'); - Assert.IsFalse(PurchaseHeaderB."Created from E-Document", 'Document B Created from E-Document should be false'); // [THEN] Both documents still exist (neither was deleted) Assert.IsTrue(PurchaseHeaderA.Get(PurchaseHeaderA."Document Type", PurchaseHeaderA."No."), 'Document A should still exist'); @@ -415,7 +410,7 @@ codeunit 139886 "E-Doc Link To Existing Test" [Test] [HandlerFunctions('PurchaseInvoicesModalPageHandler,ConfirmHandler,PIModalPageHandler')] - procedure LinkToExisting_DeletesDocumentCreatedFromEDoc() + procedure LinkToExisting_UnlinksDocumentCreatedFromEDoc() var EDocument: Record "E-Document"; EDocumentPurchaseHeader: Record "E-Document Purchase Header"; @@ -426,9 +421,10 @@ codeunit 139886 "E-Doc Link To Existing Test" EDocImport: Codeunit "E-Doc. Import"; EDocPurchaseDraftTestPage: TestPage "E-Document Purchase Draft"; CreatedDocNo: Code[20]; + EmptyGuid: Guid; begin // [SCENARIO] When linking to an existing document after a PI was already created from e-doc: - // - The originally created PI (created from e-doc) is deleted + // - The originally created PI is unlinked but NOT deleted (user must clean up manually) // - The existing document is linked Initialize(); SetupICPartner(ICPartner); @@ -450,7 +446,6 @@ codeunit 139886 "E-Doc Link To Existing Test" CreatedPurchaseHeader.SetRange("E-Document Link", EDocument.SystemId); CreatedPurchaseHeader.FindFirst(); CreatedDocNo := CreatedPurchaseHeader."No."; - Assert.IsTrue(CreatedPurchaseHeader."Created from E-Document", 'Created PI should have Created from E-Document = true'); // [GIVEN] An existing purchase invoice (not created from e-doc) LibraryPurchase.CreatePurchHeader(ExistingPurchaseHeader, ExistingPurchaseHeader."Document Type"::Invoice, Vendor."No."); @@ -465,13 +460,13 @@ codeunit 139886 "E-Doc Link To Existing Test" EDocPurchaseDraftTestPage.LinkToExistingDocument.Invoke(); EDocPurchaseDraftTestPage.Close(); - // [THEN] The originally created PI is deleted - Assert.IsFalse(CreatedPurchaseHeader.Get(CreatedPurchaseHeader."Document Type"::Invoice, CreatedDocNo), 'The PI created from e-doc should be deleted'); + // [THEN] The originally created PI is unlinked but still exists (user must clean up manually) + Assert.IsTrue(CreatedPurchaseHeader.Get(CreatedPurchaseHeader."Document Type"::Invoice, CreatedDocNo), 'The PI created from e-doc should still exist'); + Assert.AreEqual(EmptyGuid, CreatedPurchaseHeader."E-Document Link", 'The PI created from e-doc should be unlinked'); // [THEN] The existing document is now linked ExistingPurchaseHeader.Get(ExistingPurchaseHeader."Document Type", ExistingPurchaseHeader."No."); Assert.AreEqual(EDocument.SystemId, ExistingPurchaseHeader."E-Document Link", 'Existing document should be linked'); - Assert.IsFalse(ExistingPurchaseHeader."Created from E-Document", 'Existing document Created from E-Document should be false'); end; #endregion From 161a024cbfe5acd663bd6e5861d676394eb16384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Thu, 22 Jan 2026 10:39:00 +0100 Subject: [PATCH 8/9] Remove dead code --- .../EDocumentProcessing.Codeunit.al | 12 ---------- .../Processing/EDocProcessTest.Codeunit.al | 24 ------------------- 2 files changed, 36 deletions(-) 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 253a917386..05d17f0cec 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/EDocumentProcessing.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/EDocumentProcessing.Codeunit.al @@ -688,18 +688,6 @@ codeunit 6108 "E-Document Processing" exit(RecCaption); end; - procedure GetPurchaseDocTypeFilter(EDocumentType: Enum "E-Document Type") PurchaseDocType: Enum "Purchase Document Type" - begin - case EDocumentType of - EDocumentType::"Purchase Invoice": - exit(PurchaseDocType::Invoice); - EDocumentType::"Purchase Credit Memo": - exit(PurchaseDocType::"Credit Memo"); - EDocumentType::"Purchase Order": - exit(PurchaseDocType::Order); - end; - end; - internal procedure ErrorIfNotAllowedToLinkToExistingDoc(EDocument: Record "E-Document"; EDocumentPurchaseHeader: Record "E-Document Purchase Header") var Vendor: Record Vendor; diff --git a/src/Apps/W1/EDocument/Test/src/Processing/EDocProcessTest.Codeunit.al b/src/Apps/W1/EDocument/Test/src/Processing/EDocProcessTest.Codeunit.al index a6b7164928..25d665dc35 100644 --- a/src/Apps/W1/EDocument/Test/src/Processing/EDocProcessTest.Codeunit.al +++ b/src/Apps/W1/EDocument/Test/src/Processing/EDocProcessTest.Codeunit.al @@ -332,30 +332,6 @@ codeunit 139883 "E-Doc Process Test" Assert.RecordIsEmpty(PurchaseHeader); end; - - [Test] - procedure GetPurchaseDocTypeFilterReturnsCorrectType() - var - EDocumentProcessing: Codeunit "E-Document Processing"; - PurchaseDocType: Enum "Purchase Document Type"; - begin - // [SCENARIO] GetPurchaseDocTypeFilter correctly maps E-Document types to Purchase Document types - Initialize(Enum::"Service Integration"::"Mock"); - - // [WHEN/THEN] Purchase Invoice maps to Invoice - PurchaseDocType := EDocumentProcessing.GetPurchaseDocTypeFilter(Enum::"E-Document Type"::"Purchase Invoice"); - Assert.AreEqual(Enum::"Purchase Document Type"::Invoice, PurchaseDocType, 'Purchase Invoice should map to Invoice'); - - // [WHEN/THEN] Purchase Credit Memo maps to Credit Memo - PurchaseDocType := EDocumentProcessing.GetPurchaseDocTypeFilter(Enum::"E-Document Type"::"Purchase Credit Memo"); - Assert.AreEqual(Enum::"Purchase Document Type"::"Credit Memo", PurchaseDocType, 'Purchase Credit Memo should map to Credit Memo'); - - // [WHEN/THEN] Purchase Order maps to Order - PurchaseDocType := EDocumentProcessing.GetPurchaseDocTypeFilter(Enum::"E-Document Type"::"Purchase Order"); - Assert.AreEqual(Enum::"Purchase Document Type"::Order, PurchaseDocType, 'Purchase Order should map to Order'); - end; - - #region HistoricalMatchingTest [Test] From 3516af3faa087f9e2d8ca3294afed2af7a4409ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Thu, 22 Jan 2026 16:05:38 +0100 Subject: [PATCH 9/9] Reread record. --- .../App/src/Processing/EDocumentSubscribers.Codeunit.al | 2 ++ 1 file changed, 2 insertions(+) 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 b5d502c026..c992aa71fd 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/EDocumentSubscribers.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/EDocumentSubscribers.Codeunit.al @@ -394,6 +394,8 @@ codeunit 6103 "E-Document Subscribers" EDocImportParameters."Step to Run / Desired Status" := EDocImportParameters."Step to Run / Desired Status"::"Desired E-Document Status"; EDocImportParameters."Desired E-Document Status" := "Import E-Doc. Proc. Status"::"Draft Ready"; EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParameters); + + PurchaseHeader.Get(PurchaseHeader."Document Type", PurchaseHeader."No."); end; [EventSubscriber(ObjectType::Codeunit, Codeunit::"Data Classification Eval. Data", 'OnCreateEvaluationDataOnAfterClassifyTablesToNormal', '', false, false)]