aeat.domain.renta._ledger_expenses module

Renta deductible-expense observations derived from ledger facts.

RentaDeductibleExpenseFact is evaluated through evaluate_renta_deductibility() using CategoryProfile and RentaDeductibilityContext; eligible RentaDeductibilityResult values become RentaDeductibleExpenseObservation records routed through RENTA_100_FIRST_SLICE_EXPENSE_CASILLAS to registry CasillaId bindings for M100.

class RentaExpenseDirection(*values)[source]

Bases: StrEnum

Closed direction values for first-slice Renta expense facts.

OUTGOING_EXPENSE
REFUND
REVERSAL
class RentaDeductibilityStatus(*values)[source]

Bases: StrEnum

Whether a ledger fact can produce a Renta calculation observation.

ELIGIBLE
INELIGIBLE
class RentaInvoiceEvidenceStatus(*values)[source]

Bases: StrEnum

Invoice-evidence state carried by the Renta observation.

NONE
LINKED
class RentaReconciliationStatus(*values)[source]

Bases: StrEnum

Transaction/invoice reconciliation state for duplicate prevention.

TRANSACTION_ONLY
LINKED_INVOICE
class RentaDeductibilityContext(**data)[source]

Bases: _RentaStrictFrozenModel

Inputs that affect proportionality but are not ledger facts.

Parameters:
profile_year: int
usage_ratios: dict[SpendingCategory, Decimal]
statutory_cap_days: Decimal | None
statutory_cap_variant_id: str | None
statutory_cap_person_count: int
exclusive_use_confirmed: bool
residence_ccaa: CCAA | None

Ordinary residence comunidad autonoma, sourced from TaxResidenceProfile.ccaa.

Optional and inert for the general expense path: LIRPF arts. 28-30 base-imponible deductibility is state law and does not vary by comunidad (Ley 22/2009 cesion framework grants no base competence to the CCAA). The axis only selects a territorial-regime override where one is declared for the fact’s category; when an override exists but this field is None the evaluation fails closed rather than silently choosing a base (see select_deductibility_profile()).

class RentaDeductibleExpenseFact(**data)[source]

Bases: _RentaStrictFrozenModel

Canonical ledger fact eligible for Renta deductibility evaluation.

Parameters:
transaction_id: str
invoice_id: str | None
catalogue_id: str
operation_date: date
invoice_issue_date: date | None
posting_date: date | None
payment_date: date | None
gross_amount: Decimal
taxable_base: Decimal | None
iva_amount: Decimal | None
currency: Literal['EUR']
direction: RentaExpenseDirection
category: SpendingCategory
activity_key: str
property filing_date: date

Return the filing date used for first-slice selection.

Prefers invoice_issue_date when present, falling back to operation_date so observations without invoices still get a deterministic anchor.

property sign: Literal[-1, 1]

Return +1 for expenses and -1 for linked corrections.

class RentaDeductibilityResult(**data)[source]

Bases: _RentaStrictFrozenModel

Deductibility evaluation result before registry binding resolution.

Parameters:
transaction_id: str
invoice_id: str | None
category: SpendingCategory
category_family: SpendingCategoryFamily
profile_year: int
proportionality_kind: ProportionalityKind
status: RentaDeductibilityStatus
reason: str
gross_amount: Decimal
deductible_amount: Decimal
non_deductible_amount: Decimal
applied_ratio: Decimal | None
statutory_cap_applied: Decimal | None
legal_references: tuple[CategoryCitation, ...]
class RentaDeductibleExpenseObservation(**data)[source]

Bases: _RentaStrictFrozenModel

Binding-ready Renta expense observation for the first Modelo 100 slice.

Parameters:
observation_id: str
source_kind: Literal['ledger_renta_expense_aggregation']
modelo: Literal[Modelo.M100]
period: Literal['0A']
tax_year: int
activity_key: str
target_casilla_id: CasillaId
transaction_id: str
invoice_id: str | None
catalogue_id: str
operation_date: date
invoice_issue_date: date | None
posting_date: date | None
payment_date: date | None
filing_date: date
gross_amount: Decimal
taxable_base: Decimal | None
iva_amount: Decimal | None
deductible_amount: Decimal
non_deductible_amount: Decimal
currency: Literal['EUR']
direction: RentaExpenseDirection
sign: Literal[-1, 1]
category: SpendingCategory
category_family: SpendingCategoryFamily
profile_year: int
proportionality_kind: ProportionalityKind
applied_ratio: Decimal | None
invoice_evidence_status: RentaInvoiceEvidenceStatus
reconciliation_status: RentaReconciliationStatus
legal_references: tuple[CategoryCitation, ...]
normalize_spending_category(value)[source]

Normalize persisted category identifiers to closed enum members.

Return type:

SpendingCategory

Returns:

The SpendingCategory member for the given value.

Parameters:

value (SpendingCategory | str)

resolve_region_category_profiles(profile_year)[source]

Return the territorial-regime category-profile overrides for a filing year.

The override layer is provisioned but deliberately empty: the only genuinely region-varying expense-side regimes reach the base through their own dedicated bindings (the Reserva para Inversiones en Canarias, Ley 19/1994 art. 27, is modelled as its own binding, not a SpendingCategory profile), and the Ceuta/Melilla benefit is an art. 68.4 cuota deduction rather than a base-imponible expense rule. No SpendingCategory therefore warrants a per-comunidad deductibility variant today, so this resolver returns an empty mapping and every fact falls through to the state year profile. A future territorial-regime enrollment populates this mapping (grounded to its regime law) with no further architectural change.

Return type:

Mapping[CCAA, Mapping[SpendingCategory, CategoryProfile]]

Returns:

Mapping from CCAA to a per-SpendingCategory override profile mapping; empty until a territorial regime is enrolled.

Parameters:

profile_year (int)

select_deductibility_profile(*, state_profile, region_override_profiles, context)[source]

Select the applicable category profile, honouring territorial-regime overrides.

region_override_profiles holds the per-CCAA overrides declared for the fact’s category (empty for the general state-law case). Selection:

  • no override for the category: return state_profile unchanged (general expense deductibility is state base-imponible law, invariant across comunidades);

  • the category carries an override but context.residence_ccaa is None: return None (D4 fail-closed) so the caller refuses rather than silently choosing a base for an undeclared region;

  • the residence comunidad has an override: return that override profile;

  • the residence comunidad has no override (a different comunidad owns the regime): return state_profile (state law applies to this taxpayer’s region).

Return type:

CategoryProfile | None

Returns:

The selected CategoryProfile, or None when a region override exists for the category but the residence comunidad is undeclared.

Parameters:
evaluate_renta_deductibility(fact, profile, context)[source]

Evaluate one classified ledger fact and return a RentaDeductibilityResult.

Return type:

RentaDeductibilityResult

Parameters:
build_renta_deductible_expense_observation(fact, result, *, tax_year)[source]

Build a first-slice Modelo 100 observation from an eligible result.

Return type:

RentaDeductibleExpenseObservation

Returns:

A RentaDeductibleExpenseObservation ready for filing assembly.

Parameters: