cadrumo.core.period module

Canonical period code enumeration for filing periods.

Period codes represent the filing frequency and scheme for tax returns: - Quarterly: 1T, 2T, 3T, 4T (standard filing periods) - Instalment: 1P, 2P, 3P, 4P (corporate-tax/IS instalment periods) - Annual: 0A - Monthly: 01-12 (calendar months) - OSS/IOSS: EXT-1T, EXT-2T, EXT-3T, EXT-4T (extra-Union scheme) - Ad-hoc/Event: AD-HOC, EVENT-N (event-driven filings)

StandardPeriodCode is the canonical StrEnum for the closed standard vocabulary (1T-4T, 1P-4P, 0A, 01-12). It includes both calendar spans and instalment claves, so callers that need date boundaries must still check Period.has_date_span(). RegistryPeriodCode is the pydantic field annotation for the full registry union: standard members plus extended OSS/IOSS literals, AD-HOC, the open-ended event shape, the administrative censo tokens, and the symbolic EVENT-N selector. FilingPeriodCode is the narrower annotation for a period a taxpayer files in — the same union minus the administrative tokens and the symbolic selector, neither of which names one concrete filing period. Two boundaries with different value spaces need two annotations: widening one to admit a registry coordinate must not widen the other. Period combines one accepted FILING code with a filing year, and PeriodKind classifies the resulting cadence.

This module is the runtime counterpart to the registry domain.calculations.registry.schema_scalars.PeriodCode alias and domain.calculations.registry.schema_references.PeriodSelector schema. Registry objects carry bare period tokens; application services that need a concrete filing window compose those tokens with a year into Period. Operator commands follow the same separated shape (--year YYYY --period <AEAT-token>); any year-qualified filter mini-grammar is converted at the CLI boundary before it reaches this model.

class StandardPeriodCode(*values)[source]

Bases: StrEnum

Canonical enumeration of closed standard filing-period codes.

Quarterly, monthly, and annual members have calendar spans. Instalment members (1P-4P) identify filing/payment events and intentionally do not imply a contiguous ledger date span.

Q1
Q2
Q3
Q4
P1
P2
P3
P4
ANNUAL
JAN
FEB
MAR
APR
MAY
JUN
JUL
AUG
SEP
OCT
NOV
DEC
is_administrative_period_token(token)[source]

Return whether token is one of the administrative censo period codes.

The administrative sub-vocabulary (Modelo 036’s ALTA / MODIFICACION / BAJA, Modelo 145’s COMUNICACION / VARIACION) addresses a registry revision rather than naming a period, so a caller holding a raw registry token generally needs to route it somewhere other than Period. This is the one public way to ask, so the membership test cannot be hand-copied into a second, drifting set.

Matching is on the registry’s own unaccented spelling. A caller reading a token off an AEAT-rendered surface — where Spanish prints MODIFICACIÓN, COMUNICACIÓN, VARIACIÓN — folds it with fold_diacritics() first; admitting accented spellings here would put this predicate and RegistryPeriodCode into disagreement about the same token.

Raises:

ValueError – When token is not a string.

Return type:

bool

Parameters:

token (str)

is_symbolic_event_selector(token)[source]

Return whether token is the symbolic EVENT-N registry selector.

The selector covers the concrete EVENT-1/EVENT-2 periods without being one, so a registry coordinate carrying it has no filing period.

Raises:

ValueError – When token is not a string.

Return type:

bool

Parameters:

token (str)

is_filing_period_token(token)[source]

Return whether token names exactly one period a taxpayer can file.

Registry selectors also carry administrative censo tokens and the symbolic EVENT-N selector, which cover filings without being one; neither builds a Period.

Raises:

ValueError – When token is not a string.

Return type:

bool

Parameters:

token (str)

accepted_filing_period_codes()[source]

Return the fully enumerable codes a Period accepts.

The administrative tokens are excluded — they address a registry revision, not a filing period — as is the infinite EVENT-<number> family.

Return type:

tuple[str, ...]

accepted_filing_period_patterns()[source]

Return human-readable patterns for the periods a Period accepts.

Return type:

tuple[str, ...]

exception PeriodError(message=None, *, context=None, translated_message=None)[source]

Bases: CadrumoError

Raised when a Period is constructed from an invalid year/code.

Pydantic boundaries translate this registered failure to ValueError when they need Pydantic’s structured validation envelope.

Parameters:
  • message (str | None)

  • context (Mapping[str, object] | None)

  • translated_message (str | None)

Return type:

None

code
class PeriodKind(*values)[source]

Bases: StrEnum

Cadence class of a filing period, derived from its registry code.

QUARTERLY
MONTHLY
ANNUAL
INSTALMENT
EXTENDED
registry_period_kind(token)[source]

Return the cadence class of any REGISTRY period token.

