cadrumo.core.aggregation module¶
Core aggregation taxonomy shared across registry, application, and adapters.
This module owns closed value sets and tiny pydantic carriers only; it does not
aggregate ledger rows, calculate casilla values, or perform source resolution.
The registry schema imports BindingAggregation and
BindingSourceKind to validate TOML authoring input. Application resolvers and adapters import the same
BindingSourceKind members so source tokens do not drift through bare
strings.
Keep the three axes separate:
BindingAggregationOpgoverns how aBindingDefinition.aggregationfolds selected binding values.RelationAggregationOpgoverns cross-modelo relation fold-ins.RowSetGroupingKindis the downstream row-assembly grouping axis, not a bindingsourcetoken; useROW_SET_GROUPING_FOR_BINDING_SOURCEonly where a detail-record binding source must be projected into the row assembler.
- class BindingAggregationOp(*values)[source]¶
Bases:
StrEnumClosed set of aggregation operators a registry binding may declare.
A binding’s
aggregation.opselects how the resolver folds the selected source values into the bound casilla value. The members below are the complete set declared on aBindingDefinition.aggregationacross the registry authoring tree; relation aggregation (copy/sumon arelation_prefillprovider) and formula-expression operators are a separate, unrelated axis and are not modelled here.- Members:
- SUM: Add the selected source values (default for the scalar-folding
families: previous-filing, counterpart, invoice, ledger, withholding).
- ROWS: Emit one detail row per selected observation rather than folding
to a scalar (default for the detail-record families: related-party, foreign-asset, atribución, refund).
- COPY: Carry a single source value through unchanged; refuses when more
than one source value is selected.
- COUNT_DISTINCT: Count the distinct operators/perceptores behind the
selected observations (e.g. Modelo 349 operator counts).
- PRIOR_PAGOS_FRACCIONADOS: Modelo 130 cumulative pago-fraccionado carry
that nets the prior positive declaration against the running total.
- SUM¶
- ROWS¶
- COPY¶
- COUNT_DISTINCT¶
- PRIOR_PAGOS_FRACCIONADOS¶
- class BindingAggregation(**data)[source]¶
Bases:
BaseModelTyped aggregation rule carried by a registry
BindingDefinition.Placed in
core(cross-layer home) because the domain registry schema declares the field and the application/adapter layers read it; the closedBindingAggregationOpset is the only key real binding aggregation mappings carry in the registry authoring tree. The model is strict and frozen, matching the registry schema’sSTRICT_FROZEN_CONFIGconvention, so an unknownopor a stray extra key is rejected at registry-build validation rather than silently re-parsed at resolve time.- Parameters:
data (Any)
- op: BindingAggregationOp¶
- class RelationAggregationOp(*values)[source]¶
Bases:
StrEnumClosed set of aggregation operators a cross-filing fold may declare.
A relation’s
aggregation.opselects how a cross-modelo fold-in folds its matched source filings:COPYcarries a single source value through unchanged (the default when a relation declares no aggregation), andSUMadds the matched per-period source values (annual summaries). This is a deliberately separate axis fromBindingAggregationOp(which governsBindingDefinitionfolds and carries the binding-onlyrows/count_distinct/prior_pagos_fraccionadosmembers); the two are not interchanged. The complete set declared across the registry relation tree iscopyandsum.- COPY¶
- SUM¶
- class AggregationCaptureKind(*values)[source]¶
Bases:
StrEnumWhich ingestion path wrote a per-perceptor aggregation observation batch.
CAPTURE PROVENANCE, and a different axis from two neighbours it is easy to confuse with. It is not
ObservationSourceKind, which classifies whether a persisted modelo observation is official AEAT filing evidence; and it is not the innersource_kindon a retención observation, which records whether the underlying row came from a ledger transaction or a payable invoice. All three have worn the namesource_kind.That distinction was load-bearing before it was enforceable. The two aggregation stores are exempt from the official-evidence displacement guard precisely BECAUSE no evidence-authority value can reach them, and that exemption rested on the field being a free-form
strthat happened to carry one token. Nothing refused"aeat_sede_justificante"into either store, and on the day something wrote it the exemption would have become wrong silently. Typing the axis converts the disjointness from an observation about today’s content into a load-time refusal.One member is not a defect. The set is closed at what the code actually produces; a second ingestion path adds its own member here, deliberately, rather than arriving as an unreviewed string.
- AGGREGATE_PULL¶
- class RowSetGroupingKind(*values)[source]¶
Bases:
StrEnumCanonical row-set source-kind discriminators for detail-record assembly.
Placed in
core(cross-layer home) because both the application assembly layer and the domain registry schema reference these values, and domain → application imports are forbidden under the hexagonal contract.This is the row-assembly grouping axis consumed in the application layer (_row_set_assembly.py), a separate concept from the binding
sourcetoken enumerated byBindingSourceKind. For the three detail-record families whose grouping member differs from the binding source token, the correspondence is intentional and explicit; seeROW_SET_GROUPING_FOR_BINDING_SOURCE:BindingSourceKind.WITHHOLDING("withholding") ↔WITHHOLDINGBindingSourceKind.FOREIGN_ASSET("foreign_asset") ↔FOREIGN_ASSETBindingSourceKind.RELATED_PARTY_OPERATION("related_party_operation") ↔RELATED_PARTY("related_party")BindingSourceKind.ATRIBUCION_MEMBER("atribucion_member") ↔ATRIBUCION("atribucion")BindingSourceKind.REFUND_OPERATION("refund_operation") ↔REFUND("refund")BindingSourceKind.DONATIVO_DONOR("donativo_donor") ↔DONATIVO("donativo")
- WITHHOLDING¶
- RELATED_PARTY¶
- FOREIGN_ASSET¶
- ATRIBUCION¶
- REFUND¶
- DONATIVO¶
- GASTO193¶
- WITHHOLDING296¶
- class CalculationSourceLineageRole(*values)[source]¶
Bases:
StrEnumA persisted source node’s role in one resolver-produced provenance graph.
- PRIMARY¶
Durable resolved economic object whose resolver owns the binding source.
- CONTRIBUTOR¶
Upstream fact linked to, but never substituting for, a primary object.
- class BindingSourceKind(*values)[source]¶
Bases:
StrEnumThe single canonical closed set of binding/source-mesh tokens.
Every
BindingDefinitiondeclares exactly onesourcedrawn from the registry-declared subset of this enum. The same enum also carries mesh-only source decisions such asBORRADORandIVA_WALLET_DECISION, which are resolved before a registry binding is constructed and are parity-accounted as non-registry members. Per-family frozensets (invoice, ledger, counterpart) are derived from this enum rather than hand-maintained, so a new source token is added in exactly one place.BEHAVIOUR-PRESERVING LIFT: every member’s string VALUE equals the source token that was previously a bare string (or a
RowSetGroupingKindmember) in theBindingDefinition.sourceLiteral. Those tokens live in registry TOML and may be persisted; aStrEnumserialises to its value, so folding the mixed Literal onto this enum changes the static type without changing any stored or compared string (the modelo-enum-hardening precedent). Do NOT rename a stored token.This enum is the single canonical source-kind authority across BOTH the registry binding definitions AND the application resolver mesh: the counterpart subset (
COUNTERPART_SOURCE_KINDS) is derived from it, and the two grouping members reuseRowSetGroupingKindvalues so the cross-layer aggregation taxonomy stays consistent; seeROW_SET_GROUPING_FOR_BINDING_SOURCEfor the detail-record source-token ↔ grouping-axis mapping.- PROFILE¶
- PREVIOUS_FILING¶
- RELATION_PREFILL¶
- MANUAL_INPUT¶
- DESIGN_CONSTANT¶
- LEDGER_OSS_AGGREGATION¶
- LEDGER_IVA_AGGREGATION¶
- LEDGER_RENTA_GASTOS_ESTIMACION_DIRECTA_AGGREGATION¶
- LEDGER_RENTA_INCOME_AGGREGATION¶
- LEDGER_RENTA_GASTOS_PAGO_FRACCIONADO_AGGREGATION¶
- LEDGER_IMPATRIADO_INCOME_AGGREGATION¶
- LEDGER_IRNR_INCOME_AGGREGATION¶
- RETENCIONES_AGGREGATION¶
- IVA_COMPENSATION_ANNUAL_PARTITION¶
- M303_REGIMEN_SIMPLIFICADO_ANNUAL_SUMMARY¶
- BIENES_INVERSION_REGULARIZACION¶
- INVENTORY¶
- PRORRATA_REGULARIZACION¶
- BORRADOR¶
- IVA_WALLET_DECISION¶
- PAYABLE_INVOICE¶
- COLLECTIBLE_INVOICE¶
- M347_THIRD_PARTY_OPERATION¶
- LEDGER_TRANSACTION¶
- PURCHASE_INVOICE_EVIDENCE¶
- WITHHOLDING¶
- FOREIGN_ASSET¶
- RELATED_PARTY_OPERATION¶
- ATRIBUCION_MEMBER¶
- REFUND_OPERATION¶
- DONATIVO_DONOR¶
- GASTO193_CONTRIBUTOR¶
- WITHHOLDING296¶
- ROW_SET_GROUPING_FOR_BINDING_SOURCE: Final[Mapping[BindingSourceKind, RowSetGroupingKind]]¶
Explicit detail-record binding-source ↔ row-assembly grouping correspondence.
The binding
sourcetoken (e.g."related_party_operation") and the row-assemblyRowSetGroupingKindvalue (e.g."related_party") are distinct strings for the three families whose source token carries the_operation/_membersuffix; this mapping makes the relationship explicit so a reader is not misled into assuming the two axes share a value.
- INVOICE_BINDING_SOURCE_KINDS: Final[frozenset[BindingSourceKind]]¶
Invoice-shaped binding source kinds, derived from
BindingSourceKind.
- COUNTERPART_SOURCE_KIND_ORDER: Final[tuple[CounterpartSourceKind, ...]]¶
Operator-facing display order for
COUNTERPART_SOURCE_KINDS.This is the order the
--kindhelp text and alias table present to an operator (documented indocs/how-to/review-queue.md), so it is authoritative and MUST NOT be reordered incidentally.COUNTERPART_SOURCE_KINDSderives its membership from this tuple so the two can never drift apart.
- counterpart_source_kind(value)[source]¶
Return
valuenarrowed to the counterpart source-kind subset.- Parameters:
value (
object) – ABindingSourceKindmember or its stored string value.- Return type:
TypeAliasType- Returns:
The same source kind narrowed to
CounterpartSourceKindfor counterpart aggregation inputs.- Raises:
ValueError – When
valueis not a knownBindingSourceKind, or is known but outside the counterpart subset.
- LEDGER_BINDING_SOURCE_KINDS: Final[frozenset[BindingSourceKind]]¶
Ledger-aggregation binding source kinds (all seven), derived from the enum.
Every binding whose
sourceis a member reads its values from the bucket-scoped ledger (transaction-classified IVA / OSS aggregation, Renta first-slice income and estimación directa gastos aggregation, the M130 pago-fraccionado gastos cumulative aggregation, or the M151 impatriado base aggregation, M151 impatriado income, or M210 explicit IRNR income). Theledger_namespace derives the set directly from the canonical enum;ledger_transactionremains a counterpart source rather than a ledger aggregation. Cross-domain consumers route through this frozenset so the registry stays the single source of truth for ledger readiness.
- OBSERVATION_BACKED_BINDING_SOURCE_KINDS: Final[frozenset[BindingSourceKind]]¶
Binding source kinds whose value is read back from a prior-filed observation.
Both name a figure the taxpayer already declared — a direct same-modelo carry, or a cross-modelo fold-in materialised into a relation-prefill slot — rather than a value this application derives. Consumers that must not treat such a value as independent evidence, or that must not compare it against the observation store it came from, route through this frozenset.
MEMBERSHIP IS SEMANTIC, NOT LEXICAL, and this is the one per-family collection that cannot be derived by construction the way
LEDGER_BINDING_SOURCE_KINDSis from theledger_namespace: the two members share no naming property, only the behaviour of reading the observation store. So adding a source kind here is a judgement, and the question to ask is whether its resolver reads a prior-filed observation — not whether its token looks like these two. That is stated rather than papered over, because a hand-listed set that claims to be derived is worse than one that admits it is not.DO NOT WIDEN THIS TO THE CARRY-FORWARD OVERRIDE TIER. The caller-override precedence ladder declares a superset that additionally carries the IVA compensación annual partition and the prorrata regularización. That is a different concept — which values an operator override may displace — and folding it in here silently widens two registry validation guards.
- class BindingTypedEnumKind(*values)[source]¶
Bases:
StrEnumThe closed set of substrate enum-class names a binding value bridges.
A
BindingDefinitionwhose value bridges a closed-membership substrate axis declarestyped_enum= one of these members. Each value is the NAME of the closed enum class a consumer routes the binding value through:CENSO_EVENT_KIND("censo_event_kind") — Modelo 036 censo status.CCAA("CCAA") — Modelo 100 autonomic-community tax residence.ESTIMACION_DIRECTA_MODALIDAD("EstimacionDirectaModalidad") — Modelo 100 estimación-directa modality.LEGAL_ENTITY_FORM("LegalEntityForm") — Modelo 200 legal form.RENTAL_REDUCTION_ART_23_2_TIER("RentalReductionArt232Tier") — Modelo 100 reducción por arrendamiento de vivienda tier (LIRPF Art. 23.2).
BEHAVIOUR-PRESERVING LIFT: every member’s string VALUE equals the annotation token that was previously a bare
strinBindingDefinition.typed_enum. Those tokens live in registry TOML and flow through operator-facing surfaces (bindings listtable, theModeloBindingQueryRowprojection, the borrador resolver, the Sheets-pull router); aStrEnumserialises to its value, so narrowing the field fromstr | Noneto this enum changes the static type without changing any stored, compared, or emitted string (the modelo-enum-hardening precedent). Do NOT rename a stored token.Declared in
coreas a closed value set per the architecture contract; the loader hydrates the registry TOML’s raw token to its member at the schema boundary (see_coerce_typed_enum()). It is the closed-set annotation on the binding, distinct from the engineinput_channel(how a formula consumes the value); a binding may carry atyped_enumyet still be a numericdecimalchannel.- CENSO_EVENT_KIND¶
- CCAA¶
- ESTIMACION_DIRECTA_MODALIDAD¶
- LEGAL_ENTITY_FORM¶
- RENTAL_REDUCTION_ART_23_2_TIER¶
- class LedgerIncomeGrounding(*values)[source]¶
Bases:
StrEnumWhether a ledger income row’s contribution rests on declared invoice substrate.
An actividad-económica income row reaches a modelo income casilla by one of two structurally different routes, and the difference is a LEGAL one, not a presentation detail:
SUBSTRATE_DECLARED— the row carries an explicittaxable_base(the IVA-exclusive base imponible from its invoice). Every income fact reads a real declared figure.CASH_FALLBACK— no base is declared, so the only measure available is the raw bank-credited amount. That figure is net of any retención practicada and may be IVA-inclusive, so it is neither the ingresos íntegros the return asks for nor a base: theingresos_integros_sumfact folds the cash in (mis-measuring in a direction that depends on the invoice), whiletaxable_base_sumcontributes nothing for the row.
Declared in
coreas a closed value set per the architecture contract, so the domain registry protocol and the application aggregation pipeline key on ONE marker rather than each re-deriving the distinction fromtaxable_base_amount is None. Consumers must branch on the member, never on field-nullness: the marker is the fact.- SUBSTRATE_DECLARED¶
- CASH_FALLBACK¶
- class InvoiceDevengoRank(*values)[source]¶
Bases:
StrEnumWhich source produced the LIVA art. 75 devengo date an invoice files under.
Period attribution – which quarter a cuota is declared in, which filing year an annual reconciliation sees – resolves on the art. 75 devengo date. That date is when the operation occurred, and the invoice date appears nowhere in art. 75.Uno: a B2B invoice may be issued up to the fifteenth of the following month while still belonging to the earlier period, so the issue date is wrong at exactly the month and quarter boundaries where attribution changes.
A fallback chain therefore cannot hand back a bare date. The two ranks below are not two spellings of one answer: one is a fact the taxpayer recorded, the other is a substitute that is right most of the time and wrong precisely where it matters. Returning them undifferentiated would let a consumer treat the substitute as the fact, which is the mistake the marker exists to prevent.
Declared in
coreas a closed value set per the architecture contract, besideLedgerIncomeGrounding, which answers the parallel question for the income measure. Consumers must branch on the member, never onoperation_date is None: the marker is the fact.D10’s third named rank – the bank movement date – is NOT a member here. An invoice always carries an issue date, so the invoice-side chain terminates at
ISSUE_DATE_PROXYand can never reach a movement date; the movement-date substitution belongs to the ledger-transaction side, whose dates have their own owners incadrumo.domain.transactions.dates. Declaring a member no producer on this axis can emit would be dead capacity wearing the shape of coverage.- OPERATION_DATE_DECLARED¶
The invoice records its own devengo-relevant date and it was used.
Covers both art. 75 clauses: the general-regime operation date (art. 75.Uno) and a pago anticipado’s collection date (art. 75.Dos). Which clause supplied it is a separate axis, recorded on the invoice’s own
operation_date_role; this marker answers only whether the date is a declared fact or a substitute.
- ISSUE_DATE_PROXY¶
No devengo date was recorded, so the issue date stood in for it.
The attribution is a best available guess, not a legal determination. It agrees with the declared fact for every operation invoiced in its own period and diverges exactly at the boundary cases, so a period whose figures rest on this rank is one an operator should be told about.
- class LedgerWithholdingDerivation(*values)[source]¶
Bases:
StrEnumHow a ledger income row’s retención figure was arrived at.
A ledger row never declares its retención – no transaction field records one – so every non-zero figure on this surface is derived, and the reader needs to know from what. Without the marker three structurally different situations are one indistinguishable
Decimal("0"): a row that genuinely had nothing withheld, a row whose substrate was too thin to tell, and a row whose arithmetic implied a withholding so large the figure was refused. The last is the dangerous one, because a refused inference and a real zero look identical in the sum.Declared in
coreas a closed value set per the architecture contract, besideLedgerIncomeGrounding, which answers the parallel question for the income measure.- NOT_APPLICABLE¶
The row is not an actividad-económica receipt, so no retención arises.
- NO_SUBSTRATE¶
No base is declared, or the cuota is not determinable from what is.
The withholding is unknown, not zero. The row still contributes its cash to the income measure, so it under-claims the offsetting credit.
- NONE_WITHHELD¶
nothing was withheld.
- Type:
Substrate sufficed and the cash covers the invoice
- DECLARED_ON_LINKED_INVOICE¶
Read from the retención the linked sales invoice itself declares.
The strongest source available: the figure the document states, not one reconstructed from what reached the bank. Preferred over every inference below it, because a declared figure beats a derived one.
- INFERRED_FROM_DECLARED_CUOTA¶
Derived as invoice gross minus cash, with the cuota read from the row.
- INFERRED_FROM_CATEGORY_ZERO_CUOTA¶
Derived the same way, with the cuota taken as zero because the declared IVA category makes it zero by law.
This is what lets an IVA-exempt professional service recover its retención: the operation has no cuota to record, so requiring a recorded one excluded precisely the invoices whose withholding matters most.
- REFUSED_ABOVE_SUPPORTED_RATE¶
The inference exceeded the registry maximum supported rate and was dropped.
An implied withholding above the RIRPF art. 95.1 general rate is evidence that the recorded cash is the base without IVA rather than a net-of-retención payment. Emitting the figure anyway would persist a fabricated withholding; capping it at the bound would invent one at exactly the legal maximum. The figure is therefore zero and this marker says why.
- class RetencionScheme(value)[source]¶
Bases:
strOpen wire token for a registry-owned retenciones scheme.
The published authority owns membership and meaning. This core primitive validates only the stable token shape and exposes
valuefor existing persistence/sorting call sites; it deliberately does not enumerate scheme values or import the registry authority.- Parameters:
value (str)
- Return type:
- class WorkIncomeRetencionTreatment(**data)[source]¶
Bases:
BaseModelTyped result carrier for a registry-resolved withholding treatment.
- Parameters:
data (Any)
- scheme: RetencionScheme¶
- is_fixed_rate: bool¶
- class RetencionClave(value, *, _registry_validated=False)[source]¶
Bases:
StrictRegistryTokenOpaque Modelo 190/193 clave token projected from registry membership.
The selected withholding catalogue owns the letter vocabulary and its model applicability. Core carries only the typed token boundary; callers must obtain instances through the registry projection.
- class IntracomOperationType(*values)[source]¶
Bases:
StrEnumModelo 349 operation-key letters carried by invoice records.
- E¶
- H¶
- M¶
- S¶
- T¶
- R¶
- A¶
- ADQUISICION_SERVICIOS¶
- D¶
- C¶
- class TravelAgencyMediationType(value, *, _registry_validated=False)[source]¶
Bases:
StrictRegistryTokenOpaque travel-agency mediation token projected from fact 0133.
RD 1619/2012 disposición adicional cuarta owns the vocabulary and its Modelo 347 consequences. Core carries only the typed token boundary; callers must receive instances through the facts-registry projection.
- class ThirdPartyDeclarationRole(value, *, _registry_validated=False)[source]¶
Bases:
StrictRegistryTokenOpaque third-party declaration-role token projected from fact 0134.
The role vocabulary, legal grounding, and C/D/E role projections belong to the facts registry. Core carries only the validated token boundary; callers must obtain instances through the typed registry projection.
- class ForeignAssetClass(*values)[source]¶
Bases:
StrEnumModelo 720 asset classes (clave de tipo de bien).
Source: AEAT Modelo 720 instrucciones. Each clave is declared separately; the declarability gate applies the 50,000 EUR floor to the regulatory obligation block that contains the class after the aggregator runs. Declared in
coreas a closed value set.- ACCOUNT¶
- SECURITY¶
- COLLECTIVE_INVESTMENT¶
- INSURANCE¶
- REAL_ESTATE¶
- VIRTUAL_CURRENCY¶