Skip to content

[PEPPOL] Use Swedish organisation number for EndpointID under scheme 0007#8646

Open
Franco111000 wants to merge 3 commits into
microsoft:mainfrom
Franco111000:fix/7723-se-peppol-endpoint-orgno
Open

[PEPPOL] Use Swedish organisation number for EndpointID under scheme 0007#8646
Franco111000 wants to merge 3 commits into
microsoft:mainfrom
Franco111000:fix/7723-se-peppol-endpoint-orgno

Conversation

@Franco111000

@Franco111000 Franco111000 commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

What and why

Swedish PEPPOL BIS 3.0 documents fail at the access point because the supplier/customer electronic address (cbc:EndpointID) and the cac:PartyLegalEntity/cbc:CompanyID are emitted as the full 14-character Swedish VAT number (for example SE733078715601) under schemeID="0007". Scheme 0007 is SE:ORGNR, which per PEPPOL-COMMON-R049 must be exactly the 10-digit Swedish organisation number (7330787156). Today only Denmark has country-specific endpoint handling (UseVATSchemeID), so Sweden falls through unformatted and the document is rejected.

This change extracts the Swedish organisation number for parties whose Country/Region VAT Scheme is 0007, in the four procedures that build the supplier and customer EndpointID and PartyLegalEntity/CompanyID. The cac:PartyTaxScheme/cbc:CompanyID (the BT-31/BT-48 VAT identifier) is deliberately left as the full VAT, which is exactly the half that the manual "trim to 10 chars" workaround described in the issue broke.

Why gate on the scheme (0007) rather than the country (ISO = 'SE'): the emitted schemeID already comes from GetVATScheme(country), so gating the value on the same 0007 keeps the value and the scheme consistent by construction. A Swedish company legitimately registered under EAS 9955 (SE:VAT) then keeps its full VAT under schemeID="9955", which an ISO-based gate would have stripped incorrectly.

The PartyLegalEntity/cbc:CompanyID schemeID stays empty for SE, as it already does for every non-DK country. PEPPOL-COMMON-R049 only applies when schemeID="0007" is present, so the bare 10-digit organisation number there is valid and matches the expected output shown in the issue.

Note: the fix takes effect when the SE Country/Region VAT Scheme is set to 0007, which is already required for any Swedish tenant sending PEPPOL (the issue's sample XML shows schemeID="0007").

Linked work

Fixes #7723

How I validated

  • Unit tests: supplier and customer EndpointID and PartyLegalEntity/CompanyID return 7330787156 under scheme 0007
  • Regression test: an SE party under scheme 9955 keeps the full VAT (not stripped)
  • Test: PartyTaxScheme/CompanyID keeps the full VAT
  • End-to-end test: a PEPPOL BIS 3.0 sales invoice export asserts both endpoints
  • Relied on PR CI for compilation and the full test run (this repo builds via AL-Go)

Risk and compatibility

  • Denmark and every country whose VAT Scheme is not 0007 are behaviourally unchanged: the new branch is only reached for 0007, and UseVATSchemeID still handles DK in the first branch.
  • Only the SE EndpointID and PartyLegalEntity company id change; the VAT identifier in PartyTaxScheme is preserved.
  • No schema, table, or API changes.

PEPPOL BIS 3.0 scheme 0007 (SE:ORGNR) requires the 10-digit Swedish
organisation number, but the EndpointID and PartyLegalEntity CompanyID
carried the full 14-char VAT (SE...01), which access points reject
(PEPPOL-COMMON-R049). Extract the organisation number for SE parties
whose Country/Region VAT Scheme is 0007, leaving the PartyTaxScheme VAT
identifier and all other countries unchanged. Add SE coverage to the
PEPPOL BIS billing tests.
@Franco111000 Franco111000 requested a review from a team as a code owner June 17, 2026 09:59
@github-actions github-actions Bot added AL: Apps (W1) Add-on apps for W1 From Fork Pull request is coming from a fork needs-approval Workflow runs require maintainer approval to start labels Jun 17, 2026
var
DigitsOnly: Text;
begin
DigitsOnly := DelChr(VATRegistrationNo, '=', DelChr(VATRegistrationNo, '=', '0123456789'));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Style} \quad \color{gray}{\texttt{\small Iteration\ 1}}$

Nested DelChr digit-extraction is non-obvious

The double-nested DelChr(x, '=', DelChr(x, '=', '0123456789')) pattern — where the inner call extracts the non-digit mask and the outer call removes it — relies on an implicit BC behaviour that is easy to misread, and any future maintainer touching this line may accidentally invert the arguments.

Recommendation:

  • Extract the pattern into a clearly named helper, or document it with an inline comment explaining the two-step inversion so its correctness is immediately obvious.
Suggested change
DigitsOnly := DelChr(VATRegistrationNo, '=', DelChr(VATRegistrationNo, '=', '0123456789'));
// Inner DelChr removes all digits, leaving only non-digit chars as the mask.
// Outer DelChr removes those non-digit chars from the original, leaving only digits.
DigitsOnly := DelChr(VATRegistrationNo, '=', DelChr(VATRegistrationNo, '=', '0123456789'));

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, thanks. Added a comment in b4b5c7604 explaining the two-step DelChr: the inner call builds the non-digit mask, the outer call removes it, leaving only the digits.

DigitsOnly := DelChr(VATRegistrationNo, '=', DelChr(VATRegistrationNo, '=', '0123456789'));
if StrLen(DigitsOnly) >= 10 then
exit(CopyStr(DigitsOnly, 1, 10));
exit(VATRegistrationNo);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Style} \quad \color{gray}{\texttt{\small Iteration\ 1}}$

Silent fallback on malformed Swedish org number

When GetSwedishOrgNo cannot extract 10 or more digits from the VAT registration number, it silently returns the raw input unchanged. This could produce an invalid PEPPOL XML document (wrong-length or non-numeric identifier under scheme 0007) with no error surfaced to the user.

Recommendation:

  • Raise an error or at minimum log a warning when fewer than 10 digits are found, so the operator is alerted to fix the VAT registration number rather than sending a silently malformed document.
Suggested change
exit(VATRegistrationNo);
local procedure GetSwedishOrgNo(VATRegistrationNo: Text): Text
var
DigitsOnly: Text;
begin
DigitsOnly := DelChr(VATRegistrationNo, '=', DelChr(VATRegistrationNo, '=', '0123456789'));
if StrLen(DigitsOnly) >= 10 then
exit(CopyStr(DigitsOnly, 1, 10));
Error('Swedish organisation number could not be derived from VAT Registration No. ''%1'': expected at least 10 digits.', VATRegistrationNo);
end;

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. This is deliberate. GetSwedishOrgNo is a low-level formatter called for both the supplier and the customer endpoints during export, mirroring the existing FormatVATRegistrationNo helpers, which also format rather than raise. A value with fewer than 10 digits can only occur when a party is configured with scheme 0007 but a malformed VAT, which already produced an invalid document before this change, so returning the input unchanged keeps behaviour no worse than today and lets the receiver's PEPPOL-COMMON-R049 schematron report the precise error. Hard-erroring here would also block a sender from exporting when it is the customer's registration that is malformed, which they may not control. Happy to add an explicit TestField-style guard further upstream (posting/setup) if you would prefer fail-fast.

@github-actions github-actions Bot removed the needs-approval Workflow runs require maintainer approval to start label Jun 18, 2026
@JesperSchulz JesperSchulz added the Finance GitHub request for Finance area label Jun 18, 2026
The new Swedish organisation-number branches used else-if, which AA0018
rejects. Split them so the if starts its own line, and document the
keep-only-digits extraction in GetSwedishOrgNo.
@Franco111000

Franco111000 commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

Pushed b4b5c7604 addressing the review feedback and the failing build:

  • Split the new Swedish organisation-number branches so each if starts its own line (the build was red on AA0018); this is purely stylistic and the logic is unchanged.
  • Documented the digit-extraction step in GetSwedishOrgNo (per the inline suggestion).
  • Merged the latest main.

Also replied inline on the silent-fallback point.

@github-actions github-actions Bot added the needs-approval Workflow runs require maintainer approval to start label Jun 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AL: Apps (W1) Add-on apps for W1 Finance GitHub request for Finance area From Fork Pull request is coming from a fork needs-approval Workflow runs require maintainer approval to start

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Can't send e-documents with Peppol BIS 3.0 invoices from SE localization

2 participants