cadrumo.domain.iva.invoice_classification module¶
IVA classification record for the invoice-import bridge to the ledger.
Bridges the substrate’s three IVA classification axes
(IvaCategory, IvaRateKind, IvaFlowDirection)
into one frozen pydantic record. The record captures:
The operation kind (domestic / intra-community / recargo / OSS / etc.).
The applicable rate tier (general / reduced / super-reduced / zero / exempt).
The flow direction (output / input / self-assessed reverse charge).
The derived settlement-side classification (devengada and / or deducible) — pre-computed at construction time so consumers don’t have to call
settlement_sides_for_flow()repeatedly.
Scope: this record is the standard-case construction path for
invoice lines specifically — classify_invoice_line_for_iva()
derives it from an IvaRate plus the invoice direction (issued /
received), and invoice_line_to_iva_observation() is the sole
production caller, feeding the invoice-derived Modelo 303 observations in
cadrumo.application.aggregation.modelo_bindings. For
reverse-charge, intra-community, OSS / IOSS, and other non-domestic
invoice cases, callers construct the record directly with the appropriate
IvaCategory from the substrate’s classifier output
(classify_iva()).
This is NOT the construction path for the general ledger-transaction
pipeline. Bank-transaction-derived IVA observations (payment splits,
expense-report lines, OSS / IOSS observations, recargo de equivalencia
entries resolved from already-classified ledger rows) are built directly
as IvaLedgerObservation in
cadrumo.application.aggregation.iva_ledger, whose category, rate,
and flow axes are resolved upstream (manual or LLM classification) rather
than re-derived from an IvaRate + direction pair — that pipeline
never constructs an IvaInvoiceClassification.
- class IvaInvoiceClassification(**data)[source]¶
Bases:
BaseModelFrozen pydantic record bundling the IVA classification triple and derived settlement sides.
- Variables:
category – Substrate
IvaCategoryclassifying the operation kind.rate_kind – Substrate
IvaRateKindrate tier;Nonefor operations outside the scope of IVA (out-of-scope, ERRONEOUS_INVOICE, UNKNOWN).flow_direction – Substrate
IvaFlowDirection— REPERCUTIDO (output), SOPORTADO (input), or INVERSION_SUJETO_PASIVO (self-assessed reverse charge).settlement_sides – Pre-computed frozen set of the
IvaSettlementSidecornerstones the line contributes to. Derived fromflow_directionat construction time viasettlement_sides_for_flow()so consumers don’t recompute it.
- Parameters:
data (Any)
- category: IvaCategory¶
- rate_kind: IvaRateKind | None¶
- flow_direction: IvaFlowDirection¶
- settlement_sides: frozenset[IvaSettlementSide]¶
- classify_invoice_line_for_iva(*, iva_rate, invoice_kind, on_date=None)[source]¶
Build a classification record for the standard domestic-IVA case.
Covers the most common autónomo operation: a domestic invoice issued to a Spanish customer or received from a Spanish supplier at one of the ordinary IVA rate slots (or a registry-declared exempt slot). The record’s flow direction is derived from
invoice_kind:InvoiceKind.ISSUED→IvaFlowDirection.REPERCUTIDO(the autónomo charged output IVA on a sale).InvoiceKind.RECEIVED→IvaFlowDirection.SOPORTADO(the autónomo bore input IVA on a purchase).
For reverse-charge, intra-community, export, import, recargo de equivalencia, and OSS / IOSS cases, callers construct
IvaInvoiceClassificationdirectly with the appropriateIvaCategoryfrom the substrate classifier (cadrumo.domain.iva.classification.classify_iva()).recargo_amountdefaults to zero because most lines carry none. It is a parameter here rather than something a caller sets on the returned observation, so this stays the ONE bridge from invoice metadata into the observation shape: a caller that constructed the record itself to add a recargo would be a second construction path for the same concept, free to drift from this one’s classification rules.- Parameters:
iva_rate (
IvaRate) – One of the registry-governedIvaRateslots. The out-of-scope slot is rejected — see the module docstring for the rationale.invoice_kind (
InvoiceKind) – Whether the invoice was issued (sale) or received (purchase).on_date (
date|None) – Date selecting the registry rate-tier table; today when omitted, matching the rate-slot tier projection.
- Return type:
- Returns:
A frozen
IvaInvoiceClassificationwith the derived substrate triple and pre-computed settlement-side set.- Raises:
InvoiceValidationError – If
iva_rateresolves without a rate-tier classification and cannot be handled by the standard-case helper.
- invoice_line_to_iva_observation(*, invoice_id, issued_at, invoice_kind, iva_rate, base_amount, iva_amount, deduction_fact_kind, deduction_provenance, recargo_amount=Decimal('0'), investment_asset_id=None, rectifies_ledger_id=None)[source]¶
Build an
IvaLedgerObservationfrom invoice line metadata.The runtime resolver for the substrate’s
ledger_iva_aggregationbinding source kind consumescadrumo.domain.calculations.registry.ledger_iva_bindings.IvaLedgerObservationrecords. This helper turns invoice-line metadata into the observation shape the modelo registry expects, applying the standard-case classification (domestic IVA, REPERCUTIDO for issued invoices, SOPORTADO for received). Reverse-charge and intra-community lines must construct the observation directly.The function is the canonical ledger → modelo bridge for the standard-case IVA flows. Callers iterate the invoice’s lines, call this helper for each, and pass the resulting tuple to
resolve_ledger_iva_aggregation_binding_values()to populate the modelo’s binding values.- Parameters:
invoice_id (
str) – Stable id of the source invoice (becomesledger_idon the observation).issued_at (
date) – Invoice issue date (becomestransaction_date).invoice_kind (
InvoiceKind) – Whether the invoice was issued or received.iva_rate (
IvaRate) – Registry-governed IvaRate slot for the line. An out-of-scope slot raises (the substrate-NULL category needs explicit construction).base_amount (
Decimal) – Taxable base in EUR.iva_amount (
Decimal) – IVA amount in EUR.deduction_fact_kind (
IvaDeductionFactKind|None) – Exact statutory deduction family for a received IVA input, orNonefor an issued output.deduction_provenance (
IvaDeductionClassificationProvenance|None) – Immutable evidence provenance for a received IVA input, orNonefor an issued output.recargo_amount (
Decimal) – Recargo de equivalencia charged on this line in EUR (LIVA art. 161), or zero when none was. Routed to the Modelo 303 recargo cuota casilla for the line’s rate tier via therecargo_amount_sumfact.deduction_fact_kind – Exact evidence-grounded deduction family for a received line. Output lines must leave it unset.
deduction_provenance – Immutable authority for the received line’s deduction family. Output lines must leave it unset.
investment_asset_id (
str|None) – Reciprocal investment-register identity when the deduction family is an investment acquisition.rectifies_ledger_id (
str|None) – Corrected ledger identity for a rectification.
- Return type:
- Returns:
An
IvaLedgerObservationwith the full classification triple ready for binding-resolver consumption.- Raises:
InvoiceValidationError – If the classification produces a
Nonerate_kind (e.g. wheniva_rateis outside the taxable scope).IvaRateNotFoundError – If
iva_ratenames a rate that was not in force for its tier onissued_at– a transitional food slot used outside its statutory window. The line asserts a rate the statute did not offer that day, so it is refused rather than recorded at whatever the tier happened to mean.