Accepts the full RegistryPeriodCode vocabulary, so a registry-facing consistency check (a filing_schedules declaration reconciling its period_kind against its periods) can classify an administrative censo token or the symbolic EVENT-N selector. Both classify as PeriodKind.EXTENDED, matching what a Period reported for them while one validator still served both boundaries.

This lives here, rather than in the one registry validator that calls it, because _period_kind_for_code() is the single cadence authority Period.kind also reads. Registry ownership would force either a cross-package import of that private helper or a second token-to-cadence map — a duplicate authority for “what cadence is this token”. One narrow public function for one narrow caller is the cheaper of the two, and it names the vocabulary it speaks now that two vocabularies exist.

Raises:

ValueError – When token is not an accepted registry period code.

Return type:

PeriodKind

Parameters:

token (str)

class Period(**data)[source]

Bases: BaseModel

A filing period as one typed value: a year paired with a registry code.

Period is the canonical, fundamental representation of “which filing period” across the whole application. It composes exactly two authoritative fields — the filing_year and the registry period code (a StandardPeriodCode member such as 1T / 0A / 03, or an extended union member such as EXT-1T / AD-HOC / EVENT-3) — and never a combined calendar string (2026Q1 / 2026-03 / 2026). Those calendar shapes are neither accepted inputs nor canonical outputs. The only display projection is the space-separated __str__() form ("2026 1T"), and normal inbound construction is from a (year, code) pair via from_year_and_code().

The model is frozen and hashes by (filing_year, code), so a Period is a drop-in dict key, set member, and equality target wherever a typed period is required. The ledger and aggregation boundary routes through application.aggregation.modelo_bindings.aggregation_period_for_modelo(), then uses contains() as the single date-boundary authority.

Variables:
  • filing_year – Filing year carried separately from the registry token.

  • code – Bare FilingPeriodCode token — the registry union minus the coordinates that address a revision rather than a filing period.

Parameters:

data (Any)

filing_year: int

Deliberately wider than the registry’s authored filing-year window.

A period is a calendar span, not a claim that a revision exists for it. It must be able to EXPRESS a year the registry cannot serve – a pre-1995 IVA regime under Ley 41/1994, or a 1999 coordinate whose refusal is the behaviour under test – so narrowing it to the filing-year axis would make those cases unrepresentable rather than refusable.

code: FilingPeriodCode
classmethod from_year_and_code(year, code)[source]

Build a Period from a filing year and a bare registry code.

Parameters:
  • year (int) – The filing year (e.g. 2026).

  • code (str) – A bare filing period code — a StandardPeriodCode value (1T-4T / 1P-4P / 0A / 01-12) or an extended union member (EXT-1T-EXT-4T / AD-HOC / EVENT-3). The code is validated against FilingPeriodCode; a combined calendar string, an administrative censo token, and the symbolic EVENT-N selector are all refused.

Raises:

PeriodError – When code is not an accepted filing period code or year is outside the supported range.

Return type:

Period

classmethod from_string(value)[source]

Parse the canonical display string emitted by __str__().

Only the separated "YYYY <registry-code>" display form is accepted. Combined calendar strings such as "2026Q1" or "2026-1T" are refused; this method is for display round-trips, not an alternate CLI or registry grammar.

Parameters:

value (str) – Space-separated display string containing a four-digit filing year and a bare registry period code.

Return type:

Period

Returns:

A Period parsed from the canonical display string.

Raises:

PeriodError – When value is not the display form or contains an invalid registry period code.

property registry_token: str

Return the bare registry period code as a string (e.g. "1T").

Use this when calling registry APIs that expect the bare domain.calculations.registry.schema_scalars.PeriodCode token rather than a structured Period.

property standard_code: StandardPeriodCode | None

Return the StandardPeriodCode member, or None for extended forms.

property quarter_ordinal: int | None

Return the standard-quarter ordinal, or None for every non-quarter period.

Only the ordinary 1T through 4T registry tokens are quarterly. Extended OSS/IOSS tokens such as EXT-1T retain their extended cadence and therefore do not acquire a quarter ordinal.

property is_quarterly: bool

Return whether this is one of the four ordinary quarterly periods.

property kind: PeriodKind

Return the cadence class derived from the period code.

has_date_span()[source]

Return whether the period maps to an inclusive calendar date span.

Quarterly, monthly, and annual periods cover a contiguous span of calendar dates. Instalment claves (1P-4P) and the extended union members are filing/payment events, not calendar spans, so they return False and start_date / end_date refuse for them.

Return type:

bool

property start_date: date

Return the inclusive first calendar date of the period.

Raises:

PeriodError – When the period has no calendar span (has_date_span() is False).

property end_date: date

Return the inclusive last calendar date of the period.

Raises:

PeriodError – When the period has no calendar span (has_date_span() is False).

contains(value)[source]

Return whether value falls within this period’s inclusive span.

Raises:

PeriodError – When the period has no calendar span.

Return type:

bool

Parameters:

value (date)