[Bug]: E-Documents for Germany - FindEDocumentService always selects last matching service, ignoring which service triggered the export (backport to releases/29.x) - #12155
Miljan Milosavljević (miljance) wants to merge 1 commit into
Conversation
…last matching service, ignoring which service triggered the export (microsoft#9642) <!-- Thanks for contributing to BCApps! A few things before you hit "Create pull request": - Your PR must link to an approved issue. New here? See CONTRIBUTING.md. - You must have built and run your change yourself. CI is a safety net, not a substitute. - If you used AI or an agent to write this PR, you are still the author. Read the diff, build it, and try it before requesting review. Contributing guide: https://github.com/microsoft/BCApps/blob/main/CONTRIBUTING.md Local dev environment: https://github.com/microsoft/BCApps/blob/main/LOCAL_DEV_ENV.md --> ## What & why <!-- A few sentences: what does this change do, and what problem does it solve? --> The XRechnung and ZUGFeRD exports located the E-Document Service with a FindLast() lookup by document format. When more than one service shares a format the export always picked the last one by code instead of the service that actually triggered it, so settings such as "Embed PDF in export" were read from the wrong service. The triggering service is now handed to the export instead: * XRechnung: "XRechnung Format" passes it to "Export XRechnung Document" through SetEDocumentService before Run. * ZUGFeRD: the XML is built during report rendering, in a separate "Export ZUGFeRD Document" instance spawned by the report extension, which a setter called before Run cannot reach. The new "ZUGFeRD Export Context" codeunit (EventSubscriberInstance = Manual, bound per session rather than SingleInstance so parallel exports do not share state) carries the service from "ZUGFeRD Format" into the four posted-document report extensions, which push it onto their own instance. It is public so partners can do the same in customized report extensions. HasContext() reports true only once a non-blank service was pushed, so a bound-but-empty context cannot suppress the fallback. FindEDocumentService and OnAfterFindEDocumentService are obsoleted in both codeunits but deliberately kept working under #if not CLEAN29: the calls remain, so the event still fires for existing subscribers, and when no service was provided the original FindLast lookup still runs unchanged. Customized reports that do not use the context keep behaving exactly as before. As of CLEAN29 the functions, the events and the fallback are removed and callers must provide the service through SetEDocumentService. Tests cover the new and the legacy path for both formats: the triggering service wins over a later-sorting service, the FindLast fallback still applies when no service was provided, OnAfterFindEDocumentService still fires on both paths, and the ZUGFeRD context carries a service, refuses a blank one and clears on Stop. Also adds an "E-Document for Germany" workspace file, matching the per-app workspaces the other Microsoft apps already track (EDocumentCore, PEPPOL, PeppolBE, Subscription Billing and others), and removes the duplicated "features": [ "TranslationFile" ] property from the demo data app.json, where it was declared twice. ## Linked work <!-- Required: link an approved GitHub issue using "Fixes #<number>". Microsoft contributors: also link the ADO work item with "AB#<number>" if you have one. --> Fixes microsoft#8414 [AB#651618](https://dynamicssmb2.visualstudio.com/1fcb79e7-ab07-432a-a3c6-6cf5a88ba4a5/_workitems/edit/651618) ## How I validated this - [x] I read the full diff and it contains only changes I intended. - [x] I built the affected app(s) locally with no new analyzer warnings. - [x] I ran the change in Business Central and confirmed it behaves as expected. - [x] I added or updated tests for the new behavior, or explained below why none are needed. **What I tested and the outcome** *(required — be specific: scenarios, commands, screenshots for UI changes)* I have created two E-document services for xRechnung, once with embedded PDF and once without and confirmed that functionality works as expected. <!-- Example: - Ran the new "Post and Send" action on a sales invoice in a fresh container; document posted and email queued (see screenshot). - New unit tests in MyFeatureTest.Codeunit.al pass locally; full module test suite green. - No tests added because change is comment-only / refactor with existing coverage. --> ## Risk & compatibility <!-- Anything reviewers should watch for: breaking changes, upgrade/data impact, permissions, telemetry, feature flags, follow-up work. Write "None" if there's nothing to call out. --> ## Risk & compatibility **Behavior change (XRechnung):** where several E-Document Services share the XRechnung format, the export now uses the triggering service instead of the last one by code — service-dependent settings like *Embed PDF in export* may resolve differently. This is the microsoft#8414 fix. **ZUGFeRD output is unchanged** (its export reads no service field); the context added there is groundwork only. **Deprecation:** `FindEDocumentService` / `OnAfterFindEDocumentService` are obsoleted but stay fully functional under `#if not CLEAN29` — the calls remain, the event still fires, and the legacy `FindLast()` fallback still runs when no service is provided, so existing subscribers and customized reports are unaffected today. **At CLEAN29 the fallback disappears:** callers that don't supply a service via `SetEDocumentService` (partners' customized reports reading `"ZUGFeRD Export Context"`, as the standard report extensions do) would then export with a blank service. That's why the new codeunit is public. **New public surface:** codeunit `11039 "ZUGFeRD Export Context"` and `SetEDocumentService` on both export codeunits. Session-scoped via `BindSubscription` (not `SingleInstance`, so parallel UI and Job Queue exports don't share state). No schema, upgrade, permission, telemetry or feature-flag impact. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> (cherry picked from commit 0b91a89)
|
Issue #8414 is not valid. Please make sure you link an issue that exists, is open and is approved. |
Good Sense Reviewer - Round 1Recommendation: Accept with SuggestionsWhat this PR doesThis backport passes the triggering E-Document Service into XRechnung and carries it through a session-bound ZUGFeRD context, while preserving the legacy lookup and event behavior until CLEAN30. The change addresses the root cause: export settings now come from the service that started the export, not the last service with the same format. The report extensions transfer the ZUGFeRD context into their local exporter instance, and the compatibility paths, event behavior, instance reset, and multi-service cases have focused tests. After normalizing the release guard and object ID, the patch matches the integrated implementation. Problem-solution fitFit: Strong The reported multi-service scenario is fully addressed at the service-selection point. The tests cover both XRechnung PDF settings, the legacy fallback, event compatibility, reused instances, and the ZUGFeRD report-extension path. SuggestionsS1 (🟠 Moderate): Run the target-branch build and tests Risk assessment and necessityRisk: The runtime change affects export service selection and adds public setter/context surfaces. Compatibility is preserved until CLEAN30, and the normalized patch matches the integrated implementation, but current automation has not run an app build or the tests on this target branch. Necessity: The change is needed because multiple services with the same format can otherwise apply settings from the wrong service. The backport scope is focused and includes compatibility fallbacks and regression coverage.
|
What & why
Linked work
Fixes #8414
AB#651618
How I validated this
What I tested and the outcome (required — be specific: scenarios, commands, screenshots for UI changes)
cherry-pick -xof the squash-merge commit of [Bug]: E-Documents for Germany - FindEDocumentService always selects last matching service, ignoring which service triggered the export #9642 (0b91a89) ontoreleases/29.x, with no conflicts. Its changed lines are identical to the diff of [Bug]: E-Documents for Germany - FindEDocumentService always selects last matching service, ignoring which service triggered the export #9642 (12 files, same tests included).releases/29.x, and that the new codeunit id 11042 is free and inside the app's id range there.Risk & compatibility
Behavior change (XRechnung): where several E-Document Services share the XRechnung format, the export now uses the triggering service instead of the last one by code. Service-dependent settings like Embed PDF in export may therefore resolve differently. This is the #8414 fix. ZUGFeRD output is unchanged (its export reads no service field). The context added there is groundwork only.
Deprecation:
FindEDocumentService/OnAfterFindEDocumentServiceare obsoleted but stay fully functional under#if not CLEAN30: the calls remain, the event still fires, and the legacyFindLast()fallback still runs when no service is provided, so existing subscribers and customized reports are unaffected. At CLEAN30 the fallback disappears: callers that don't supply a service viaSetEDocumentServicewould then export with a blank service. Partners' customized reports can supply it by reading"ZUGFeRD Export Context"the way the standard report extensions do, which is why the new codeunit is public.Obsolete tag on this branch: the members keep
ObsoleteTag = '30.0'and theCLEAN30guard exactly as merged to main. That matches the existing convention for obsoletions backported toreleases/29.x(e.g.Expense Event Subscriber AT:'30.0'under#if not CLEAN30). The build scripts allow30.0as an obsolete-tag version on this branch, and the Clean build mode here (CLEAN25–CLEAN29) keeps the legacy path compiled.New public surface: codeunit
11042 "ZUGFeRD Export Context"andSetEDocumentServiceon both export codeunits. Session-scoped viaBindSubscription(notSingleInstance, so parallel UI and Job Queue exports don't share state).No schema, upgrade, permission, telemetry or feature-flag impact. No conflict resolution was needed on
releases/29.x.🤖 Generated with Claude Code