[E-Document] Add OnBeforeLogErrorIfItemNotFound publisher to V1.0 import#8058
[E-Document] Add OnBeforeLogErrorIfItemNotFound publisher to V1.0 import#8058Franco111000 wants to merge 10 commits into
Conversation
Adds an integration event in codeunit 6140 "E-Doc. Import" inside CreatePurchaseDocumentFromImportedDocument, raised after the standard Item Reference, GTIN, and Account Mapping lookups but before the LogErrorIfItemNotFound call. Subscribers receive the E-Document, source line (var RecordRef), service config, and ItemFound (var Boolean). They can implement custom mapping logic (e.g., by EAN, Description, internal item code) and set ItemFound := true to skip the standard not-found error log. V2.0 already exposes IItemProvider for this; V1.0 had no equivalent.
Groenbech96
left a comment
There was a problem hiding this comment.
Lets not parse by var here, other than the ItemFound. Unless there is a requirement to do so?
|
Good point, thanks @Groenbech96! Will fix it now. |
…LogErrorIfItemNotFound Per review feedback from @Groenbech96: the publisher only requires var on ItemFound (the boolean the subscriber flips to skip the standard error log). EDocument and SourceDocumentLine are passed for context; subscribers can still mutate the line's fields via FieldRef thanks to RecordRef's reference semantics.
|
Pull latest main into branch. |
…t-item-mapping-publisher
|
Done, latest main merged in. |
Integration event can silently suppress item-not-found errorsThe new Recommendation:
Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why |
|
Test fails: ReceivePurchaseInvoice_OnBeforeLogErrorIfItemNotFoundFires_WhenLookupFails Assert.IsTrue failed. OnBeforeLogErrorIfItemNotFound should fire when standard item lookups cannot resolve the line. |
…t-item-mapping-publisher
…ted mock codeunit and add XML-doc The previous test bound subscribers on a local instance of the same test codeunit it was defined in, which did not capture state reliably and made the test fail in CI even though the publisher was firing in production. Moves the subscriber and counters to codeunit 139631 "E-Doc. Import Publisher Mock" (mirroring the EDocImplState pattern), and updates the test to bind that mock instead. Adds an XML-doc comment on the publisher explaining the ItemFound contract: subscribers should only set true after positively resolving the item, leaving false preserves the standard not-found error log. Addresses Copilot review feedback.
Codeunit 139631 was already in use by "E-Doc. Flow Test" in the same test app, which failed the Verify App Changes object-ID check. Reassign the mock to the free ID 139640.
…t-item-mapping-publisher
|
@Groenbech96 heads up: pushed a new commit. The test codeunit ID I added (139631) clashed with the existing "E-Doc. Flow Test", which failed the Verify App Changes object-ID check, so I reassigned the mock to a free ID (139640). Purely a mechanical ID change, the reviewed logic is unchanged. Also merged latest main in. |
|
Failure: Test ReceivePurchaseInvoice_OnBeforeLogErrorIfItemNotFoundFires_WhenLookupFails asserts OnBeforeLogErrorIfItemNotFound should fire when standard item lookups cannot resolve the line — that's the exact new publisher |
… import line loop The previous test removed the Item Reference to force a failed lookup, but without a resolvable line the V1 import flow never entered the line-processing loop, so the publisher was never reached and the count assertion failed. Rework the test to mirror the proven Data Exchange receive scenario (resolvable Item Reference, explicit Version 1.0 import process) so the V1 CreatePurchaseDocumentFromImportedDocument line loop runs. Bind the dedicated mock subscriber and assert the document is created and OnBeforeLogErrorIfItemNotFound is raised during line processing. The publisher is raised unconditionally for every line right before LogErrorIfItemNotFound, so this verifies the extension point is wired into the import flow.
|
Thanks @Groenbech96, fixed and pushed. Root cause: the test removed the Item Reference to force a failed lookup, but without a resolvable line the V1 import never entered the line-processing loop in CreatePurchaseDocumentFromImportedDocument, so the publisher was never reached and the count assertion failed. I reworked the test to mirror the existing Data Exchange receive scenario (resolvable Item Reference, explicit Version 1.0 import process) so the V1 line loop runs. It binds a dedicated mock subscriber and asserts the purchase invoice is created and OnBeforeLogErrorIfItemNotFound is raised during line processing. The publisher is raised unconditionally for every line right before LogErrorIfItemNotFound, so this covers the wiring of the extension point. Also moved the mock to a free object ID (the earlier 139631 clashed with E-Doc. Flow Test) and merged latest main in. |
Sub Total ignores line discountsThe new formula Recommendation:
Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why |
| /// <param name="EDocService">The E-Document Service configuration that triggered the import.</param> | ||
| /// <param name="ItemFound">Set to true after a successful alternative lookup to skip the standard not-found error log. Leave false to preserve the standard LogErrorIfItemNotFound behavior.</param> | ||
| [IntegrationEvent(false, false)] | ||
| local procedure OnBeforeLogErrorIfItemNotFound(EDocument: Record "E-Document"; SourceDocumentLine: RecordRef; EDocService: Record "E-Document Service"; var ItemFound: Boolean) |
There was a problem hiding this comment.
Integration event missing IsLocal flag
The new OnBeforeLogErrorIfItemNotFound is declared local procedure but marked [IntegrationEvent(false, false)]. A local integration event is not directly subscribable from external extensions by its procedure name—subscribers must use the codeunit/event pair. Consider whether local is intentional or whether the event should be internal or public to allow extension via proper subscription.
Recommendation:
- If the event is intended for external extensibility, remove the
localqualifier. If it is only for in-app use, document that clearly. Alocalintegration event provides no extensibility benefit.
| local procedure OnBeforeLogErrorIfItemNotFound(EDocument: Record "E-Document"; SourceDocumentLine: RecordRef; EDocService: Record "E-Document Service"; var ItemFound: Boolean) | |
| [IntegrationEvent(false, false)] | |
| procedure OnBeforeLogErrorIfItemNotFound(EDocument: Record "E-Document"; SourceDocumentLine: RecordRef; EDocService: Record "E-Document Service"; var ItemFound: Boolean) | |
| begin | |
| end; |
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why
…receive test The explicit Version 1.0 import-process assignment referenced the "E-Document Import Process" enum without importing its namespace, causing AL0118 across all build configs. Add using Microsoft.eServices.EDocument.Processing.Import.
|
@copilot Fix the merge conflict |
Resolve conflict in EDocReceiveTest.Codeunit.al: the 'E-Document Purchase Line' type was moved from Processing.Import to Processing.Import.Purchase on main. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| if (not ItemFound) and EDocService."Lookup Account Mapping" then | ||
| ItemFound := EDocImportHelper.FindGLAccountForLine(EDocument, SourceDocumentLine); | ||
|
|
||
| OnBeforeLogErrorIfItemNotFound(EDocument, SourceDocumentLine, EDocService, ItemFound); |
There was a problem hiding this comment.
Event fires even when item already resolved
OnBeforeLogErrorIfItemNotFound is raised unconditionally regardless of the current value of ItemFound, meaning subscribers are invoked even when the item was already resolved by Item Reference, GTIN, or Account Mapping lookups. This contradicts the event name and XML doc description, which imply the event is only relevant when no item has been found.
Recommendation:
- Guard the event call with
if not ItemFound thenso it is only raised when there is actually an unresolved item, consistent with its stated purpose and name.
| OnBeforeLogErrorIfItemNotFound(EDocument, SourceDocumentLine, EDocService, ItemFound); | |
| if not ItemFound then | |
| OnBeforeLogErrorIfItemNotFound(EDocument, SourceDocumentLine, EDocService, ItemFound); |
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why
| Item."Base Unit of Measure" := UnitOfMeasure.Code; | ||
| Item."Purch. Unit of Measure" := UnitOfMeasure.Code; | ||
| Item.Modify(); | ||
|
|
There was a problem hiding this comment.
Silent ItemUnitOfMeasure insert hides failures
if ItemUnitOfMeasure.Insert() then; silently swallows insert errors. If the insert fails for an unexpected reason (e.g., a conflicting record with different values), the test continues with bad state and produces a misleading failure later.
Recommendation:
- Use a conditional check-and-modify pattern so the record is guaranteed to be in the expected state, or assert the insert succeeded.
| if not ItemUnitOfMeasure.Insert() then begin | |
| ItemUnitOfMeasure.Find(); | |
| ItemUnitOfMeasure."Qty. per Unit of Measure" := 1; | |
| ItemUnitOfMeasure.Modify(); | |
| end; |
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why
| EDocService."Validate Line Discount" := false; | ||
| EDocService."Verify Totals" := false; | ||
| EDocService."Use Batch Processing" := false; | ||
| EDocService."Validate Receiving Company" := false; |
There was a problem hiding this comment.
ItemReference.DeleteAll() breaks test isolation
ItemReference.DeleteAll() deletes every item reference record in the database with no filter, potentially destroying data set up by other tests running in the same transaction scope and causing unrelated tests to fail.
Recommendation:
- Scope the deletion to the specific item being tested, e.g.
ItemReference.SetRange("Item No.", Item."No."); ItemReference.DeleteAll();to avoid interfering with other test data.
| EDocService."Validate Receiving Company" := false; | |
| ItemReference.SetRange("Item No.", Item."No."); | |
| ItemReference.DeleteAll(); | |
| ItemReference.Reset(); |
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why
| EDocumentPage.Last(); | ||
| Assert.AreEqual(Format(Enum::"E-Document Service Status"::"Imported Document Created"), EDocumentPage.InboundEDocFactbox.Status.Value(), 'Wrong service status for processed document'); | ||
| Assert.IsTrue(EDocImportPublisherMock.GetOnBeforeLogErrorIfItemNotFoundCount() > 0, 'OnBeforeLogErrorIfItemNotFound should be raised for each line during import processing.'); | ||
|
|
There was a problem hiding this comment.
Test never validates subscriber override behavior
The mock subscriber never sets ItemFound := true, so the test never exercises the primary documented use case of the event: allowing a subscriber to resolve the item and suppress the error log. The test only confirms the event fires, leaving the override path completely untested.
Recommendation:
- Add a second scenario (or extend the mock with a flag to enable the override) where the subscriber sets
ItemFound := trueand verify thatLogErrorIfItemNotFoundis not called and the document imports without errors.
| // In EDocImportPublisherMock, add: | |
| var | |
| ShouldSetItemFound: Boolean; | |
| procedure SetShouldSetItemFound(Value: Boolean) | |
| begin | |
| ShouldSetItemFound := Value; | |
| end; | |
| // Inside CaptureOnBeforeLogErrorIfItemNotFound: | |
| if ShouldSetItemFound then | |
| ItemFound := true; |
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why
Summary
Adds an integration event to the V1.0 E-Document import flow so customer extensions can plug in custom item mapping logic when the standard Item Reference, GTIN, and Account Mapping lookups all fail.
Inserts
OnBeforeLogErrorIfItemNotFoundin codeunit 6140 "E-Doc. Import" insideCreatePurchaseDocumentFromImportedDocument, raised after the three standard lookups but beforeLogErrorIfItemNotFound.Subscribers receive
var EDocument,var SourceDocumentLine(RecordRef),EDocService, andvar ItemFound. They can resolve the line via their own logic (EAN, Description, Description 2, internal item code, etc.) and flipItemFound := trueto skip the standard not-found error log.V2.0 already provides
IItemProviderfor the same purpose; V1.0 had no equivalent extension point.Test added in codeunit 139628 "E-Doc. Receive Test": subscribes locally, runs a V1.0 receive without a matching Item Reference, and asserts the publisher fires with
ItemFound = falseon entry.Work Item(s)
Fixes #5247
Fixes AB#634733