Source code for aeat.application.aggregation._models
"""Strict boundary models for financial transaction aggregation.Carries the per-casilla :class:`CasillaProvenance` trace and the aggregated:class:`CasillaAggregation` ledger shape. The aggregation package re-exportsthe canonical :class:`core.Period`; period construction and date-spanauthority live in core, not in an application-layer wrapper."""from__future__importannotationsfromcollections.abcimportMapping,SequencefromdecimalimportDecimalfromtypesimportMappingProxyTypefromtypingimportAnnotatedfrompydanticimport(BaseModel,BeforeValidator,Field,field_serializer,field_validator,)from...coreimportSTRICT_FROZEN_CONFIGas_STRICT_FROZENfrom...coreimportPeriodfrom...domain.calculations.registryimportCasillaIdfrom...domain.categoriesimportSpendingCategorydef_coerce_spending_category(value:object)->object:"""Accept the canonical SpendingCategory value string AND the enum member. Strict pydantic refuses str→Enum coercion by default. Wrapping the field with a ``BeforeValidator`` keeps registry/JSON payloads (which carry the enum's ``.value`` string) loadable without weakening strict-mode for every other field on the model. """ifvalueisNoneorisinstance(value,SpendingCategory):returnvalueifisinstance(value,str):returnSpendingCategory(value)returnvalue_SpendingCategoryField=Annotated[SpendingCategory,BeforeValidator(_coerce_spending_category)]
[docs]classCasillaProvenance(BaseModel):"""Transaction trace backing one (casilla, category) subtotal. Attributes: casilla_id: Target canonical casilla id (e.g. ``"02"``). transaction_ids: Sorted, frozen tuple of contributing transaction IDs. subtotal: Sum of contributions for this casilla/category pair. category_id: Optional category identifier when the contribution came from an expense bucket. """model_config=_STRICT_FROZENcasilla_id:CasillaIdtransaction_ids:Sequence[str]=Field(default_factory=tuple)subtotal:Decimal# Typed SpendingCategory enum (was bare ``str`` before R025/R026# follow-up). The BeforeValidator coerces canonical string inputs# to the enum member so existing TOML/JSON payloads round-trip# without registry-data changes. Downstream comparisons no longer# need a manual ``normalize_spending_category`` step.category_id:_SpendingCategoryField|None=None@field_validator("transaction_ids")@classmethoddef_freeze_transaction_ids(cls,value:Sequence[str])->tuple[str,...]:"""Freeze ``value`` into an immutable tuple."""returntuple(value)
[docs]classCasillaAggregation(BaseModel):"""Aggregated casilla ledger for one modelo and period. Attributes: modelo: Modelo identifier (``ModeloCode.value``) the totals belong to. period: The :class:`Period` covered. casilla_values: Mapping of canonical casilla id to summed :class:`~decimal.Decimal` value, sorted and frozen. provenance: Tuple of :class:`CasillaProvenance` rows tracing each contribution back to its source transactions. """model_config=_STRICT_FROZENmodelo:str=Field(min_length=1,max_length=16)period:Periodcasilla_values:Mapping[CasillaId,Decimal]=Field(default_factory=dict)provenance:Sequence[CasillaProvenance]=Field(default_factory=tuple)@field_validator("casilla_values")@classmethoddef_freeze_casilla_values(cls,value:Mapping[CasillaId,Decimal])->Mapping[CasillaId,Decimal]:"""Return ``value`` as a sorted, immutable :class:`MappingProxyType`."""returnMappingProxyType(dict(sorted(value.items())))@field_serializer("casilla_values")def_serialize_casilla_values(self,value:Mapping[CasillaId,Decimal])->dict[CasillaId,Decimal]:"""Serialise the immutable view back to a plain ``dict`` for JSON output."""returndict(value)@field_validator("provenance")@classmethoddef_freeze_provenance(cls,value:Sequence[CasillaProvenance])->tuple[CasillaProvenance,...]:"""Freeze ``value`` into an immutable tuple."""returntuple(value)