cadrumo.application.invoices.issuer_establishment module¶
Whether the taxpayer issuing an ISSUED invoice is established in the TAI.
RD 1619/2012 art. 6.1.d makes a factura simplificada’s counterparty tax id mandatory in three cases. Case 3.º governs the DESTINATARIO’s NIF for an operation realised in the territorio de aplicación del impuesto [TAI] whose issuer is established there: “operaciones que se entiendan realizadas en el [TAI] y el empresario o profesional obligado a la expedición de la factura haya de considerarse establecido en dicho territorio.”
Scoped to ISSUED invoices only. The party the case asks about is whoever is
“obligado a la expedición de la factura” – the issuer – and that party
differs by direction: on an ISSUED invoice it is this app’s own taxpayer, so
the case reduces to “does the destinatario’s (the counterparty’s) NIF need to
be on the document”, a question this predicate can answer. On a RECEIVED
invoice the issuer is the counterparty (the supplier), and the destinatario
whose NIF case 3.º would require is the PROFILE HOLDER – this app’s own
taxpayer, whose own NIF is not a fact this record needs to track (it is
already known) and not a field Invoice
carries. There is no second, RECEIVED-side version of this predicate to
build; the question case 3.º asks has no unknown left to answer there.
This is not the same as “a received invoice’s completeness is not our concern”. RD 1619/2012 art. 6.4, read together with LIVA art. 97.Uno, conditions the RIGHT TO DEDUCT the IVA soportado on holding a factura that meets art. 6’s content requirements: “tendrá la consideración de factura aquella que contenga todos los datos y reúna los requisitos a que se refiere este artículo.” A supplier’s non-compliant invoice is very much this taxpayer’s problem – it is the document the deduction rests on. Nothing in this codebase currently checks whether a RECEIVED invoice is complete enough to support the deduction it feeds; that is a real, adjacent, UNADDRESSED gap this module does not attempt to close (it is a different question – factura completeness for deduction purposes generally, not the narrow destinatario- NIF case 3.º asks about on the ISSUED side).
Invoice has nowhere to carry establishment
even for the ISSUED side – it is a pure domain record with no profile
dependency, by design (a domain model does not reach into repository/profile
state) – and it is not a NEW fact this app needs to learn:
TaxpayerProfile already carries
fiscal_residency (TRLIRNR
RDLeg 5/2004 art. 2), which this module reads rather than duplicating.
The approximation, stated rather than hidden, with its error direction
named. fiscal_residency is an IRPF-RESIDENCE axis (national-level:
resident in Spain vs not); TAI establishment is an IVA-TERRITORIAL axis that
excludes Canarias (IGIC territory) and Ceuta/Melilla (IPSI territory) even
though both are part of Spain. These are two different axes that happen to
look like one word. TaxpayerProfile
carries no field distinguishing a Canarias- or Ceuta/Melilla-resident
taxpayer from a mainland one, so this predicate CANNOT currently tell them
apart: a Canarias-resident autónomo reads fiscal_residency ==
RESIDENT_IRPF (true) and is NOT established in the TAI (also true), and
this predicate returns True for both.
The error direction is OVER-STRICT, which is the safer of the two possible
mistakes: for a Canarias/Ceuta/Melilla-resident issuer, this predicate wrongly
reports “established” and so wrongly demands a counterparty NIF the law does
not actually require there. It never does the reverse (it never reports
“established” as False for someone who genuinely is). No
EstablecimientoPermanente-style territorial axis exists on
TaxpayerProfile today to close this;
designing one is a real schema decision with its own blast radius and is
explicitly NOT this module’s job.
test_a_canarias_or_ceuta_melilla_resident_is_a_pinned_known_limitation in
the test suite pins the current (wrong-but-safe) behaviour so the day that
axis is added, the test fails and points at exactly what to fix.
See also
cadrumo.domain.contribuyente.renta_codes.FiscalResidencyThe two-member closed enum this predicate reads.
cadrumo.domain.deadlines.models.TaxpayerProfileOwns the per-taxpayer residency fact; one profile per bucket.
- issuer_established_in_tai(profile)[source]¶
Return whether the taxpayer issuing invoices under
profileis established in the TAI.fiscal_residency is Noneis treated asRESIDENT_IRPF, matching the field’s own documented default for engine consumers (fiscal_residency).- Parameters:
profile (
TaxpayerProfile) – The bucket’sTaxpayerProfile.- Return type:
- Returns:
Trueunless the profile declaresNON_RESIDENT_IRNR. See the module docstring for the Canarias/Ceuta/Melilla over-strict approximation (a known, pinned limitation) and the non-resident- with-permanent-establishment carve-out this predicate does not yet model.
- simplificada_requires_tax_id_for_domestic_issuer(invoice, profile)[source]¶
Return whether RD 1619/2012 art. 6.1.d case 3.º makes
invoiceneed a tax id.Case 3.º applies to a domestic operation (
counterparty_country == "ES") whose issuer – theTaxpayerProfileholder passed asprofile– is established in the TAI. Scoped tokindISSUEDonly: case 3.º asks about the DESTINATARIO’s NIF, which on an ISSUED invoice is the counterparty this predicate can evaluate; on a RECEIVED invoice the destinatario is the profile holder, whose own NIF is not a question this predicate (or this record) needs to answer – see the module docstring for why that is not the same as declaring a received invoice’s completeness out of scope generally.It is meaningful only for a factura simplificada that currently omits the tax id – an ordinaria or rectificativa already requires one unconditionally (
cadrumo.domain.invoices), and a simplificada that already carries one has nothing further to ask for.The
kindcheck is defence in depth rather than the only thing preventing a false positive:Invoice’s own class-consistency validator already refuses a received simplified invoice with no tax id, socounterparty_tax_id is Nonealone already implieskind is ISSUEDfor everyInvoicereachable through normal construction. The check is kept, and tested via a bypassed-validation construction, so this function’s own contract does not silently depend on that domain invariant never changing.This is an ADVISORY-weight fact, not a construction-time or verify-time refusal: an ordinary domestic ticket with no identified customer is common, legitimate practice, and case 3.º’s own destinatario-NIF requirement is the least certain of the three art. 6.1.d cases this codebase can evaluate (it rests on the residency approximation the module docstring states, over-strict for a Canarias/Ceuta/Melilla issuer). Callers should surface a positive result as a
Notice, never as a hard refusal.- Parameters:
invoice (
Invoice) – The invoice to evaluate.profile (
TaxpayerProfile) – The bucket’s taxpayer profile.
- Return type:
- Returns:
Truewhen the invoice is a domestic, ISSUED factura simplificada with no counterparty tax id, issued by a TAI-established taxpayer.