cadrumo.application.ledger.country_vocabulary_advisory module

Name the country code a party stated when it named no country, without blocking.

The country rung matches against a bounded vocabulary, so a code outside it fires nothing and the ladder walks on. That refusal is correct and it is silent: the operator sees a territory that did not resolve, with no way to tell that the document did state something and that the something was XX. A blank where a typo lives is exactly the reading an operator clears without acting.

Advisory, not blocking, and the vocabulary’s own size is the reason. The bundled country vocabulary carries a bounded subset of the world’s jurisdictions. A stated code outside it is therefore an ordinary event on a live population – a Thai supplier, a Kenyan customer – and refusing to confirm the draft would make every such document unfileable until somebody committed to the registry. That is alert fatigue on exactly the operators whose documents are correct, and an alert only earns attention if every firing is a defect the operator can act on. What the operator needs here is to be told, in the words that name the fix, and left free to proceed.

Nothing is bought back by blocking, because the under-declaration this axis was narrowed to close is already shut one layer down: an unresolved country yields no customer_residency, the classification criteria do not assemble, and the zero-rated export category is unreachable. The refusal that matters is the assembly’s, and it is untouched by this module’s severity.

Two kinds, because the two failures have different owners. A code in an ISO 3166-1 user-assigned range – AA, QM-QZ, XA-XZ and ZZ in alpha-2, and their alpha-3 counterparts AAA-AAZ, QMA-QZZ, XAA-XZZ and ZZA-ZZZ – denotes no country by construction, so the document is wrong and the operator can fix it off the page. Both spellings, because a structured record states either and this module is asked about the record’s own token: naming only the alpha-2 pairs described the axis as half of what it judges, on the spelling Facturae states. A code outside those ranges that the bundled vocabulary simply does not carry may name a real jurisdiction, so the data is ours to fix and re-reading the document will settle nothing. Reporting both as “unrecognised country” would send the operator to look for a typo that is not there, on exactly the population where the fix is a registry commit. The distinction survives the move to the advisory channel: each kind carries its own notice code and its own sentence, so a JSON consumer can route them apart.

The token is read from the field that keeps it, not from the resolved one. A draft’s *_country_code is contracted alpha-2 and is populated only where the bundled vocabulary placed the record’s token, so it is empty for precisely the codes this module exists to report – and reading it made every firing above unreachable from a real document while the module’s own tests, which set that field by hand, stayed green. The verbatim token lives on supplier_stated_country_code and its sibling, and that is what is asked. A structured record stating THA for a Thai supplier was otherwise byte-identical, all the way to the operator, to one carrying no address block at all.

The judgement is borrowed, not restated. Which bucket a code falls in is asked of record_country_code_status() – the record-token sibling of the printed-value axis, which admits the alpha-3 spelling Facturae states and is the one authority the confirm path’s relief guard asks the same question of – and whether the country already settled the territory is asked of territorial_scope_for_country(). Neither rule is spelled here. A second copy of the user-assigned ranges sitting upstream of the authority that owns them is the drift this module is placed to avoid, and it would be the weaker copy.

Why it does not fire on every unresolved country. A party whose country field is empty raises nothing: an absent value is an honest absence, already reported as a missing classifier input, and naming it here would duplicate that in a shape that reads like a document defect. A party whose field holds something that is not an alpha-2 code at all raises nothing either – the status axis declines to call an address line a bad country code, because a string nobody claimed was a country is not a country the issuer got wrong.

So the question asked is not did the country resolve but did the document state a code that named nothing, where naming something would have mattered.

See also

party_attribution_advisory()

The sibling advisory, on the same non-blocking channel and projected by the same review surface.

record_country_code_status()

The authority on which of the two kinds a stated code is.

DraftDiscrepancyKind

The blocking axis this deliberately does not join.

COUNTRY_VOCABULARY_ADVISED_STATUSES: Final[frozenset[StatedCountryCodeStatus]]

The statuses this advisory reports, derived from the sentences it can say.

CATALOGUED is deliberately outside rather than mapped to anything: a code the vocabulary carries either settled the territory or refused it for Spain’s own documented reason, and neither is worth the operator’s attention. Derived from the detail table rather than hand-listed, so a status that gains a sentence is reported by construction and one that does not cannot be advised wordlessly.

class CountryVocabularyWarning(**data)[source]

Bases: BaseModel

One party’s stated country code that named no country, and which kind it is.

Variables:
  • role – The side of the document the code was stated for, in the terms the operator reads the invoice in – issuing or billed.

  • field – The draft field carrying the code – the verbatim *_stated_ one, never the resolved sibling, because the resolved sibling is empty exactly when this warning exists. The review surface emits a row per draft field, so the operator goes straight to the row that shows the token rather than to the blank one beside it.

  • stated_code – The code as the status axis matched it: stripped and upper-cased. Quoted rather than described, because an operator shown XX sees the placeholder at a glance while one told “the country did not resolve” learns nothing they can act on.

  • status – Which of the two failures this is. The machine-readable half of the distinction the advisory exists to preserve: a typo the operator fixes off the page, or a gap in our vocabulary that a registry commit closes.

  • detail – The operator-facing sentence, naming the code and its kind.

Parameters:

data (Any)

role: str
field: str
stated_code: str
status: StatedCountryCodeStatus
detail: str
class CountryVocabularyAdvisory(**data)[source]

Bases: BaseModel

Every party on one draft stating a country code that named no country.

Variables:

parties – One entry per affected party, in document order. A draft with no affected party produces no advisory at all rather than an empty one, so a caller cannot surface a warning about nothing.

Parameters:

data (Any)

parties: tuple[CountryVocabularyWarning, ...]
property fields: tuple[str, ...]

Return every affected draft field, in document order.

by_status(status)[source]

Return the warnings of one kind, in document order.

The projection the review surface needs to emit one notice per kind. Offered here rather than filtered at the call site so a second consumer cannot group the two kinds together and undo the split.

Parameters:

status (StatedCountryCodeStatus) – The kind to select.

Return type:

tuple[CountryVocabularyWarning, ...]

Returns:

The matching warnings, empty when this draft raised none of that kind.

country_vocabulary_advisory(draft, *, operation)[source]

Return what an operator must be told about this draft’s country codes, or None.

Reports a party only where the country evidence did not settle that party’s territory some other way, so every entry names a code that was actually needed.

Non-blocking by construction: the result is an advisory model with no route into confirmation_blockers(), and the conditions it reports have no member on DraftDiscrepancyKind. Making one block again takes a deliberate change to that axis, not an edit here.

Parameters:
  • draft (InvoiceDraft) – The draft to check, carrying each party’s stated country code and printed country name as the reader recovered them.

  • operation (PinnedAuthorityOperation) – Caller-owned pinned authority operation used for country vocabulary and territory resolution.

Return type:

CountryVocabularyAdvisory | None

Returns:

The advisory, or None when every stated code is catalogued, when the territories were settled anyway, or when no code was stated at all – an absent field is an honest absence and is not reported as a wrong one.

Raises:

IvaCatalogueError – When the bundled country vocabulary cannot be read. Propagated rather than softened, on the terms the resolver states: a corrupt bundled table is a defect, not an unestablished party.