Discussion: national rule layers can't be reached when BT-24 only declares the format (Factur-X / ZUGFeRD / bare EN16931) #19
Replies: 2 comments 2 replies
|
@antoine-rehm thanks for the comprehensive discussion startpoint :-) There are a couple of things I like to respond to, and where I would like to point out what others did: BT-24First of all, the
and the following usage note:
Additionally, EN 16931-1:2017 section 7.6 "Identification of CIUS" states:
Based on these definitions, my personal opinion on France's choice to use no specific BT-24 is, that I think they made a mistake. Additionally, when looking at this from a German angle, where the PDF can be thrown away and only the XML can be processed, that seems to be a really bad design choice. To my understanding, the XML must be self-contained for processing - letting it depend on the PDF makes things even worse. DDDI am totally aware, that DDD is not the best possible solution, but it proves to be helpful for all specifications that follow the intended usage of BT-24 ;-) Additionally, I am not a big fan of Country selectionIndeed using the country selection from the postal address seems to be a straight forward solution. However, e.g. in the Peppol BIS Billing Schematron rules, sometimes the countries are taken from the respective VAT number and sometimes even from the VAT number of the Tax Representative - it might be the case, that these cases don't apply to France, but they need to be kept in scope. Ad Directionscomposite VES in phive-rulesAs you pointed out, it might be more correct to use the Factur-X Extended XSD instead of the "default" CII ones. Replacing the XSD in the current VES should resolve the issues - right? ... more to come ... |
|
Thanks, the BT-24 point is something I genuinely got wrong, and your quote from EN 16931 makes it clear: a user specification is supposed to identify itself in BT-24. And I share your concern about XML-only processing: a document that cannot state which ruleset governs it isn't self-contained, which is exactly the problem I ran into. One thing worth adding, because it sharpens where the gap actually sits. The French spec does give itself an identity for the pure syntaxes — On On replacing the XSD in the current VESI think your suggestion is the right one, and I'd like to withdraw the composite-VES idea in favour of it. My initial reasoning was that the two rule sets aren't interchangeable. Comparing But on reflection that difference is the point rather than an obstacle. So replacing the XSD in On the postal-address discriminator: agreed it is not as simple as it looks, and your point about Peppol BIS rules reading VAT / Tax Representative data is something I had not considered. Happy to drop the country heuristic and rely on explicit caller-side selection instead. |
Uh oh!
There was an error while loading. Please reload this page.
First, thanks for phorm, phive-rules and
ddd— the amount of ground they cover is remarkable, and I realise maintaining all of it is a lot. I'm opening this as a question rather than a bug report: I hit a concrete case with the French mandate, but while digging I started suspecting the underlying shape is not French-specific, and I'd rather check my understanding with you before proposing anything.The concrete case
A Factur-X 1.0.9 (EXTENDED) CII invoice intended for the French mandate (Flux 2). Via
/api/dd_and_validate, phorm reports no French national findings. Mustang 2.24.0 on the same file reports two:The rules themselves are fine in phive-rules: running its own
20260216_BR-FR-Flux2-Schematron-CII_V1.3.0.xslton the document with Saxon gives byte-identical findings to Mustang. They are simply never selected.Chain:
urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extendeddddvalue-providers.xml(syntaxcii) →fr.factur-x:extended:latestfr.factur-x:*is only an alias ofde.zugferd:*(ZugferdValidation._createFacturXAlias(),ZUGFERD_TO_FACTURX_MAP);latest→de.zugferd:extended:2.5FACTUR-X_EXTENDED.xsd+FACTUR-X_EXTENDED.xslt, nothing else. Zero occurrences ofBR-FR/SIRET/SIREN; run standalone → 0 findings.dddreachesfr.ctc:extended-cii:latestonly for BT-24 =urn:cen.eu:en16931:2017#conformant#urn.cpro.gouv.fr:1p0:extended-ctc-fr.So
fr.factur-x:extended:1.0.9doesn't fail to apply BR-FR — it never contained them.Why the obvious workaround doesn't apply
"Just emit the CTC-FR BT-24 instead" doesn't work: a Factur-X hybrid invoice must declare the Factur-X guideline URN in BT-24 for its profile, consistent with the PDF/A-3 XMP
fx:ConformanceLevel. When the CII is extracted from the PDF and posted todd_and_validate, BT-24 is necessarily the Factur-X URN.Interestingly, neither schematron constrains the value of BT-24 — no occurrence of
urn:factur-x.eu:1p0:extendedinFACTUR-X_EXTENDED.xslt, and no occurrence ofcpro.gouv.frorfactur-x.euin the 1.4.0 CTC-FR / BR-FR-Flux2 sources. The national layer is orthogonal to the semantic profile; it composes cleanly on top. The obstacle is purely the routing table.The part I think may generalise
BT-24 identifies a format/profile, not a jurisdiction. So whenever a country's rules are layered on top of a format-level profile, BT-24-only routing structurally cannot reach them. Looking at the
ciisyntax invalue-providers.xml, the entries split cleanly in two:de.xrechnung:cii:*. Unambiguous, works today.de.zugferd:*,fr.factur-x:*, andeu.cen.en16931:cii:latestfor bareurn:cen.eu:en16931:2017. No national layer is reachable, by construction.France is where this bites right now, but the same shape shows up elsewhere:
XMLValidator.java:401-406), atnoticeseverity by default (l.155), raised toerroronly when BT-24 really declares xrechnung. Same pattern, different country.urn:cen.eu:en16931:2017, or Peppol BIS Billing 3.0, lands oneu.cen.en16931:cii:latest— also with no national layer.dddhas no entry forfr.ctc:cii,fr.ctc:ubl-invoiceorfr.ctc:ubl-creditnote— only theextended-*andcdarones. Those base CIUS profiles are the mainstream mandate case, and they're unreachable for exactly the same reason: their BT-24 is shared with EN16931 / Peppol.If that reading is right, then any future country layering national rules on Factur-X / ZUGFeRD / bare EN16931 CII will hit this identically — which is why I'd rather ask about the general shape than propose a France-only patch.
How Mustang resolves it
Not as an argument that Mustang is right, just as a data point on what a payload-based answer looks like.
validator/src/main/java/org/mustangproject/validator/XMLValidator.java(2.24.0).Discriminator — domestic FR transaction (l.226-230):
That is BT-40 == FR and BT-55 == FR. Not BT-24, not BT-23 — no reference to
BusinessProcess,ProcessIDorcpro.gouvanywhere in the validator package.Composition — additive, not a replacement (l.384-393):
Mustang keeps the profile's Factur-X schematron as the primary validation and adds the national layer. This applies to every CII profile that set an
xsltFilename.Worth flagging: this means rerouting to
fr.ctc:extended-cii:*would not be equivalent. That VES isEXTENDED-CTC-FR-CII.xslt+BR-FR-Flux2— it replaces the Factur-X schematron with the French EN16931 derivative instead of layering on top. So "pick a different VESID" and "add a rule layer" are genuinely different operations here.Possible directions
No strong opinion — this is where I'd value your view, especially on whether the general framing is the right one.
A — composite VES in phive-rules. Register e.g. Factur-X EXTENDED XSD +
FACTUR-X_EXTENDED.xslt+BR-FR-Flux2-Schematron-CIIas one VES (and equivalents per profile). Explicit, preserves Factur-X semantics, reproducible against Mustang. But it multiplies VES count per country × profile, and callers still need a way to select it.B — phorm-side: let
countrydrive the XML layer too.hybrid_validate?country=FRcurrently only feeds kaltblut's PDF-carrier layer (ApiPostHybridValidate,setCountry()); XML validation ignores it. Havingcountryalso select the national XML layer is self-contained, explicit, needs no payload heuristic, and generalises to any country. Would need exposing ondd_and_validatetoo.C — payload heuristic in
ddd. Two obstacles I ran into:dddreturns one VESID and has no notion of "add a layer", so C only helps combined with A.VPIfTypeis anxs:choice, so an<if>holds eitherset/flagor nestedselect, never both, andDDDValueProviderPerSyntax._getAllDeducedValuesRecursive()returns on the first<if>with determined values. "Default tofr.factur-x, override when FR/FR" can't be written; nesting a country select under the Factur-X<if>would leave every non-FR Factur-X document with no VESID at all. Two top-level selects relying on fall-through isn't deterministic either, sincem_aSelectsis aCommonsHashMap. Enabling C would need one of:set+ nestedselect(schema 1.2, nested overriding outer); an explicit<else>; or an orderedm_aSelectswith documented document-order evaluation.D — document the limitation and leave selection to the caller. Reasonable, though
dd_and_validatethen silently under-validates, which is the surprising part.Happy to do the work on whichever direction you think is right — including the
dddside — or to drop it if I've misread the design intent.All reactions