aeat.domain.prorrata_register package

Per-ejercicio cross-period IVA prorrata register (LIVA arts. 102-106).

The provisional-to-definitive IVA prorrata lifecycle is inherently cross-year: under prorrata general only the deduction percentage of soportado is deductible in each liquidation period (art. 104.Uno); the percentage provisionally applicable each year is the prior year’s definitive (art. 105.Uno), with the regulated alternatives of an AEAT-authorised provisional (art. 105.Dos) and the inicio-de-actividades proposed percentage (art. 105.Tres via art. 111.Dos); the last liquidation of the year computes the definitive prorrata from the year’s actual operations and regularises the provisional deductions (art. 105.Cuatro).

This module is the CARRY HOME for that lifecycle: a durable per-ejercicio ProrrataRegister, one ProrrataRegisterEntry per (ejercicio, sector) carrying the regime, the provisional percentage in force with its regulated ProrrataProvisionalProvenance, and — once settled — the definitive percentage with the annual volume inputs it derived from. The pure precedence-ladder resolver (resolve_provisional_percentage()) selects the in-force provisional percentage among candidate provenances (authorised/inicio outranking the carried prior definitive) and returns a visible unresolved state rather than any fabricated default — no percentage is ever assumed.

This is a taxpayer-fact store, sibling to domain.bienes_inversion: it holds the per-ejercicio percentages and their provenance, never the regulatory constants. The prorrata compute substrate (domain.iva: compute_prorrata_definitiva_anual, compute_regularizacion_prorrata_anual) is consumed at settlement, not re-implemented here, and this module reads no secure-object store — the seed, in-year apportionment, and settlement write-back live in the application layer.

See also

adapters.persistence.profile.prorrata_register

FINANCIAL secure-object repository that stores the register singleton.

domain.iva

Legal prorrata substrate that computes the definitive percentage from annual volumes and the art. 105.Cuatro regularisation cuota.

domain.bienes_inversion

Sibling per-taxpayer-fact register whose shape this mirrors.

exception ProrrataRegisterError(message=None, *, context=None, suggestion=None, translated_message=None)[source]

Bases: AeatError

Raised when a prorrata-register record is structurally invalid.

Parameters:
  • message (str | None)

  • context (dict[str, object] | None)

  • suggestion (str | None)

  • translated_message (str | None)

Return type:

None

code: ClassVar[ErrorCode]
exception ProrrataRegisterValidationError(message=None, *, context=None, suggestion=None, translated_message=None)[source]

Bases: ProrrataRegisterError, ValueError

Raised when a prorrata-register model fails Pydantic validation.

Parameters:
  • message (str | None)

  • context (Mapping[str, object] | None)

  • suggestion (str | None)

  • translated_message (str | None)

Return type:

None

code: ClassVar[ErrorCode]
PRORRATA_REGISTER_SCHEMA_VERSION

Forward-compatible schema version stamped onto every record in this module.

class SectorDefinition(**data)[source]

Bases: BaseModel

One operator-declared differentiated sector (LIVA arts. 9.1.c / 101).

Strict, frozen, no extra fields. The art. 9.1.c partition of a taxpayer’s activities into differentiated sectors is a legal judgment the ledger cannot infer (which CNAE groups are run, whether their prorrata percentages diverge by more than 50 percentage points, whether a special-regime activity is present), so it is operator-declared: each sector carries a stable sector_id (the key the register entries and the ledger rows reference), the member activity codes it groups, and the SectorDiferenciadoLetra that makes it differentiated. Fail-closed: a register with no sector definitions is a whole-entity register (sector_id = None throughout), the landed cross-period behaviour, never a silently inferred partition.

Variables:
  • sector_id – Stable identifier the register entries and ledger rows reference. Must match the sector_id on the per-sector ProrrataRegisterEntry rows.

  • letra – The SectorDiferenciadoLetra (art. 9.1.c letra a’/b’/c’/d’) on which this sector is differentiated.

  • member_activity_codes – The CNAE / IAE-epígrafe activity codes grouped into this sector. Non-empty: a declared sector groups at least one activity. Recorded for provenance and operator audit; the per-sector routing keys on sector_id, never on these codes.

Parameters:
sector_id: str
letra: SectorDiferenciadoLetra
member_activity_codes: tuple[str, ...]
class ProrrataRegisterEntry(**data)[source]

Bases: BaseModel

One (ejercicio, sector) entry in the cross-period prorrata register.

Strict, frozen, no extra fields. Carries the regime in force, the provisional percentage with its regulated provenance (and, for the referenced provenances, its authorisation/proposal reference), and — populated only at settlement — the definitive percentage with the annual volume inputs it derived from. The provisional, referenced, and settlement field groups each travel together (present or absent as a unit), enforced by _validate_field_coupling().

Variables:
  • ejercicio – Filing year the entry covers.

  • regimeProrrataRegisterRegime in force for the ejercicio.

  • sector_id – Sector identifier for a sectores-diferenciados register, or None for the whole-entity register. Present from birth so sectores land without migration; the per-sector compute is deferred.

  • interrupted – The art. 105.Cinco “sin operaciones” marker — True when the taxpayer (or the differentiated sector) performed no operations during the ejercicio. Distinct from the ninguna regime (an active year under no prorrata): an interrupted year is inactive. An interrupted entry carries no provisional or definitive percentage and no volume inputs; the three-active-years seed walk skips it. The register thereby retains a truthful active/inactive history.

  • provisional_percentage – The provisional deduction percentage (0-100) in force during the year’s liquidations (art. 104.Uno + 105.Uno), or None when no percentage has resolved yet (never a fabricated default).

  • provisional_provenance – The ProrrataProvisionalProvenance the provisional percentage came from. Present iff provisional_percentage is present.

  • authorisation_reference – The AEAT authorisation (art. 105.Dos) or inicio-de-actividades proposal (art. 105.Tres) reference. Required iff provisional_provenance is a referenced provenance; forbidden otherwise.

  • definitive_percentage – The definitive deduction percentage (0-100) computed at settlement from the annual volumes (art. 105.Cuatro), or None before settlement.

  • definitive_volume_con_derecho – The annual con-derecho operations volume the definitive percentage derived from. Present iff definitive_percentage is present.

  • definitive_volume_sin_derecho – The annual sin-derecho operations volume the definitive percentage derived from. Present iff definitive_percentage is present.

  • source_observation_ref – The prior settlement observation identity a carried_prior_definitiva entry was seeded from, so the register stays cross-checkable against the prior filing. Permitted only for the carried provenance.

  • schema_version – Forward-compatible schema version. "1".

Parameters:
ejercicio: int
regime: ProrrataRegisterRegime
sector_id: str | None
interrupted: bool
provisional_percentage: Decimal | None
provisional_provenance: ProrrataProvisionalProvenance | None
authorisation_reference: str | None
definitive_percentage: Decimal | None
definitive_volume_con_derecho: Decimal | None
definitive_volume_sin_derecho: Decimal | None
source_observation_ref: str | None
schema_version: str
class ProrrataProvisionalResolution(**data)[source]

Bases: BaseModel

Outcome of the precedence-ladder resolution of the in-force provisional percentage.

Variables:
  • percentage – The in-force provisional deduction percentage (0-100), or None when the ladder resolved no value (the visible unresolved state — the caller surfaces an advisory, never a silent default).

  • provenance – The winning ProrrataProvisionalProvenance, or None when unresolved.

Parameters:
percentage: Decimal | None
provenance: ProrrataProvisionalProvenance | None
property resolved: bool

Whether the ladder resolved a percentage.

resolve_provisional_percentage(candidates)[source]

Resolve the in-force provisional percentage among candidate entries by the LIVA art. 105 ladder.

Applies the single declared precedence ladder (_PROVENANCE_PRECEDENCE): an AEAT-authorised (art. 105.Dos) or inicio-de-actividades (art. 105.Tres) provisional percentage outranks the carried prior definitive (art. 105.Uno). Only candidates that actually carry a provisional percentage participate; a candidate recording a regime but no percentage does not contribute a value. When no candidate carries a percentage the result is the visible unresolved state (both fields None) — never a fabricated default.

Parameters:

candidates (Iterable[ProrrataRegisterEntry]) – Register entries to resolve among. In the normal register this is the (at most one) entry for a (ejercicio, sector) key; the seeding/override recording path supplies several provenance candidates.

Return type:

ProrrataProvisionalResolution

Returns:

A ProrrataProvisionalResolution naming the winning percentage and provenance, or both None when unresolved.

class ThreeActiveYearsAggregate(**data)[source]

Bases: BaseModel

Aggregated volume inputs of the last three ACTIVE años naturales (LIVA art. 105.Cinco).

The art. 105.Cinco interrupted-activity rule seeds a resumed ejercicio from the percentage that “globalmente corresponda al conjunto de los tres últimos años naturales en que se hubiesen realizado operaciones”: a GLOBAL percentage over the AGGREGATE volumes of the last three active years, not the average of their three definitive percentages. This carrier holds those summed volumes and the contributing ejercicios (newest-first). sufficient is True only when a full three active years were found; with fewer, the application seed surfaces an advisory rather than assuming a percentage.

Variables:
  • contributing_ejercicios – The active ejercicios whose volumes were summed, newest first (at most three).

  • summed_volume_con_derecho – Sum of the contributing years’ annual con-derecho operation volumes.

  • summed_volume_sin_derecho – Sum of the contributing years’ annual sin-derecho operation volumes.

Parameters:
  • contributing_ejercicios (tuple[int, ...])

  • summed_volume_con_derecho (Decimal)

  • summed_volume_sin_derecho (Decimal)

contributing_ejercicios: tuple[int, ...]
summed_volume_con_derecho: Decimal
summed_volume_sin_derecho: Decimal
property sufficient: bool

Whether a full three active años naturales contributed.

class ProrrataRegister(**data)[source]

Bases: BaseModel

Encrypted JSON document holding the per-ejercicio prorrata register.

Holds one ProrrataRegisterEntry per (ejercicio, sector_id) key; a duplicate key is rejected at construction. The regime and sector axes are present from birth so prorrata especial and sectores diferenciados land without a schema migration (no-legacy-compatibility).

Variables:
  • schema_version – Forward-compatible schema version. "1".

  • entries – Tuple of ProrrataRegisterEntry rows.

  • sector_definitions – The operator-declared differentiated-sector partition (LIVA arts. 9.1.c / 101). Empty for a whole-entity register — the fail-closed default; when non-empty every per-sector ProrrataRegisterEntry sector_id and every sectored ledger row references one of these declared sectors.

Parameters:
schema_version: str
entries: tuple[ProrrataRegisterEntry, ...]
sector_definitions: tuple[SectorDefinition, ...]
property is_sectorized: bool

Whether the register declares a differentiated-sector partition.

Fail-closed: False (whole-entity) when no sector definition exists, so a taxpayer with no declared partition keeps the landed cross-period behaviour byte-identical.

sector_ids()[source]

Return the declared sector ids, in declaration order.

Return type:

tuple[str, ...]

sector_definition_for(sector_id)[source]

Return the declared SectorDefinition for sector_id, or None.

Return type:

SectorDefinition | None

Parameters:

sector_id (str)

entries_for_ejercicio(ejercicio)[source]

Return every entry recorded for ejercicio across all sectors.

Return type:

tuple[ProrrataRegisterEntry, ...]

Parameters:

ejercicio (int)

entry_for(ejercicio, *, sector_id=None)[source]

Return the entry for a (ejercicio, sector_id) key, or None when absent.

Return type:

ProrrataRegisterEntry | None

Parameters:
  • ejercicio (int)

  • sector_id (str | None)

resolve_provisional(ejercicio, *, sector_id=None)[source]

Resolve the in-force provisional percentage for a (ejercicio, sector_id) key.

Filters the register to the key’s entry and applies resolve_provisional_percentage(), returning the visible unresolved state when no percentage is recorded.

Return type:

ProrrataProvisionalResolution

Parameters:
  • ejercicio (int)

  • sector_id (str | None)

collect_last_three_active_years(*, before_ejercicio, sector_id=None)[source]

Aggregate the volume inputs of the last three ACTIVE años naturales (LIVA art. 105.Cinco).

Walks the register backward from before_ejercicio for the given sector_id, SKIPPING interrupted (sin operaciones) years and any year that has not settled (no definitive volumes), and sums the con-derecho and sin-derecho volume inputs of the last three active años naturales. An “active” year is a settled, non-interrupted entry; the walk is over active years, not calendar years, so the interruption gap is skipped.

Returns a ThreeActiveYearsAggregate whose sufficient is True only when three active years contributed; the application seed turns an insufficient aggregate into a visible advisory rather than assuming a percentage.

Return type:

ThreeActiveYearsAggregate

Parameters:
  • before_ejercicio (int)

  • sector_id (str | None)

Submodules