cadrumo.application.invoices.self_counterparty module

Whether an invoice names the taxpayer running this app as its own counterparty.

An invoice records an operation between two parties. When the recorded counterparty is the filer themselves, the record does not describe a real operation in either direction: a received invoice would mean buying from oneself, an issued one selling to oneself. Neither is a transaction the tax system recognises as between parties, and both are conspicuous – the counterparty totals on Modelo 347 and Modelo 349 are informativas AEAT cross-checks against what the counterparty declared, so a self-dealing row is matched against the filer’s own return.

This lives in the application layer for the same reason issuer_establishment does: it is a fact about the TAXPAYER, not about the invoice, and Invoice is a pure domain record that does not reach into profile state.

The failure this guards is not hypothetical or operator-typo-shaped, and the mechanism has changed while the exposure has not. The reader no longer scans for the first checksum-valid Spanish tax id, and the field contract (INVOICE_FIELD_CONTRACTS) no longer asks only for “the supplier’s NIF/NIE/CIF”: both parties are asked for separately, by role, precisely because one identifier plus a caller’s assumption made the two indistinguishable.

What survives that fix is the ISSUED case, and it survives by construction rather than by defect. On an invoice the filer issued, the supplier slot legitimately holds the filer’s OWN identifier – the document is correct and the reading is correct – so any path that takes the supplier side as the counterparty records the taxpayer against themselves. The value is real, checksum-valid, and bound for an informativa AEAT reconciles against the counterparty’s own return. A probabilistic reader transposing the two roles on an ambiguous layout reaches the same place by a different route.

Scope note, stated rather than assumed: this treats a self-naming counterparty as always wrong, which holds for every operation this codebase can currently record. Autoconsumo (LIVA art. 9) is the one family where a taxpayer documents an operation directed at themselves, and it has no IvaCategory member here – the only trace is the registry-projected art. 104.Tres prorrata exclusion, which is declared “auto-derived from the IVA category” against a category that does not exist. If autoconsumo invoicing is ever modelled, THIS is the predicate that must learn about it; until then a self-naming counterparty is a misread, not a self-supply.

See also

issuer_established_in_tai()

Sibling profile-derived invoice predicate.

TaxpayerProfile

Owns the per-taxpayer identity fact; one profile per bucket.

counterparty_is_the_filer(*, counterparty_tax_id, profile)[source]

Return whether counterparty_tax_id names the taxpayer in profile.

Compares on same_tax_identifier(), the one shared “these name the same bearer” predicate – the same one the evidence reader’s own-identity exclusion consumes, so a document that evades one cannot evade the other. That helper deliberately asserts no checksum, which is right here: the question is identity, not validity, and a profile carrying a foreign or otherwise non-Spanish identifier must still be comparable against a document that prints it.

Parameters:
  • counterparty_tax_id (str | None) – The counterparty identifier about to be recorded, or None when the invoice carries none (a factura simplificada may legitimately omit it).

  • profile (TaxpayerProfile) – The bucket’s TaxpayerProfile.

Return type:

bool

Returns:

True only when both sides carry a value and they are the same identifier. An absent counterparty id, or a profile with no tax id, answers False – absence is not a match, and this predicate must not turn “nothing to compare” into “the same”.