diff --git a/src/Apps/W1/EDocument/App/app.json b/src/Apps/W1/EDocument/App/app.json
index 42a39ca645..c7b0e49a5b 100644
--- a/src/Apps/W1/EDocument/App/app.json
+++ b/src/Apps/W1/EDocument/App/app.json
@@ -57,6 +57,10 @@
{
"from": 6234,
"to": 6234
+ },
+ {
+ "from": 6401,
+ "to": 6410
}
],
"resourceExposurePolicy": {
diff --git a/src/Apps/W1/EDocument/App/src/Processing/EDocImport.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/EDocImport.Codeunit.al
index c5d9bf88eb..a349265ed3 100644
--- a/src/Apps/W1/EDocument/App/src/Processing/EDocImport.Codeunit.al
+++ b/src/Apps/W1/EDocument/App/src/Processing/EDocImport.Codeunit.al
@@ -136,13 +136,16 @@ codeunit 6140 "E-Doc. Import"
var
EDocDraftSessionTelemetry: Codeunit "E-Doc. Imp. Session Telemetry";
EDocumentErrorHelper: Codeunit "E-Document Error Helper";
+ EDocImportErrorContext: Codeunit "E-Doc. Import Error Context";
LastErrorText: Text;
begin
EDocumentErrorHelper.ClearErrorMessages(EDocument);
Commit();
+ BindSubscription(EDocImportErrorContext);
if not ImportEDocumentProcess.Run() then begin
LastErrorText := GetLastErrorText();
if LastErrorText <> '' then begin // We don't insert an error when empty, following the convention of empty error meaning "operation cancelled by user"
+ LastErrorText := EDocImportErrorContext.WrapErrorMessage(LastErrorText);
EDocument.SetRecFilter();
EDocument.FindFirst();
@@ -154,8 +157,10 @@ codeunit 6140 "E-Doc. Import"
end;
EDocDraftSessionTelemetry.SetText('Step', Format(ImportEDocumentProcess.GetStep()));
EDocDraftSessionTelemetry.SetBool('Success', false);
+ UnbindSubscription(EDocImportErrorContext);
exit(false);
end;
+ UnbindSubscription(EDocImportErrorContext);
exit(true);
end;
diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/EDocImportErrorContext.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/EDocImportErrorContext.Codeunit.al
new file mode 100644
index 0000000000..0464aded9d
--- /dev/null
+++ b/src/Apps/W1/EDocument/App/src/Processing/Import/EDocImportErrorContext.Codeunit.al
@@ -0,0 +1,80 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+namespace Microsoft.eServices.EDocument.Processing.Import;
+
+codeunit 6199 "E-Doc. Import Error Context"
+{
+ Access = Internal;
+ InherentEntitlements = X;
+ InherentPermissions = X;
+ EventSubscriberInstance = Manual;
+
+ var
+ CurrentContext: Text;
+ AdditionalFieldContextLbl: Label 'While applying additional field "%1" (ID %2) with value ''%3''', Comment = '%1 = Field Name, %2 = Field Number, %3 = Value';
+ ValidatingFieldLbl: Label 'While validating field "%1"', Comment = '%1 = Field Caption';
+ WrapErrorLbl: Label '%1: %2', Comment = '%1 = Context, %2 = Original Error';
+
+ ///
+ /// Returns whether a context message is currently set.
+ ///
+ /// True if a context message is set, false otherwise.
+ procedure HasContext(): Boolean
+ begin
+ exit(CurrentContext <> '');
+ end;
+
+ ///
+ /// Wraps the original error message with the current context, if one is set.
+ ///
+ /// The original error message to wrap.
+ /// The error message prefixed with the current context, or the original message if no context is set.
+ procedure WrapErrorMessage(OriginalError: Text): Text
+ begin
+ if CurrentContext = '' then
+ exit(OriginalError);
+ exit(StrSubstNo(WrapErrorLbl, CurrentContext, OriginalError));
+ end;
+
+ ///
+ /// Clears the additional field context
+ ///
+ procedure ClearAdditionalFieldContext()
+ begin
+ CurrentContext := '';
+ end;
+
+ [EventSubscriber(ObjectType::Codeunit, Codeunit::"E-Doc. Import Error Context", OnValidateFieldWithContext, '', false, false)]
+ local procedure ValidateFieldWithContextSubscriber(FieldCaption: Text)
+ begin
+ CurrentContext := StrSubstNo(ValidatingFieldLbl, FieldCaption);
+ end;
+
+ [EventSubscriber(ObjectType::Codeunit, Codeunit::"E-Doc. Import Error Context", OnSetAdditionalFieldContext, '', false, false)]
+ local procedure SetAdditionalFieldContext(FieldName: Text; FieldNo: Integer; Value: Text)
+ begin
+ CurrentContext := StrSubstNo(AdditionalFieldContextLbl, FieldName, FieldNo, Value);
+ end;
+
+ ///
+ /// Sets the context to describe a field being validated during e-document import.
+ ///
+ /// The caption of the field being validated.
+ [IntegrationEvent(false, false)]
+ procedure OnValidateFieldWithContext(FieldCaption: Text)
+ begin
+ end;
+
+ ///
+ /// Sets the context to describe an additional field being applied
+ ///
+ /// The name of the additional field being applied.
+ /// The ID of the additional field being applied.
+ /// The value being applied to the field.
+ [IntegrationEvent(false, false)]
+ procedure OnSetAdditionalFieldContext(FieldName: Text; FieldNo: Integer; Value: Text)
+ begin
+ end;
+}
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 5744937013..b684b1fcfd 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
@@ -112,6 +112,7 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft,
EDocumentPurchaseLine: Record "E-Document Purchase Line";
PurchaseLine: Record "Purchase Line";
EDocRecordLink: Record "E-Doc. Record Link";
+ EDocPurchaseDocumentHelper: Codeunit "E-Doc. Purch. Doc. Helper";
PurchCalcDiscByType: Codeunit "Purch - Calc Disc. By Type";
EDocLineByReceipt: Query "E-Doc. Line by Receipt";
LastReceiptNo: Code[20];
@@ -132,9 +133,9 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft,
PurchaseHeader."Pay-to Vendor No." := EDocumentPurchaseHeader."[BC] Vendor No.";
PurchaseHeader."Posting Description" := EDocumentPurchaseHeader."Posting Description";
if EDocumentPurchaseHeader."Document Date" <> 0D then
- PurchaseHeader.Validate("Document Date", EDocumentPurchaseHeader."Document Date");
+ EDocPurchaseDocumentHelper.ValidateFieldWithContext(PurchaseHeader, PurchaseHeader.FieldNo("Document Date"), EDocumentPurchaseHeader."Document Date");
if EDocumentPurchaseHeader."Due Date" <> 0D then
- PurchaseHeader.Validate("Due Date", EDocumentPurchaseHeader."Due Date");
+ EDocPurchaseDocumentHelper.ValidateFieldWithContext(PurchaseHeader, PurchaseHeader.FieldNo("Due Date"), EDocumentPurchaseHeader."Due Date");
VendorInvoiceNo := CopyStr(EDocumentPurchaseHeader."Sales Invoice No.", 1, MaxStrLen(PurchaseHeader."Vendor Invoice No."));
VendorLedgerEntry.SetLoadFields("Entry No.");
@@ -145,7 +146,7 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft,
Error(InvoiceAlreadyExistsErr, VendorInvoiceNo, EDocumentPurchaseHeader."[BC] Vendor No.");
end;
- PurchaseHeader.Validate("Vendor Invoice No.", VendorInvoiceNo);
+ EDocPurchaseDocumentHelper.ValidateFieldWithContext(PurchaseHeader, PurchaseHeader.FieldNo("Vendor Invoice No."), VendorInvoiceNo);
PurchaseHeader.Insert(true);
PurchaseHeader."Invoice Received Date" := PurchaseHeader."Document Date";
@@ -154,7 +155,7 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft,
// Validate of currency has to happen after insert.
GLSetup.GetRecordOnce();
if EDocumentPurchaseHeader."Currency Code" <> GLSetup.GetCurrencyCode('') then begin
- PurchaseHeader.Validate("Currency Code", EDocumentPurchaseHeader."Currency Code");
+ EDocPurchaseDocumentHelper.ValidateFieldWithContext(PurchaseHeader, PurchaseHeader.FieldNo("Currency Code"), EDocumentPurchaseHeader."Currency Code");
PurchaseHeader.Modify();
end;
EDocRecordLink.InsertEDocumentHeaderLink(EDocumentPurchaseHeader, PurchaseHeader);
diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocPurchDocHelper.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocPurchDocHelper.Codeunit.al
new file mode 100644
index 0000000000..007eb6dd13
--- /dev/null
+++ b/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocPurchDocHelper.Codeunit.al
@@ -0,0 +1,39 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+namespace Microsoft.eServices.EDocument.Processing.Import;
+
+using Microsoft.Purchases.Document;
+
+///
+/// Shared logic for creating BC purchase documents (invoices and credit memos) from e-document draft data.
+///
+codeunit 6402 "E-Doc. Purch. Doc. Helper"
+{
+ Access = Internal;
+ InherentEntitlements = X;
+ InherentPermissions = X;
+
+ procedure ValidateFieldWithContext(var Rec: Record "Purchase Header"; FieldNo: Integer; Value: Variant)
+ var
+ VariantRec: Variant;
+ begin
+ VariantRec := Rec;
+ ValidateFieldWithContext(VariantRec, FieldNo, Value);
+ Rec := VariantRec;
+ end;
+
+ local procedure ValidateFieldWithContext(var RecVariant: Variant; FieldNo: Integer; Value: Variant)
+ var
+ EDocImportErrorContext: Codeunit "E-Doc. Import Error Context";
+ RecRef: RecordRef;
+ FldRef: FieldRef;
+ begin
+ RecRef.GetTable(RecVariant);
+ FldRef := RecRef.Field(FieldNo);
+ EDocImportErrorContext.OnValidateFieldWithContext(FldRef.Caption());
+ FldRef.Validate(Value);
+ RecRef.SetTable(RecVariant);
+ end;
+}
diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/History/EDocPurchaseHistMapping.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/History/EDocPurchaseHistMapping.Codeunit.al
index 8b2e8ffbdb..80786575f1 100644
--- a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/History/EDocPurchaseHistMapping.Codeunit.al
+++ b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/History/EDocPurchaseHistMapping.Codeunit.al
@@ -212,8 +212,10 @@ codeunit 6120 "E-Doc. Purchase Hist. Mapping"
var
EDocPurchLineFieldSetup: Record "ED Purchase Line Field Setup";
EDocPurchLineField: Record "E-Document Line - Field";
+ EDocImportErrorContext: Codeunit "E-Doc. Import Error Context";
NewPurchLineRecordRef: RecordRef;
NewPurchLineFieldRef: FieldRef;
+ FieldValue: Variant;
begin
if not EDocPurchLineFieldSetup.FindSet() then
exit;
@@ -223,7 +225,10 @@ codeunit 6120 "E-Doc. Purchase Hist. Mapping"
continue;
EDocPurchLineField.Get(EDocumentPurchaseLine, EDocPurchLineFieldSetup);
NewPurchLineFieldRef := NewPurchLineRecordRef.Field(EDocPurchLineFieldSetup."Field No.");
- NewPurchLineFieldRef.Validate(EDocPurchLineField.GetValue());
+ FieldValue := EDocPurchLineField.GetValue();
+ EDocImportErrorContext.OnSetAdditionalFieldContext(NewPurchLineFieldRef.Name(), EDocPurchLineFieldSetup."Field No.", EDocPurchLineField.GetValueAsText());
+ NewPurchLineFieldRef.Validate(FieldValue);
+ EDocImportErrorContext.ClearAdditionalFieldContext();
until EDocPurchLineFieldSetup.Next() = 0;
NewPurchLineRecordRef.SetTable(PurchaseLine);
end;
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 82e79a65fb..508b6732ee 100644
--- a/src/Apps/W1/EDocument/Test/src/Processing/EDocProcessTest.Codeunit.al
+++ b/src/Apps/W1/EDocument/Test/src/Processing/EDocProcessTest.Codeunit.al
@@ -25,11 +25,13 @@ using Microsoft.Purchases.Vendor;
using Microsoft.Sales.Customer;
using System.IO;
using System.TestLibraries.Utilities;
+using System.Utilities;
codeunit 139883 "E-Doc Process Test"
{
Subtype = Test;
TestType = IntegrationTest;
+ TestPermissions = Disabled;
var
Customer: Record Customer;
@@ -581,6 +583,333 @@ codeunit 139883 "E-Doc Process Test"
Assert.AreNotEqual(Location.Code, PurchaseLine."Location Code", 'The location code should not be set on the purchase line.');
end;
+ [Test]
+ procedure AdditionalFieldWithInvalidValueEnrichesErrorMessage()
+ var
+ EDocPurchLineFieldSetup: Record "ED Purchase Line Field Setup";
+ PurchaseInvoiceLine: Record "Purch. Inv. Line";
+ EDocument: Record "E-Document";
+ EDocImportParams: Record "E-Doc. Import Parameters";
+ PurchaseHeader: Record "Purchase Header";
+ EDocPurchLineField: Record "E-Document Line - Field";
+ EDocPurchaseLine: Record "E-Document Purchase Line";
+ ErrorMessage: Record "Error Message";
+ EDocImport: Codeunit "E-Doc. Import";
+ EDocumentErrorHelper: Codeunit "E-Document Error Helper";
+ begin
+ // [SCENARIO] An additional field is configured with an invalid value that fails FieldRef.Validate.
+ // The error message should contain the additional field name, ID, and value.
+ Initialize(Enum::"Service Integration"::"Mock");
+
+ // [GIVEN] An additional field is configured for Location Code (Code[10])
+ EDocPurchLineFieldSetup."Field No." := PurchaseInvoiceLine.FieldNo("Location Code");
+ EDocPurchLineFieldSetup.Insert();
+
+ // [GIVEN] An inbound e-document is received and a draft created
+ EDocImportParams."Step to Run" := "Import E-Document Steps"::"Prepare draft";
+ Assert.IsTrue(LibraryEDoc.CreateInboundPEPPOLDocumentToState(EDocument, EDocumentService, 'peppol/peppol-invoice-0.xml', EDocImportParams), 'The draft for the e-document should be created');
+
+ // [GIVEN] A value that does not exist as a Location Code
+ EDocPurchLineField."E-Document Entry No." := EDocument."Entry No";
+ EDocPurchaseLine.SetRange("E-Document Entry No.", EDocument."Entry No");
+ EDocPurchaseLine.FindFirst();
+ EDocPurchLineField."Line No." := EDocPurchaseLine."Line No.";
+ EDocPurchLineField."Field No." := PurchaseInvoiceLine.FieldNo("Location Code");
+ EDocPurchLineField."Code Value" := 'INVALID';
+ EDocPurchLineField.Insert();
+
+ // [WHEN] Finalizing the draft
+ EDocImportParams."Step to Run" := "Import E-Document Steps"::"Finish draft";
+ EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParams);
+
+ // [THEN] The e-document should have an error
+ EDocument.Get(EDocument."Entry No");
+ Assert.IsTrue(EDocumentErrorHelper.HasErrors(EDocument), 'The e-document should have errors');
+
+ // [THEN] The error message should reference the additional field name, ID, and value
+ ErrorMessage.SetRange("Context Record ID", EDocument.RecordId());
+ ErrorMessage.SetRange("Message Type", ErrorMessage."Message Type"::Error);
+ ErrorMessage.FindFirst();
+ Assert.ExpectedMessage('While applying additional field "Location Code"', ErrorMessage."Message");
+ Assert.ExpectedMessage(Format(PurchaseInvoiceLine.FieldNo("Location Code")), ErrorMessage."Message");
+ Assert.ExpectedMessage('INVALID', ErrorMessage."Message");
+
+ // [THEN] No purchase invoice should have been created
+ PurchaseHeader.SetRange("E-Document Link", EDocument.SystemId);
+ Assert.RecordIsEmpty(PurchaseHeader);
+ end;
+
+ [Test]
+ procedure AdditionalFieldValueExceedingFieldLengthEnrichesErrorMessage()
+ var
+ EDocPurchLineFieldSetup: Record "ED Purchase Line Field Setup";
+ PurchaseInvoiceLine: Record "Purch. Inv. Line";
+ EDocument: Record "E-Document";
+ EDocImportParams: Record "E-Doc. Import Parameters";
+ PurchaseHeader: Record "Purchase Header";
+ EDocPurchLineField: Record "E-Document Line - Field";
+ EDocPurchaseLine: Record "E-Document Purchase Line";
+ ErrorMessage: Record "Error Message";
+ EDocImport: Codeunit "E-Doc. Import";
+ EDocumentErrorHelper: Codeunit "E-Document Error Helper";
+ FieldValue: Code[2048];
+ begin
+ // [SCENARIO] An additional field is configured with a value that exceeds the target field's maximum length.
+ // The error message should reference the additional field name, ID, and the overlong value.
+ Initialize(Enum::"Service Integration"::"Mock");
+
+ // [GIVEN] An additional field is configured for Location Code (Code[10])
+ EDocPurchLineFieldSetup."Field No." := PurchaseInvoiceLine.FieldNo("Location Code");
+ EDocPurchLineFieldSetup.Insert();
+
+ // [GIVEN] An inbound e-document is received and a draft created
+ EDocImportParams."Step to Run" := "Import E-Document Steps"::"Prepare draft";
+ Assert.IsTrue(LibraryEDoc.CreateInboundPEPPOLDocumentToState(EDocument, EDocumentService, 'peppol/peppol-invoice-0.xml', EDocImportParams), 'The draft for the e-document should be created');
+
+ // [GIVEN] A value that exceeds the target field length (Code[10])
+ FieldValue := 'LONGLOCCODE1'; // 12 characters, exceeds Code[10]
+ EDocPurchLineField."E-Document Entry No." := EDocument."Entry No";
+ EDocPurchaseLine.SetRange("E-Document Entry No.", EDocument."Entry No");
+ EDocPurchaseLine.FindFirst();
+ EDocPurchLineField."Line No." := EDocPurchaseLine."Line No.";
+ EDocPurchLineField."Field No." := PurchaseInvoiceLine.FieldNo("Location Code");
+ EDocPurchLineField."Code Value" := FieldValue;
+ EDocPurchLineField.Insert();
+
+ // [WHEN] Finalizing the draft
+ EDocImportParams."Step to Run" := "Import E-Document Steps"::"Finish draft";
+ EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParams);
+
+ // [THEN] The e-document should have an error
+ EDocument.Get(EDocument."Entry No");
+ Assert.IsTrue(EDocumentErrorHelper.HasErrors(EDocument), 'The e-document should have errors');
+
+ // [THEN] The error message should reference the additional field name and value
+ ErrorMessage.SetRange("Context Record ID", EDocument.RecordId());
+ ErrorMessage.SetRange("Message Type", ErrorMessage."Message Type"::Error);
+ ErrorMessage.FindFirst();
+ Assert.ExpectedMessage('While applying additional field "Location Code"', ErrorMessage."Message");
+ Assert.ExpectedMessage(FieldValue, ErrorMessage."Message");
+
+ // [THEN] No purchase invoice should have been created
+ PurchaseHeader.SetRange("E-Document Link", EDocument.SystemId);
+ Assert.RecordIsEmpty(PurchaseHeader);
+ end;
+
+ [Test]
+ procedure StandardFieldValidationFailureEnrichesErrorMessage()
+ var
+ EDocument: Record "E-Document";
+ EDocImportParams: Record "E-Doc. Import Parameters";
+ EDocumentPurchaseHeader: Record "E-Document Purchase Header";
+ PurchaseHeader: Record "Purchase Header";
+ ErrorMessage: Record "Error Message";
+ EDocImport: Codeunit "E-Doc. Import";
+ EDocumentErrorHelper: Codeunit "E-Document Error Helper";
+ begin
+ // [SCENARIO] A standard field validation fails during purchase invoice creation.
+ // The error message should contain the field caption.
+ Initialize(Enum::"Service Integration"::"Mock");
+
+ // [GIVEN] An inbound e-document is received and a draft created
+ EDocImportParams."Step to Run" := "Import E-Document Steps"::"Prepare draft";
+ Assert.IsTrue(LibraryEDoc.CreateInboundPEPPOLDocumentToState(EDocument, EDocumentService, 'peppol/peppol-invoice-0.xml', EDocImportParams), 'The draft for the e-document should be created');
+
+ // [GIVEN] The draft has an invalid currency code
+ EDocumentPurchaseHeader.GetFromEDocument(EDocument);
+ EDocumentPurchaseHeader."Currency Code" := 'INVCURR';
+ EDocumentPurchaseHeader.Modify();
+
+ // [WHEN] Finalizing the draft
+ EDocImportParams."Step to Run" := "Import E-Document Steps"::"Finish draft";
+ EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParams);
+
+ // [THEN] The e-document should have an error
+ EDocument.Get(EDocument."Entry No");
+ Assert.IsTrue(EDocumentErrorHelper.HasErrors(EDocument), 'The e-document should have errors');
+
+ // [THEN] The error message should reference the Currency Code field
+ ErrorMessage.SetRange("Context Record ID", EDocument.RecordId());
+ ErrorMessage.SetRange("Message Type", ErrorMessage."Message Type"::Error);
+ ErrorMessage.FindFirst();
+ Assert.ExpectedMessage('While validating field "Currency Code"', ErrorMessage."Message");
+
+ // [THEN] No purchase invoice should have been created
+ PurchaseHeader.SetRange("E-Document Link", EDocument.SystemId);
+ Assert.RecordIsEmpty(PurchaseHeader);
+ end;
+
+ [Test]
+ procedure SuccessfulImportWithAdditionalFieldsHasNoErrors()
+ var
+ EDocPurchLineFieldSetup: Record "ED Purchase Line Field Setup";
+ PurchaseInvoiceLine: Record "Purch. Inv. Line";
+ EDocument: Record "E-Document";
+ EDocImportParams: Record "E-Doc. Import Parameters";
+ PurchaseHeader: Record "Purchase Header";
+ EDocPurchLineField: Record "E-Document Line - Field";
+ EDocPurchaseLine: Record "E-Document Purchase Line";
+ ErrorMessage: Record "Error Message";
+ Location: Record Location;
+ EDocImport: Codeunit "E-Doc. Import";
+ EDocumentErrorHelper: Codeunit "E-Document Error Helper";
+ begin
+ // [SCENARIO] Additional fields are configured with valid values.
+ // The import should succeed with no errors or warnings.
+ Initialize(Enum::"Service Integration"::"Mock");
+
+ // [GIVEN] An additional field is configured for Location Code
+ EDocPurchLineFieldSetup."Field No." := PurchaseInvoiceLine.FieldNo("Location Code");
+ EDocPurchLineFieldSetup.Insert();
+
+ // [GIVEN] A valid location exists
+ Location.Code := 'VALIDLOC';
+ if Location.Insert() then;
+
+ // [GIVEN] An inbound e-document is received and a draft created
+ EDocImportParams."Step to Run" := "Import E-Document Steps"::"Prepare draft";
+ Assert.IsTrue(LibraryEDoc.CreateInboundPEPPOLDocumentToState(EDocument, EDocumentService, 'peppol/peppol-invoice-0.xml', EDocImportParams), 'The draft for the e-document should be created');
+
+ // [GIVEN] The additional field has a valid value
+ EDocPurchLineField."E-Document Entry No." := EDocument."Entry No";
+ EDocPurchaseLine.SetRange("E-Document Entry No.", EDocument."Entry No");
+ EDocPurchaseLine.FindFirst();
+ EDocPurchLineField."Line No." := EDocPurchaseLine."Line No.";
+ EDocPurchLineField."Field No." := PurchaseInvoiceLine.FieldNo("Location Code");
+ EDocPurchLineField."Code Value" := 'VALIDLOC';
+ EDocPurchLineField.Insert();
+
+ // [WHEN] Finalizing the draft
+ EDocImportParams."Step to Run" := "Import E-Document Steps"::"Finish draft";
+ Assert.IsTrue(EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParams), 'The finalization should succeed');
+
+ // [THEN] The e-document should have no errors
+ EDocument.Get(EDocument."Entry No");
+ Assert.IsFalse(EDocumentErrorHelper.HasErrors(EDocument), 'The e-document should not have errors');
+
+ // [THEN] No error or warning messages should exist
+ ErrorMessage.SetRange("Context Record ID", EDocument.RecordId());
+ Assert.RecordIsEmpty(ErrorMessage);
+
+ // [THEN] A purchase invoice should have been created
+ PurchaseHeader.SetRange("E-Document Link", EDocument.SystemId);
+ Assert.RecordIsNotEmpty(PurchaseHeader);
+ end;
+
+ [Test]
+ procedure MultipleAdditionalFieldsFailureOnSecondHasCorrectContext()
+ var
+ EDocPurchLineFieldSetup: Record "ED Purchase Line Field Setup";
+ PurchaseInvoiceLine: Record "Purch. Inv. Line";
+ EDocument: Record "E-Document";
+ EDocImportParams: Record "E-Doc. Import Parameters";
+ PurchaseHeader: Record "Purchase Header";
+ EDocPurchLineField: Record "E-Document Line - Field";
+ EDocPurchaseLine: Record "E-Document Purchase Line";
+ ErrorMessage: Record "Error Message";
+ Location: Record Location;
+ EDocImport: Codeunit "E-Doc. Import";
+ EDocumentErrorHelper: Codeunit "E-Document Error Helper";
+ begin
+ // [SCENARIO] Two additional fields are configured. The first has a valid value, the second has an invalid value.
+ // The error message should reference the second field, not the first.
+ Initialize(Enum::"Service Integration"::"Mock");
+
+ // [GIVEN] Two additional fields configured: Location Code and Bin Code
+ EDocPurchLineFieldSetup."Field No." := PurchaseInvoiceLine.FieldNo("Location Code");
+ EDocPurchLineFieldSetup.Insert();
+ Clear(EDocPurchLineFieldSetup);
+ EDocPurchLineFieldSetup."Field No." := PurchaseInvoiceLine.FieldNo("Bin Code");
+ EDocPurchLineFieldSetup.Insert();
+
+ // [GIVEN] A valid location exists
+ Location.Code := 'MULTILOC';
+ if Location.Insert() then;
+
+ // [GIVEN] An inbound e-document is received and a draft created
+ EDocImportParams."Step to Run" := "Import E-Document Steps"::"Prepare draft";
+ Assert.IsTrue(LibraryEDoc.CreateInboundPEPPOLDocumentToState(EDocument, EDocumentService, 'peppol/peppol-invoice-0.xml', EDocImportParams), 'The draft for the e-document should be created');
+
+ // [GIVEN] First field (Location Code) has a valid value, second field (Bin Code) has an invalid value
+ EDocPurchaseLine.SetRange("E-Document Entry No.", EDocument."Entry No");
+ EDocPurchaseLine.FindFirst();
+
+ EDocPurchLineField."E-Document Entry No." := EDocument."Entry No";
+ EDocPurchLineField."Line No." := EDocPurchaseLine."Line No.";
+ EDocPurchLineField."Field No." := PurchaseInvoiceLine.FieldNo("Location Code");
+ EDocPurchLineField."Code Value" := 'MULTILOC';
+ EDocPurchLineField.Insert();
+
+ Clear(EDocPurchLineField);
+ EDocPurchLineField."E-Document Entry No." := EDocument."Entry No";
+ EDocPurchLineField."Line No." := EDocPurchaseLine."Line No.";
+ EDocPurchLineField."Field No." := PurchaseInvoiceLine.FieldNo("Bin Code");
+ EDocPurchLineField."Code Value" := 'INVALIDBIN';
+ EDocPurchLineField.Insert();
+
+ // [WHEN] Finalizing the draft
+ EDocImportParams."Step to Run" := "Import E-Document Steps"::"Finish draft";
+ EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParams);
+
+ // [THEN] The e-document should have an error
+ EDocument.Get(EDocument."Entry No");
+ Assert.IsTrue(EDocumentErrorHelper.HasErrors(EDocument), 'The e-document should have errors');
+
+ // [THEN] The error message should reference the second field (Bin Code), not the first (Location Code)
+ ErrorMessage.SetRange("Context Record ID", EDocument.RecordId());
+ ErrorMessage.SetRange("Message Type", ErrorMessage."Message Type"::Error);
+ ErrorMessage.FindFirst();
+ Assert.ExpectedMessage('Bin Code', ErrorMessage."Message");
+ Assert.ExpectedMessage('INVALIDBIN', ErrorMessage."Message");
+
+ // [THEN] No purchase invoice should have been created
+ PurchaseHeader.SetRange("E-Document Link", EDocument.SystemId);
+ Assert.RecordIsEmpty(PurchaseHeader);
+ end;
+
+ [Test]
+ procedure NoAdditionalFieldsStandardFieldFailureStillEnriched()
+ var
+ EDocument: Record "E-Document";
+ EDocImportParams: Record "E-Doc. Import Parameters";
+ EDocumentPurchaseHeader: Record "E-Document Purchase Header";
+ PurchaseHeader: Record "Purchase Header";
+ ErrorMessage: Record "Error Message";
+ EDocImport: Codeunit "E-Doc. Import";
+ EDocumentErrorHelper: Codeunit "E-Document Error Helper";
+ begin
+ // [SCENARIO] No additional fields are configured. A standard field validation fails.
+ // The error message should still be enriched with the field context.
+ Initialize(Enum::"Service Integration"::"Mock");
+
+ // [GIVEN] An inbound e-document is received and a draft created (no additional fields configured)
+ EDocImportParams."Step to Run" := "Import E-Document Steps"::"Prepare draft";
+ Assert.IsTrue(LibraryEDoc.CreateInboundPEPPOLDocumentToState(EDocument, EDocumentService, 'peppol/peppol-invoice-0.xml', EDocImportParams), 'The draft for the e-document should be created');
+
+ // [GIVEN] The draft has an invalid currency code
+ EDocumentPurchaseHeader.GetFromEDocument(EDocument);
+ EDocumentPurchaseHeader."Currency Code" := 'BADCURR';
+ EDocumentPurchaseHeader.Modify();
+
+ // [WHEN] Finalizing the draft
+ EDocImportParams."Step to Run" := "Import E-Document Steps"::"Finish draft";
+ EDocImport.ProcessIncomingEDocument(EDocument, EDocImportParams);
+
+ // [THEN] The e-document should have an error
+ EDocument.Get(EDocument."Entry No");
+ Assert.IsTrue(EDocumentErrorHelper.HasErrors(EDocument), 'The e-document should have errors');
+
+ // [THEN] The error message should contain the Currency Code field context
+ ErrorMessage.SetRange("Context Record ID", EDocument.RecordId());
+ ErrorMessage.SetRange("Message Type", ErrorMessage."Message Type"::Error);
+ ErrorMessage.FindFirst();
+ Assert.ExpectedMessage('Currency Code', ErrorMessage."Message");
+
+ // [THEN] No purchase invoice should have been created
+ PurchaseHeader.SetRange("E-Document Link", EDocument.SystemId);
+ Assert.RecordIsEmpty(PurchaseHeader);
+ end;
+
[Test]
procedure PreparingPurchaseDraftFindsItemReference()
var
@@ -792,7 +1121,7 @@ codeunit 139883 "E-Doc Process Test"
Currency.Init();
Currency.Validate(Code, 'XYZ');
if Currency.Insert(true) then
- LibraryERM.CreateExchangeRate(Currency.Code, WorkDate(), 1.0, 1.0);
+ LibraryERM.CreateExchangeRate(Currency.Code, Today(), 1.0, 1.0);
EDocument.DeleteAll();
EDocumentServiceStatus.DeleteAll();