cadrumo.domain.calculations.registry.schema_formula module

Formula and parameter schema models for the registry.

class LegalParameterDataType(*values)[source]

Bases: StrEnum

The shape a legal parameter’s declared value takes.

Overlaps CasillaDataType on six tokens but is NOT a narrowing of it: a parameter can be a bracket table, and a casilla never can. The two answer different questions – what a stored value IS, and what a parameter DECLARES – so they are kept apart despite the shared spellings.

DECIMAL
MONEY
INTEGER
RATIO
TEXT
BOOLEAN
BRACKET_TABLE
KEYED_BRACKET_TABLE
class FormulaExpression(**data)[source]

Bases: RegistryModel

Represent one formula leaf or operator node.

A leaf carries exactly one source, while an operator carries no leaf source and must satisfy the canonical argument-count contract.

op: FormulaOperator | None
args: tuple[FormulaExpression, ...]
casilla_id: CasillaId | None
binding: BindingId | None
date_binding: BindingId | None
parameter: ParameterId | None
literal: DecimalValue | None
dispatch_table: Annotated[Mapping[str, ParameterId], FROZEN_MAPPING] | None
class DatedValue(**data)[source]

Bases: RegistryModel

Map one decimal value to a date axis and validity window.

value: DecimalValue
date_axis: DateAxisField
valid_from: date
valid_to: date | None
comparison: ThresholdComparisonField

How this value is compared when it is used as a threshold.

A provision can be redrafted so that only the COMPARISON moves: LIVA art. 103.Dos.2 read “exceda en un 20 por 100” until 2014 and “exceda en un 10 por ciento o mas” from 2015, and the added “o mas” shifts the boundary case from excluded to included. Two redactions of one provision therefore differ on the operator as well as the value, and could not be two dated values while the operator lived in a Python year branch.

Defaults to EXCLUSIVE, which is the semantics every shipped value is read with today, so declaring the field changes no existing meaning. A value that is not used as a threshold simply never has it read.

class BracketEntry(**data)[source]

Bases: RegistryModel

One row of a piecewise-linear bracket schedule (e.g. an IRPF escala).

Each entry encodes a half-open base-amount interval [lower_bound, upper_bound] plus the cuota previously accumulated up to lower_bound (fixed_addition) and the marginal rate applied to the slice above lower_bound. A None upper_bound declares the open-ended top bracket.

Cuota for a base amount base resolved by lookup_bracket:

cuota = fixed_addition + marginal_rate * (base - lower_bound)

lower_bound: DecimalValue
upper_bound: DecimalValue | None
fixed_addition: DecimalValue
marginal_rate: DecimalValue
valid_from: date
valid_to: date | None
class KeyedBracketEntry(**data)[source]

Bases: RegistryModel

One row of a string-keyed rate-lookup table.

Sister shape to BracketEntry for parameters that dispatch on a categorical registry token rather than a piecewise-linear numeric interval. Each row binds a key (e.g. an IRNR tipo_renta code: general / ue_residente / ganancia_patrimonial / inmobiliaria) to a value (typically a Decimal rate) within a valid_from/valid_to window. Lookup is exact-match on (key, year) — there is no notion of interval overlap because the domain is token-discrete, not numeric-continuous.

First consumer: M210 IRNR m210-tipo-gravamen-2025.

key: str
value: DecimalValue
valid_from: date
valid_to: date | None
class NonFilingAxisAdmission(**data)[source]

Bases: RegistryModel

The declared justification for keying a parameter to a non-filing date.

Every dated value that ships today uses filing_period, and that is not an accident of authoring: for most regulated figures the period a declaration covers IS the thing the law keys to. The other axes exist for figures fixed by an EVENT – a capital good’s acquisition, an invoice’s issue date – where the filing period genuinely cannot express the rule.

That distinction is a legal claim, so it is declared rather than inferred. A parameter reaching for a non-filing axis must name the provision that fixes the event date and say why the filing period cannot carry it, and the pair is checked at load. Without this an author can silently move a figure onto an event axis, which changes WHICH LAW applies to an old fact – the exact defect the single-value tripwires elsewhere in this tree exist to force into the open.

Variables:
  • date_axis – The axis being admitted. Must equal the axis the parameter’s values actually carry, so the declaration cannot drift from the data.

  • legal_ref – Catalogue id of the provision that fixes the event date.

  • reason – Why the filing period cannot express this figure’s applicability.

date_axis: DateAxisField
legal_ref: str
reason: str
class ParameterDefinition(**data)[source]

Bases: RegistryModel

Declare a dated or bracketed parameter with legal/source grounding.

The data type selects scalar values, numeric brackets, or keyed brackets; validation rejects empty, mixed, or overlapping declarations.

id: ParameterId
data_type: LegalParameterDataTypeField
unit: str
values: tuple[DatedValue, ...]
brackets: tuple[BracketEntry, ...]
keyed_brackets: tuple[KeyedBracketEntry, ...]
bracket_axis: DateAxisField | None
non_filing_axis_admission: NonFilingAxisAdmission | None
legal_refs: LegalRefs
source_refs: SourceRefs
source_citations: tuple[SourceCitation, ...]