French CTC: split into per-flow addons + validation fault-ignore mechanism (APP-509)#820
French CTC: split into per-flow addons + validation fault-ignore mechanism (APP-509)#820alvarolivie wants to merge 39 commits into
Conversation
Introduce two new French CTC addons — reporting (Flow 10, on bill.Invoice and bill.Payment) and lifecycle status (Flow 6, on bill.Status) — and reorganise Flow 2 under addons/fr/ctc/flow2/ so the three variants sit side by side. Flow 10 (e-reporting) covers B2B and B2C invoices plus B2B and B2C payments, gated by a b2c tag. Validates billing mode (G1.02), allowed UNTDID document types, final-after-advance invariant (G1.60), currency convertibility to EUR, address country, supplier/customer legal scheme (G2.19), VAT ID when scheme is SIREN/EU-VAT (G2.33), exempt categories, G1.24 rate whitelist on invoices and payments, B2C transaction category (G1.68), and payment receipt/lines/document refs. Normalizes SIREN and EU VAT identities from TaxID, defaults billing mode (M1/M2) and the B2C category (TNT1), and maps rate keys to UNTDID 5305 categories. Flow 6 (CDV lifecycle) is standalone — does not require Flow 2. Carries the authoritative tables gobl.cii reads for the CDAR round-trip: ProcessConditionCode 200–213 (extends bill.StatusEvents with 8 France-specific keys), 45 ReasonCodes with bucket + default-for-key flag, 7 RequestedActionCodes. Three extensions (fr-ctc-role, fr-ctc-reason-code) plus a Characteristic complement covering MDT-207 and the MEN for paid lines. Validations for the rules surfaced in BR-FR-CDV (supplier SIREN, doc code/issue-date, reason required on rejection-like statuses, TypeCode whitelist, characteristic ReasonCode link to sibling Reason); the Type-dependent role and recipient routing rules are intentionally left to PPF-side business logic. The Flow 2 move (addons/fr/ctc/ → addons/fr/ctc/flow2/) renames the package to flow2 and shortens Flow2Key/Flow2V1 to Key/V1. addons.go blank-imports all three flows. Tests are one _test.go per source file in each package (internal package so unexported helpers are reachable), with individual named tests wired to tiny per-axis helpers instead of table-driven loops. Coverage: flow2 93.0%, flow10 90.1%, flow6 96.4%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #820 +/- ##
==========================================
+ Coverage 93.50% 93.80% +0.29%
==========================================
Files 369 379 +10
Lines 20259 20599 +340
==========================================
+ Hits 18944 19323 +379
+ Misses 877 858 -19
+ Partials 438 418 -20 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
- Five GOBL example documents covering the new addons: - Flow 10 B2B invoice, B2C invoice, B2B payment - Flow 6 accepted status, paid status with MEN complement - Flow 6 now enforces exactly one StatusLine per bill.Status (CDAR carries a single status per CDV message) - Tighten Flow 2 description copy Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each source file now matches a single domain: bill_invoice.go owns the invoice rules, normalizers, and shared VAT-key map; bill_payment.go owns the payment rules and helpers. The catch-all bill.go file is removed; tests follow the same split. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merge bill.go (invoice helpers) and bill_invoices.go (invoice rules) into a single bill_invoice.go matching the flow10 layout. Tests follow the rename. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Tighten descriptions on rules 03/04 (corrective preceding), 09 (factoring vs advance-payment doc type), 24/25 (consolidated credit note contracts) so each message says what it actually checks. - Default the flow2 billing mode the same way flow10 does — M2 if Totals.Paid(), M1 otherwise; user-supplied values are preserved. - Auto-fill the three BR-FR-05 regulatory mentions (PMT / PMD / AAB) with minimal, business-neutral text when missing. - Auto-fill the BR-FR-CO-14 TXD / MEMBRE_ASSUJETTI_UNIQUE note when the supplier carries an STC-scheme (0231) identity. - Regenerate data/ artefacts via go generate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The flow2 test files were external (package flow2_test) so the defensive nil / wrong-type paths in unexported helpers were unreachable, dragging coverage below the rest of the suite. Switching to package flow2 lets the same one-test-per-source layout reach the helpers; adding a focused set of nil/wrong-type tests pushes coverage to 97.5%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lint flagged "TXD", "MEMBRE_ASSUJETTI_UNIQUE", and "peppol" as repeated literals. Lift the first two into noteSubjectTXD / stcMembreAssujettiUnique constants in flow2/bill_invoice.go, and switch all "peppol" references to the existing org.InboxKeyPeppol constant. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pull "BAR" / "B2B" out of bill_invoice.go into noteSubjectBAR and barTreatmentB2B so the goconst lint stops complaining and the isB2BTransaction / notesValidBARText helpers read clearly. No behaviour change: the BAR note is intentionally not auto-defaulted — its value (B2B / B2BINT / B2C / OUTOFSCOPE / ARCHIVEONLY) is transaction-specific and must come from the caller. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per BR-FR-22 ("règle à exécuter si la facture fait l'objet d'un
traitement B2B ou si elle contient une note (BG-1) avec un code sujet
(BT-21) = BAR et un contenu (BT-22) = B2B"), the B2B-specific rules
should fire whenever flow2 is in scope — flow2 *is* the B2B addon —
unless the caller explicitly opts the invoice out via a BAR note with
a non-B2B treatment (B2BINT / B2C / OUTOFSCOPE / ARCHIVEONLY).
Previously isB2BTransaction returned true only when an explicit BAR=B2B
note was present, which silently let invoices skip the customer-SIREN,
supplier-SIREN-inbox and self-billed-customer-inbox checks. Flip the
default so absence of a BAR note keeps the B2B path active.
The international b2bint example is updated to carry an explicit
BAR=B2BINT note to opt out, with no Key on the note so the en16931
normalizer doesn't rewrite the subject code based on note.Key.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR expands the France CTC support by introducing two new addons (Flow 10 e-reporting and Flow 6 lifecycle statuses), while reorganizing and tightening the existing Flow 2 addon implementation and updating rules, schemas, and examples accordingly.
Changes:
- Add
fr-ctc-flow10-v1(Flow 10) addon with scenarios/tags, validation + normalization for invoices and payments, and new examples. - Add
fr-ctc-flow6-v1(Flow 6) addon forbill.Statuslifecycle messages, including aCharacteristiccomplement schema, code tables, validation/normalization, and new examples. - Move/rename Flow 2 into
addons/fr/ctc/flow2(package rename + API rename), add normalization defaults (billing mode + required mentions), and refine rule descriptions.
Reviewed changes
Copilot reviewed 48 out of 54 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/ops/bulk_test.go | Updates schema list fixture to include the new Flow 6 characteristic schema. |
| examples_test.go | Skips an additional path during examples scanning. |
| examples/fr/status-fr-fr-ctc-flow6-paid.yaml | New Flow 6 “paid” status example (includes characteristic/MEN). |
| examples/fr/status-fr-fr-ctc-flow6-accepted.yaml | New Flow 6 “accepted” status example. |
| examples/fr/payment-fr-fr-ctc-flow10-b2b.yaml | New Flow 10 B2B payment receipt example. |
| examples/fr/out/status-fr-fr-ctc-flow6-paid.json | Generated output for the Flow 6 “paid” status example. |
| examples/fr/out/status-fr-fr-ctc-flow6-accepted.json | Generated output for the Flow 6 “accepted” status example. |
| examples/fr/out/payment-fr-fr-ctc-flow10-b2b.json | Generated output for the Flow 10 B2B payment example. |
| examples/fr/out/invoice-fr-fr-ctc-flow10-b2c.json | Generated output for the Flow 10 B2C invoice example. |
| examples/fr/out/invoice-fr-fr-ctc-flow10-b2b.json | Generated output for the Flow 10 B2B invoice example. |
| examples/fr/out/invoice-fr-de-ctc-b2bint.json | Updates generated output (adds BAR note + digest changes). |
| examples/fr/invoice-fr-fr-ctc-flow10-b2c.yaml | New Flow 10 B2C invoice input example. |
| examples/fr/invoice-fr-fr-ctc-flow10-b2b.yaml | New Flow 10 B2B invoice input example. |
| examples/fr/invoice-fr-de-ctc-b2bint.yaml | Updates input example to include BAR note. |
| data/schemas/tax/addon-list.json | Registers Flow 6 and Flow 10 addon keys in the addon list schema. |
| data/schemas/addons/fr/ctc/flow6/characteristic.json | New JSON Schema for the Flow 6 characteristic complement. |
| data/rules/fr-ctc-flow6.json | New rule definitions for Flow 6 validations. |
| data/rules/fr-ctc-flow2.json | Updates Flow 2 rule descriptions for clarity/accuracy. |
| data/rules/fr-ctc-flow10.json | New rule definitions for Flow 10 validations. |
| data/addons/fr-ctc-flow6-v1.json | New addon definition for Flow 6 (extensions + sources). |
| data/addons/fr-ctc-flow2-v1.json | Updates Flow 2 addon definition text (removes currency conversion note). |
| data/addons/fr-ctc-flow10-v1.json | New addon definition for Flow 10 (extensions, tags, scenarios). |
| addons/fr/ctc/item_test.go | Removes old CTC-level item meta validation tests (moved under Flow 2 tests). |
| addons/fr/ctc/flow6/org_party_test.go | Adds Flow 6 party extension/scheme validation tests. |
| addons/fr/ctc/flow6/org_party.go | Adds Flow 6 org.Party validation for role code + allowed identity schemes. |
| addons/fr/ctc/flow6/flow6.go | Registers Flow 6 addon, schema objects, rules, and normalizer. |
| addons/fr/ctc/flow6/extensions_test.go | Tests helper for unwrapping extensions passed by pointer/value. |
| addons/fr/ctc/flow6/extensions.go | Defines Flow 6 extensions + builds reason-code value list from table. |
| addons/fr/ctc/flow6/complements.go | Defines Flow 6 Characteristic type + MDT-207 type-code whitelist. |
| addons/fr/ctc/flow6/codes_test.go | Tests Process/Reason/Action code-table mappings and round-trips. |
| addons/fr/ctc/flow6/codes.go | Implements ProcessCondition/Reason/Action code tables and lookups. |
| addons/fr/ctc/flow6/bill_status_test.go | Adds Flow 6 status validation + normalization tests. |
| addons/fr/ctc/flow6/bill_status.go | Implements Flow 6 bill.Status/bill.Reason/bill.Action validation + normalization. |
| addons/fr/ctc/flow2/tags.go | Renames package from ctc to flow2. |
| addons/fr/ctc/flow2/org_test.go | Updates Flow 2 tests for package/API rename; adds/moves item meta tests here. |
| addons/fr/ctc/flow2/org_party.go | Renames package to flow2. |
| addons/fr/ctc/flow2/org.go | Renames package; switches inbox peppol key handling to constants. |
| addons/fr/ctc/flow2/flow2.go | Renames Flow 2 package and exports (Key/V1); updates registration/guard. |
| addons/fr/ctc/flow2/extensions.go | Renames package to flow2. |
| addons/fr/ctc/flow2/bill_invoice.go | Adds Flow 2 normalization defaults (billing mode + required mentions + TXD note) and BAR logic; updates rule descriptions. |
| addons/fr/ctc/flow10/tags.go | Adds Flow 10 B2C tag definition for invoices/payments. |
| addons/fr/ctc/flow10/scenarios.go | Adds Flow 10 scenarios mapping invoice types/tags → UNTDID document types. |
| addons/fr/ctc/flow10/party_test.go | Adds unit tests for Flow 10 party normalization/helpers. |
| addons/fr/ctc/flow10/party.go | Adds Flow 10 party normalization + legal scheme selection helpers. |
| addons/fr/ctc/flow10/flow10.go | Registers Flow 10 addon, rules, scenarios, tags, and normalizer. |
| addons/fr/ctc/flow10/extensions.go | Defines Flow 10 extensions and billing mode / B2C category values. |
| addons/fr/ctc/flow10/bill_payment_test.go | Adds Flow 10 payment validation tests. |
| addons/fr/ctc/flow10/bill_payment.go | Adds Flow 10 payment validation (receipt-only, value date, VAT whitelist, supplier SIREN, B2B doc refs). |
| addons/fr/ctc/flow10/bill_invoice_test.go | Adds Flow 10 invoice validation + normalization tests (B2B/B2C). |
| addons/fr/ctc/flow10/bill_invoice.go | Adds Flow 10 invoice validation + normalization (billing mode default, VAT whitelist, exempt rules, party scheme rules). |
| addons/fr/ctc/bill.go | Removes old shared CTC bill helpers (now Flow 2–scoped). |
| addons/addons.go | Registers new Flow 2/6/10 addon packages instead of the old monolithic CTC package. |
| CHANGELOG.md | Documents addition of Flow 6 and Flow 10 addons. |
Comments suppressed due to low confidence (1)
addons/fr/ctc/flow2/bill_invoice.go:838
- notesValidBARText currently allows a BAR note with an empty Text value to pass validation, but the corresponding rule message states the BAR text must be one of the allowed treatment values. If a BAR note is present, its Text should be non-empty and within allowedBARTreatments; otherwise return false so invalid/blank BAR notes don’t silently disable B2B processing.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The b2c distinction is naturally captured by Customer presence — a B2C sale is to an unidentified consumer, so the Customer slot is left unset. Drop the addon-specific TagB2C tag, the tags.go file, and the Tags wiring on the addon definition; flip invoiceIsB2C / paymentIsB2C to read Customer == nil. The b2c example loses its $tags and customer block accordingly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The supplier-SIREN rule fires on both B2B and B2C, so the addon must also derive a SIREN-scheme identity from a French TaxID for B2C suppliers that ship without an Identities entry. Move normalizeParty calls before the B2C early return — Customer is nil for B2C so its call is a harmless no-op — and keep the B2C-specific bits (default TNT1 category) after. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The per-code Go comments said "Deposit of an already paid X invoice" (and similar) which contradicted the documented suffix semantics — the 2-suffix means "already paid", not "deposit". Rewrite each comment to state the actual meaning (B/S/M = goods / services / mixed; suffix = payment context), and add a header summarising the convention. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Require Issuer and Recipient (MDG-16, MDG-23) with valid roles per BR-FR-CDV-CL-03 / CL-04; recipient must carry an inbox unless its role is WK or DFH (BR-FR-CDV-08). - Add a CDV "side" mapping (Annexe A "Acteurs CDV") that pins each process code to buyer-issued / seller-issued / platform-issued, and use it in normalize to fill the fr-ctc-role on Issuer and Recipient when the caller leaves it empty. - Propagate the SE-roled party's SIREN onto Supplier when missing so ref.IssuerTradeParty (MDT-129) is populated without forcing the caller to repeat the seller in a separate slot. - Add the BR-FR-CDV-CL-09 per-status allowed-reason-code table and validate it at the bill.Status level. - Reuse stock GOBL keys: paid + update -> 211, paid + response -> 212, acknowledged + response -> 202, querying + response -> 208 (drop payment-forwarded / received-by-platform / suspended additions). - Move the BR-FR-CDV-14 paid-MEN check to the status level so it only fires for response-phase paid (CDV-212), not transmission (CDV-211). - Refresh the FR Flow 6 examples to match. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Share the inbox-when-not-WK/DFH helper across issuer and recipient. CDAR DocumentTypeCode 23 is always the case for this addon, so BR-FR-CDV-08 applies unconditionally to both parties; malformed imports are rejected on the CII side instead. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror the paid/211/212 pattern: the `issued` key now covers both update/200 (Déposée) and response/201 (Émise par la plateforme), with Status.Type distinguishing the two. Callers reporting platform-side issuance no longer need a separate event key. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add fr-ctc-status-code, populated by the normalizer from (line.Key, Status.Type) and cross-checked at validation time. Makes the wire-level event identifier visible on the GOBL document and gives callers round-tripping a parsed CDV a place to pin a specific code. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror normalizeReason: the ext and the (key, type) pair are two sides of the same lookup. When the caller pins a CDAR ProcessConditionCode but leaves the line's Key (and the Status.Type) blank, the normalizer fills them from the process table. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Folds the three former sub-addons (fr-ctc-flow2-v1, fr-ctc-flow6-v1, fr-ctc-flow10-v1) into a single fr-ctc-v1 addon under addons/fr/ctc. Invoice rule sets are now dispatched at validation time based on whether both parties resolve as French (SIREN identity or French tax ID): both French → Flow 2 clearance, otherwise → Flow 10 e-reporting. Flow 6 lifecycle messages (bill.Status) and payment receipts (bill.Payment) run their own rule sets unconditionally. eu-en16931-v2017 is no longer a hard Requires; the Flow 2 branch enforces it as a soft assertion so Flow 10 / Flow 6 callers don't have to pull it in. Examples renamed for clarity: payment-fr-fr-ctc-flow10-b2b is replaced by payment-fr-de-ctc-b2bint (cross-border B2B receipt), and invoice-fr-fr-ctc-flow10-b2b is removed (a domestic FR-FR B2B invoice belongs in Flow 2, not Flow 10). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…/ status Ports the deleted flow2/flow6/flow10 test files into the consolidated addons/fr/ctc package: - codes_test.go: CDAR ProcessConditionCode / ActionCode / ReasonCode round-trip + lookup misses - extensions_test.go: extValue defensive branches - org_test.go: SIREN-inbox validation, peppol-key normalisation, identity scheme format (BR-FR-CO-10), private-id (0224) normalisation, SIREN-from-SIRET derivation, party / inbox / item meta edge cases (flow2 + flow10 + flow6 merged) - bill_payment_test.go: payment receipt rules — VAT rate whitelist, supplier SIREN, per-line invoice refs for B2B - bill_status_test.go: Flow 6 lifecycle rules — exactly-one-line, SIREN propagation, MEN amount on paid response, reason-code link, MDT-207 type codes - helpers_test.go: shared addonContext / runNormalize / party builders Coverage rises from 0% to ~57%. bill_invoice tests still to port. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Combines the deleted flow2/bill_invoice_test.go and flow10/bill_invoice_test.go into a single test file against the merged addon. Adaptations: - declare eu-en16931-v2017 alongside fr-ctc-v1 on Flow 2 fixtures (the addon no longer hard-Requires it; rule 02 enforces it softly) - carry iso-scheme-id ext on every identity so BR-FR-CO-10 accepts them - swap renamed helpers (extensionsValue -> extValue, invoiceIsB2BAny -> invoiceIsCrossBorderB2BAny, normalizeInvoiceBillingMode -> normalizeBillingMode) - drop BAR-note dispatcher tests (the merged code routes on party residency, not on the BAR note); Flow 10 cross-border cases now use deCustomerWithVATID() - match on rule messages instead of the renumbered fault codes Coverage rises from ~57% to 92% on the fr-ctc package. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes the addon-wide org.Party identity scheme allow-list. It was a Flow 6 (CDV) constraint that had crept into the addon-wide ruleset, rejecting legitimate Flow 10 cross-border B2B invoices whose foreign counterparty carries a secondary identifier in a scheme outside the French / CDAR list. - org.go: keep only allowedFlow6IdentitySchemes; STC (0231) stays out - org_party.go: drop rule 04 (partyIdentitySchemeAllowed) and the helper; SIRET/SIREN coherence + scheme-format checks remain - bill_status.go: new rule 22 enforces allowedFlow6IdentitySchemes across the four party slots on a bill.Status (Supplier, Customer, Issuer, Recipient), so STC and other non-CDV schemes are still rejected where they actually matter - Tests: add the STC invoice happy-path (now reachable because rule 04 no longer blocks 0231), the tax-rep exempt path via a non-EU supplier, and the CDV-rejects-STC case; replace the now-obsolete "unknown scheme rejected addon-wide" test with its inverse Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts: # addons/fr/ctc/bill_test.go # addons/fr/ctc/org_test.go # internal/ops/bulk_test.go
- bill_invoice.go:898: collapse the if-then-return / return-true pair into a single `return strings.HasPrefix(...)` so staticcheck S1008 stops flagging it - defensive_test.go: add nil / wrong-type / empty-slice tests for the predicate helpers and normalisers whose defensive guards were previously unreached (setPartyRoleDefault, partyHasRole, partyHasInboxWhenRequired, ensureSIRENOnSupplier, isPartyIdentitySTC, the is*Invoice family, statusReasonCodesAllowed, etc.). Coverage: 91.7% -> 94.6% Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- bill_status.go: rename siRENFromSEParty -> sirenFromSEParty (S/N1)
- extensions.go: rewrite fr-ctc-role values + descriptions to match
the French CTC specification of MDT-158 (CDAR RoleCode). Labels
for WK and DFH were generic UNCL 3035 ("Work/Service receiver",
"Delivery From") but CDAR assigns them CDAR-specific meanings:
WK = dematerialisation platform / operator, DFH = Portail Public
de Facturation. Updated all role names (BY Acheteur, DL Affactureur,
AB Agent d'acheteur, SR Agent de vendeur, etc.) and added French
translations.
- bill_status.go: align BR-FR-CDV-08 comment with the corrected WK /
DFH meanings.
- org.go: normalizeIdentity now maps SIREN Type -> iso-scheme-id
0002 and SIRET Type -> 0009 directly, so downstream validators can
rely on the scheme-id ext being present even when eu-en16931 is
not declared (Flow 6 / standalone Flow 10).
- Updated TestPrivateIDNormalization to reflect the new contract.
Copilot comments on the deleted flow10 files (early-return party
normalisation; inverted billing-mode comments) are already resolved
by the consolidation merge.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- tax/addons.go: HasAddon now takes ...cbc.Key. Returns true when the object declares at least one of the listed addons (semantics symmetric with AddonIn). Backwards-compatible with single-key callers. - addons/fr/ctc/bill_invoice.go: drop the local invoiceHasEN16931Addon helper; rule 02 now reads `tax.HasAddon(en16931.V2017)` directly. - addons/fr/ctc: rename helpers_test.go -> ctc_test.go (paired with ctc.go) and dissolve defensive_test.go — its nil / wrong-type / empty-slice tests are redistributed to bill_invoice_test.go, bill_status_test.go, codes_test.go and org_test.go alongside the source they exercise. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aligns naming with the underlying value (G1.24 is a whitelist of VAT percentages, not a tax-rate object). Renames cover the variable, the predicate helpers, is.Func labels, rule messages and tests: - allowedVATRates -> allowedVATPercents - invoiceVATRatesAllowed -> invoiceVATPercentsAllowed - paymentVATRatesAllowed -> paymentVATPercentsAllowed - TestPaymentVATRate* -> TestPaymentVATPercent* - TestInvoiceB2CVATRateNot* -> TestInvoiceB2CVATPercentNot* Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ill schemas - Switch the two example status-system YAMLs (anomaly, shutdown) to the new `other` line key; arbitrary custom keys are no longer accepted now that bill.StatusLine keys are a closed set with an explicit Other entry. - Regenerate the four affected example output snapshots (es status, fr flow10 invoices) to pick up the recent normalization changes (untdid-tax-category on VAT combos, line-key fixes). - Add bill/action, bill/fault, bill/reason, bill/status-line to the schemas golden list in internal/ops/bulk_test.go so the schemas bulk-action test reflects the new bill types. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Picks up the new bill types (Action, Fault, Reason, StatusLine), the closed-set status line key change, the STATUSLINE-02 assertion message fix, and the StatusLineOther entry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Linter (golangci-lint):
- gofmt addons/fr/ctc/flow10/extensions.go and addons/fr/ctc/flow2/org.go
- correct the bill.Fault type doc comment ("Faults" -> "Fault")
- rename the `any`-shadowing locals in rules/is/any_of_test.go
- remove the unused issuerParty/recipientParty test helpers in flow6
Coverage: add direct unit tests for the new guard/predicate helpers
introduced by this PR, which were previously exercised only indirectly:
- bill.PaymentTypeIn / StatusTypeIn / StatusLineKeyIn (bill/guards_test.go)
- org.IdentitiesExtensionIn (org/identity_test.go)
- flow2 predicates: precedingDocCodeValid, notesHaveTXD,
finalInvoiceAdvancesMatch, finalInvoicePayableZero, identitiesNoDupExt
- flow10 predicates: partyHasVATCode, invoiceHasSellerVATIDForExempt,
invoiceHasExemptTaxNote
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add direct unit tests for the flow2/flow6/flow10 normalizers, party helpers and rule predicates that were previously exercised only indirectly (or not at all): - flow2: org normalizers/helpers (normalizeParty, ensureSIRENIdentity, identity & inbox validators, meta/scheme guards) and bill_invoice predicates (billing mode, STC note, attachments, due dates, notes, tax-ext guards). - flow6: org normalizers/validators, extValue, partyRoleKnown, the reason-code->key reverse mapping, and prepareStatusWithLine. - flow10: org scheme/identity helpers and bill VAT-percent / exempt predicates. Also remove dead production helpers in flow6 that were referenced only by their own tests (statusPartiesIdentitySchemesAllowed, partyHasRole, ensureSIRENOnSupplier), and simplify flow2's ensureIdentity to ensureSIRENIdentity since it only ever creates SIREN identities (fixes a golangci-lint unparam finding). Package coverage: flow2 ~98%, flow6 ~97%, flow10 ~99%. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- AddAddons: append, skip-empty, skip-duplicate, in-call dedupe, no-op and nil-receiver branches. - SetAddons: set and wholesale-replace. - ExtractNormalizersForNew: nil object, non-addons object, unseen-key collection with seen-map update, already-seen skipping, idempotent repeat pass, and the incremental meta-addon case where a second pass after AddAddons returns only the newly-introduced addon. All three reach 100% coverage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| // of the suggested values. | ||
| func IdentitiesExtensionIn(key cbc.Key, value ...cbc.Code) rules.Test { | ||
| return identitiesTest{ | ||
| desc: fmt.Sprintf("has a ext [%s] in [%s]", key, strings.Join(cbc.CodeStrings(value), ", ")), |
The French CTC addons carried ~126 rules (~half re-implementing the
format-spec business rules a converter's Schematron already enforces).
Per the legal-substance-vs-format barrier, those format rules move to
the external gobl.frctc converter; GOBL keeps only what protects its
own data model.
- flow2/flow6/flow10 rule sets reduced to extension-value integrity
(registered codes + per-document-type partitions) and supported-shape
guards (status/payment type, recognised status-line key). Rules go
~126 -> ~10; every custom is.Func predicate and orphaned code-list is
removed.
- Extensions, normalizers and scenarios are unchanged; normalized output
is byte-identical (example snapshots regenerate with no diff).
- Package docs + a new addons/README.md "Scope: data-model integrity vs.
format compliance" section + CHANGELOG document the split.
- Regenerated data/rules/fr-ctc-flow{2,6,10}.json (data/addons unchanged).
- Pruned tests that asserted removed-rule faults; kept normalizer and
surviving-rule coverage.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Part 2 removed the rule test files that incidentally covered the normalizers that stay in GOBL, dropping codecov below the patch threshold. Add focused normalizer/dispatch unit tests so each fr/ctc package is back above 98%: - flow2 -> 98.4% - flow6 -> 100% - flow10 -> 99% - ctc -> 100% Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Superseded by #847. That PR takes a different direction: the general-purpose features developed here (bill lifecycle, envelope fault-ignoring, rules OneOf/AnyOf + by-code fault dropping) move into core, and the French CTC addon itself moves out of core into a standalone, opt-in module — https://github.com/invopop/gobl.fr.ctc — recognised via core's new approved external-addon registry. Closing in favour of #847. |
Restructures French CTC support around a clear split: GOBL owns the
data model; the converter owns format compliance.
French CTC addons (
addons/fr/ctc/)The previously-monolithic
fr-ctc-v1is a meta-addon that auto-dispatcheson document content, plus three flow sub-addons callers can also declare
directly:
fr-ctc-flow2-v1— domestic B2B clearance invoices (requireseu-en16931-v2017)fr-ctc-flow6-v1—bill.Status/bill.Paymentlifecycle (CDV)fr-ctc-flow10-v1— B2C and cross-border B2B e-reportingEach addon owns its extensions (role / reason / status / condition / action
codes, billing modes, B2C categories — in
catalogues/dgfip+ the flowpackages), their normalization (forward+reverse code↔key mapping, identity
scheme tagging, SIREN derivation, role/billing-mode/category defaults), and its
scenarios (UNTDID 1001 document-type mapping).
Validation is integrity-only
After review against a legal-substance vs. format-specification barrier, the
addons keep only the rules that protect their own data model:
(
tax.ExtensionHasValidCode/tax.ExtensionsHasCodes);recognised status-line key).
Every rule sourced from the format specification (
BR-FR-*,BR-FR-CDV-*,G1.*/G2.*— invoice-code format, SIREN-inbox, VAT-percent whitelists,per-process-code reason allow-lists, final-invoice totals, exemption reasons, …)
has been removed — it belongs to the converter's Schematron, not to GOBL.
Rule count dropped from ~126 to ~10, and every custom
is.Funcpredicate isgone. Substantive VAT-law correctness already lives upstream in core
bill/taxcalculation, the FR regime, and EN16931.This split is documented in each addon's package doc, a new
addons/README.mdsection ("Scope: data-model integrity vs.format compliance"), and the CHANGELOG. The relocated rules will live in a
separate
gobl.frctcconverter project (out of scope for this PR).Validation framework — fault-ignore mechanism
A general way to relax a fault emitted elsewhere:
rules.Ignore(codes...)— aDefan active rule set declares to suppressspecific fully-qualified fault codes from the aggregate result, order-independent.
rules.WithIgnore(codes...)— the same as a call-siterules.Validateoption.head.Header.Ignore []rules.Code— a caller-supplied ignore list thattravels with the envelope and is covered by the envelope signature when sealed.
billmodel evolutionsbill.PaymentTypeIn,bill.StatusTypeIn,bill.StatusLineKeyInfor use inside
rules.When(replacing ad-hocis.Exprstrings).bill.StatusLinekeys are a closed set with an explicitStatusLineOtherentry;
StatusLine.JSONSchemaExtendenumerates them viaOneOf.tax.ExtractNormalizersForNew+ theCalculatenormalizer loop that lets themeta-addon append flow addons during normalization.
Pre-Review Checklist
And if you are part of the org: