Skip to content

French CTC: split into per-flow addons + validation fault-ignore mechanism (APP-509)#820

Open
alvarolivie wants to merge 37 commits into
mainfrom
fr-ctc-reporting-status
Open

French CTC: split into per-flow addons + validation fault-ignore mechanism (APP-509)#820
alvarolivie wants to merge 37 commits into
mainfrom
fr-ctc-reporting-status

Conversation

@alvarolivie
Copy link
Copy Markdown
Contributor

@alvarolivie alvarolivie commented Apr 24, 2026

Restructures French CTC support and introduces general-purpose
fault-suppression mechanisms motivated by it.

French CTC refactor (addons/fr/ctc/)

The previously-monolithic fr-ctc-v1 is replaced by a meta-addon that
auto-dispatches based on document content, plus three flow-specific
sub-addons that operators can also declare directly:

  • fr-ctc-flow2-v1 — domestic B2B clearance invoices
  • fr-ctc-flow6-v1bill.Status / bill.Payment lifecycle (CDV)
  • fr-ctc-flow10-v1 — B2C and cross-border B2B e-reporting (invoices + payment receipts)

Shared DGFiP codes now live in the new catalogues/dgfip package
(owns dgfip-billing-mode, replacing fr-ctc-billing-mode). Flow 6
exposes role / reason / status / condition / action extensions, each
with EN+FR labels and per-document-type allow-lists.

All three flow addons follow the conventions documented in the new
addons/README.md:

  • composed rules using built-in testers (tax.ExtensionsHasCodes, is.In, is.Present, …) over custom is.Func predicates,
  • sequential rule numbering per rules.For base object (with frozen numbers after release),
  • consistent assertion messages: <context/object(s)> <field> <constraint> (<BR-code>).

Validation framework — fault-ignore mechanism

A new way for addons or callers to relax a fault emitted elsewhere
(e.g. when the EXTENDED-CTC-FR profile drops EN16931 rules that GOBL's
addons/eu/en16931 enforces):

  • rules.Ignore(codes...) — a Def an active rule set declares
    to suppress specific fully-qualified fault codes from the aggregate
    result, order-independent.
  • rules.WithIgnore(codes...) — the same as a call-site
    rules.Validate option, useful for one-off converter calls.
  • head.Header.Ignore []rules.Code — a caller-supplied ignore
    list that travels with the envelope and is covered by the envelope
    signature when sealed. Targets format-conversion cases where a
    specific fault is known-acceptable for a given document.

bill model evolutions

  • New rules.Test helpers bill.PaymentTypeIn, bill.StatusTypeIn,
    bill.StatusLineKeyIn for use as guards inside rules.When (replaces
    ad-hoc is.Expr string expressions).
  • bill.StatusLine keys are now a closed set with an explicit
    StatusLineOther entry; StatusLine.JSONSchemaExtend enumerates them
    via OneOf.

Pre-Review Checklist

  • Opened this PR as a draft
  • Read the CONTRIBUTING.md guide.
  • Performed a self-review of my code.
  • Added thorough tests with at least 90% code coverage.
  • Modified or created example GOBL documents to show my changes in use, if appropriate.
  • Added links to the source of the changes in tax regimes or addons, either structured or in the comments.
  • Run `go generate .` to ensure that the Schemas and Regime data are up to date.
  • Reviewed and fixed all linter warnings.
  • Been obsessive with pointer nil checks to avoid panics.
  • Updated the CHANGELOG.md with an overview of my changes.
  • Marked this PR as ready for review.

And if you are part of the org:

  • Requested a review from Copilot and fixed or dismissed (with a reason) all the feedback raised.
  • Requested a review from @samlown.

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
Copy link
Copy Markdown

codecov Bot commented Apr 24, 2026

Codecov Report

❌ Patch coverage is 96.99647% with 68 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.87%. Comparing base (042daa7) to head (1f40c79).

Files with missing lines Patch % Lines
addons/fr/ctc/flow2/org.go 90.83% 16 Missing and 7 partials ⚠️
addons/fr/ctc/flow6/bill_status.go 96.94% 14 Missing and 1 partial ⚠️
addons/fr/ctc/ctc.go 84.90% 4 Missing and 4 partials ⚠️
addons/fr/ctc/flow2/bill_invoice.go 98.49% 3 Missing and 3 partials ⚠️
addons/fr/ctc/flow10/bill_invoice.go 98.07% 2 Missing and 2 partials ⚠️
addons/fr/ctc/flow10/org.go 97.43% 2 Missing and 2 partials ⚠️
addons/fr/ctc/flow6/bill_payment.go 96.72% 2 Missing and 2 partials ⚠️
bill/invoice.go 88.88% 1 Missing ⚠️
bill/payment.go 94.11% 1 Missing ⚠️
bill/status.go 98.52% 1 Missing ⚠️
... and 1 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #820      +/-   ##
==========================================
+ Coverage   93.50%   93.87%   +0.36%     
==========================================
  Files         369      380      +11     
  Lines       20259    21783    +1524     
==========================================
+ Hits        18944    20449    +1505     
- Misses        877      898      +21     
+ Partials      438      436       -2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

alvarolivie and others added 9 commits April 27, 2026 08:14
- 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>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 for bill.Status lifecycle messages, including a Characteristic complement 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.

Comment thread addons/fr/ctc/flow10/bill_invoice.go Outdated
Comment thread addons/fr/ctc/bill_payment.go
Comment thread addons/fr/ctc/flow10/extensions.go Outdated
alvarolivie and others added 3 commits April 27, 2026 11:22
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>
alvarolivie and others added 6 commits May 8, 2026 13:51
- 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>
@alvarolivie alvarolivie changed the title Add FR CTC Flow 10 and Flow 6 addons Consolidate French CTC into single fr-ctc addon May 11, 2026
alvarolivie and others added 2 commits May 11, 2026 15:27
…/ 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
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 39 out of 47 changed files in this pull request and generated 4 comments.

Comment thread addons/fr/ctc/bill_status.go Outdated
Comment thread addons/fr/ctc/bill_status.go Outdated
Comment thread addons/fr/ctc/bill_status.go Outdated
Comment thread addons/fr/ctc/bill_status.go Outdated
alvarolivie and others added 4 commits May 11, 2026 16:43
- 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>
@pmenendz pmenendz changed the title Consolidate French CTC into single fr-ctc addon Consolidate French CTC into single fr-ctc addon (APP-509) May 20, 2026
samlown and others added 6 commits May 20, 2026 12:39
…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>
@samlown samlown changed the title Consolidate French CTC into single fr-ctc addon (APP-509) French CTC: split into per-flow addons + validation fault-ignore mechanism (APP-509) May 29, 2026
samlown and others added 4 commits May 29, 2026 13:37
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>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 103 out of 116 changed files in this pull request and generated 1 comment.

Comment thread org/identity.go
// 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), ", ")),
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants