aeat.application.aggregation._impatriado_income_ledger module

Repository-backed Modelo 151 impatriado Spanish-source income aggregation.

This is the ledger projection behind the ledger_impatriado_income_aggregation source for Modelo 151 (régimen especial de trabajadores desplazados, “Ley Beckham”, art. 93 LIRPF). The annual entry point aggregate_impatriado_income_ledger_from_repositories() loads a TransactionCatalogue from the active bucket through TransactionCatalogueRepository and delegates to aggregate_impatriado_income_ledger().

Unlike the Modelo 130 / Modelo 100 actividad-económica income pipeline (_renta_income_ledger), which admits worldwide income into the resident-IRPF base (LIRPF art. 8), the impatriado base is legally source-scoped: art. 93.2 taxes the impatriado by the IRNR scope rules, so its impatriado.base-liquidable-general casilla admits ONLY Spanish-source income. The declared per-row source_jurisdiction axis — which the CLI create-boundary gate compels an impatriado profile to supply on every ledger row — is finally consumed here:

  • an INCOMING row whose source_jurisdiction resolves to ES folds into the impatriado base;

  • a foreign-source row (source_jurisdiction set to any non-ES code) is segregated out of the base and surfaced as a typed ImpatriadoIncomeLedgerAggregationIssueReason.BECKHAM_FOREIGN_SOURCE_SEGREGATED issue carrying the rejected jurisdiction code (art. 93.2 / art. 25.1.f TRLIRNR segregation);

  • a jurisdiction-unresolved row (source_jurisdiction is None) is NEVER silently coerced to ES: it fails loud as the same segregation issue with an unresolved-jurisdiction detail (no-silent-under-declaration).

The impatriado base admits trabajo income — the exact income class the M130 pipeline routes OUT — because the Beckham base is predominantly rendimientos del trabajo (nómina). The two pipelines are complementary, not shared.

The savings escala (art. 93.2.e.2º → art. 25.1.f TRLIRNR: the parte del ahorro) is out of scope here and blocked on a separate corpus ingest; the base casilla is labelled “excluida la parte del ahorro” to keep that deferral honest.

class ImpatriadoIncomeLedgerAggregationIssueReason(*values)[source]

Bases: StrEnum

Machine-readable reasons why a ledger row did not fold into the impatriado base.

UNSUPPORTED_DIRECTION
UNSUPPORTED_CURRENCY
UNCLASSIFIED_BUSINESS_STATE
PERSONAL_TRANSACTION
OUTSIDE_PERIOD
UNSUPPORTED_PERIOD
BECKHAM_FOREIGN_SOURCE_SEGREGATED
class ImpatriadoIncomeLedgerAggregationIssue(**data)[source]

Bases: BaseModel

Traceable exclusion emitted while aggregating impatriado income ledger rows.

Parameters:
transaction_id: str
reason: ImpatriadoIncomeLedgerAggregationIssueReason
detail: str
rejected_source_jurisdiction: str | None
class ImpatriadoIncomeObservation(**data)[source]

Bases: BaseModel

One eligible INCOMING Spanish-source income ledger row for the impatriado base.

Carries the typed gross amount and the target casilla id it feeds (impatriado.base-liquidable-general). The domain registry resolver sums the fiscally computable ingreso (taxable_base_amount when the row carries an explicit IVA tagging, else gross_amount) across all observations for that casilla.

source_jurisdiction is retained on the observation for provenance and is "ES" by construction: a foreign or unresolved jurisdiction is segregated into an issue before an observation is ever emitted.

Parameters:
  • transaction_id (str)

  • target_casilla_id (CasillaId)

  • gross_amount (Decimal)

  • taxable_base_amount (Decimal | None)

  • filing_date (date)

  • source_jurisdiction (str)

transaction_id: str
target_casilla_id: CasillaId
gross_amount: Decimal
taxable_base_amount: Decimal | None
filing_date: date
source_jurisdiction: str
class ImpatriadoIncomeLedgerAggregation(**data)[source]

Bases: BaseModel

Annual Spanish-source income observations for one Modelo 151 ejercicio.

out_of_window_summary is populated by repository-backed date partitions. Full-catalogue aggregation keeps row-level issues because every transaction is already loaded for classification.

Parameters:
modelo: str
period: Period
observations: Sequence[ImpatriadoIncomeObservation]
issues: Sequence[ImpatriadoIncomeLedgerAggregationIssue]
out_of_window_summary: OutOfWindowTransactionSummary | None
casilla_aggregation: CasillaAggregation
aggregate_impatriado_income_ledger_from_repositories(*, bucket_id, period, transaction_repository=None)[source]

Load the transaction catalogue and aggregate annual impatriado Spanish-source income.

When no protocol-compatible repository override is supplied, this loader uses TransactionCatalogueRepository scoped to bucket_id.

Returns an ImpatriadoIncomeLedgerAggregation.

Return type:

ImpatriadoIncomeLedgerAggregation

Parameters:
aggregate_impatriado_income_ledger(transactions, *, bucket_id, period)[source]

Aggregate INCOMING Spanish-source income into Modelo 151 impatriado.base-liquidable-general.

Applies the impatriado source scope over the FULL ejercicio (Jan 1 to Dec 31 of period.year): only INCOMING, EUR-denominated rows whose declared source_jurisdiction resolves to ES fold into the base. Foreign-source and jurisdiction-unresolved rows are segregated into ImpatriadoIncomeLedgerAggregationIssueReason.BECKHAM_FOREIGN_SOURCE_SEGREGATED issues rather than silently entering (or silently dropping from) the base.

Parameters:
Return type:

ImpatriadoIncomeLedgerAggregation

Returns an ImpatriadoIncomeLedgerAggregation for the ejercicio. period must be the annual period.