cadrumo.domain.deadlines.festivos module¶
Spanish business-day calendar and AEAT deadline shift.
This module is the holiday-adjustment service. It loads BOE- published national plus autonomous-community (“CCAA”) holiday calendars from governed event facts in the validated authority and exposes pure functions that answer two questions:
Is a given date a día hábil (business day) for AEAT filings in the taxpayer’s CCAA of tax residence?
Given an AEAT-registered close date, what is the legally-due filing deadline once weekends, national holidays, and CCAA holidays are considered, and what was the reason for the shift?
AEAT’s published deadline-shift rule (Calendario del Contribuyente, “Vencimientos en días inhábiles, sábados o festivos”) is:
“Si el último día del plazo coincide con un sábado, domingo o festivo, el plazo se entiende ampliado hasta el primer día hábil siguiente.”
The rule covers national holidays plus the autonomous-community holiday of the taxpayer’s domicilio fiscal. Local (municipal) holidays do NOT affect AEAT filing deadlines and are not part of this calendar.
One well-known exception: Modelo 369 (OSS / IOSS one-stop-shop)
deadlines do NOT shift, even when the close date falls on a non-
business day, because the OSS / IOSS regime is governed by the EU
Council Directive’s harmonised cutoffs and the AEAT cannot lengthen
the EU-wide window unilaterally. The exception list is encoded in
MODELOS_WITHOUT_SHIFT so future modelo additions land as data,
not as a fork in shift_deadline().
The substrate is pure domain logic: it never touches the CLI, never mutates input, and resolves only governed, BOE-cited event facts from the validated registry authority.
- class CalendarCCAA(value, *, _registry_validated=False)[source]¶
Bases:
strRegistry-projected ISO 3166-2:ES deadline-calendar territory token.
Fact 0143 owns the nineteen territory codes used by the deadline calendar, including the foral communities and autonomous cities that are intentionally outside fact 0129’s fifteen-member tax-residence vocabulary. Direct token construction is reserved for the typed catalogue projection; callers resolve membership through the same operation before constructing one.
- class HolidayJurisdiction(*values)[source]¶
Bases:
StrEnumLayer of government that declared the holiday.
NATIONAL— declared by the State; observed everywhere.CCAA— declared by an autonomous community; observed only in that CCAA’s territory.
AEAT does not consider municipal-level holidays for filing-deadline shifts, so a corresponding
LOCALvalue would be out of scope.- NATIONAL¶
- CCAA¶
- class Holiday(**data)[source]¶
Bases:
BaseModelA single declared holiday.
- Parameters:
data (Any)
- holiday_date: date¶
- jurisdiction: HolidayJurisdiction¶
- ccaa_code: CalendarCCAA | None¶
- name: _NonEmptyShortString¶
- class HolidayCalendar(**data)[source]¶
Bases:
BaseModelA year’s BOE-published holiday calendar.
boe_refis the citation stem of the BOE Resolución that published the annual relación de fiestas laborales.boe_urlis an optional convenience anchor for the same resolution.- Parameters:
data (Any)
- year: Annotated[int, Field(ge=2000, le=2100)]¶
- boe_ref: _NonEmptyShortString¶
- boe_url: str | None¶
- national: tuple[Holiday, ...]¶
- ccaa: tuple[Holiday, ...]¶
- class DeadlineShift(**data)[source]¶
Bases:
BaseModelOutcome of applying the AEAT deadline-shift rule to one close date.
Carries the original AEAT-registered close date, the legally-adjusted close date after weekend / holiday shifts, a structured reason, and references to the holiday source(s) that drove the shift.
shiftedis the boolean predicate “did the close date move?”;shift_daysis the (always non-negative) day count between original and adjusted;shift_reasonis a stable identifier consumers may use for output-formatting and rule-explanation; the optionalholiday_refsandjurisdictionstuples carry the specific holidays whose presence on the calendar caused the shift.- Parameters:
data (Any)
- original_close_date: date¶
- adjusted_close_date: date¶
- shifted: bool¶
- shift_days: NonNegativeInt¶
- shift_reason: _NonEmptyShortString¶
- jurisdictions: tuple[HolidayJurisdiction, ...]¶
- holiday_refs: tuple[str, ...]¶
- MODELOS_WITHOUT_SHIFT: tuple[str, ...]¶
Modelos whose deadlines are NOT shifted by the AEAT día-inhábil rule.
Per the AEAT Calendario del Contribuyente, the OSS / IOSS one-stop-shop regime (Modelo 369) is governed by the EU-harmonised cutoff date and AEAT cannot extend the window unilaterally even when the close date falls on a Spanish holiday or weekend. Adding new modelos to this tuple keeps the exception list data-driven;
shift_deadline()never grows a switch statement.
- load_holiday_calendar(year, *, operation)[source]¶
Load a calendar through the caller’s pinned operation.
- Return type:
- Parameters:
year (int)
operation (PinnedAuthorityOperation)
- holiday_calendar_from_authority(year, *, operation)[source]¶
Resolve one complete published calendar from the governed-fact authority.
The publication event is resolved first. It is the positive proof that an absent per-date holiday event means an ordinary business day rather than an unpublished calendar year. All individual holiday values are then read through exact event queries, retaining the authority’s provenance.
- Return type:
- Parameters:
year (int)
operation (PinnedAuthorityOperation)
- is_business_day(candidate, *, calendar, ccaa_code)[source]¶
Return True when
candidateis a business day for AEAT filings.A date is a business day when it is not a Saturday, not a Sunday, not a national holiday for that year, and (when
ccaa_codeis supplied) not a CCAA holiday for that ccaa-year pair. Whenccaa_codeisNonethe predicate degrades to national-only: callers with no tax-residence information get weekend + national detection but miss CCAA shifts.- Return type:
- Parameters:
candidate (date)
calendar (HolidayCalendar)
ccaa_code (CalendarCCAA | None)
- next_business_day(start, *, calendar, ccaa_code)[source]¶
Return the first date on or after
startthat is a business day.The walk is bounded by 14 days (a fortnight) to keep the function safe against pathological inputs; in practice the AEAT calendar never strings more than four non-business days together (e.g. Semana Santa weekend + Jueves Santo + Viernes Santo + Lunes de Pascua = 4 days).
- Return type:
- Parameters:
start (date)
calendar (HolidayCalendar)
ccaa_code (CalendarCCAA | None)
- shift_deadline(original_close_date, *, modelo, ccaa_code, calendar=None, operation)[source]¶
Apply the AEAT deadline-shift rule and return a
DeadlineShiftresult.The rule moves the deadline to the next business day when the original close date is a Saturday, Sunday, national holiday, or CCAA holiday of the taxpayer’s tax residence.
Modelo-specific exceptions (e.g., Modelo 369 OSS / IOSS) bypass the shift and return an unshifted
DeadlineShiftwith reasonmodelo_exception.When
calendaris omitted, the caller’s pinnedoperationresolves a BOE-published calendar through the governed publication and holiday event facts. A missing publication fact fails closed; it is never treated as a holiday-free calendar. Callers that already hold a calendar may pass it directly while retaining the operation that owns the workflow.- Return type:
- Parameters:
original_close_date (date)
modelo (str)
ccaa_code (CalendarCCAA | None)
calendar (HolidayCalendar | None)
operation (PinnedAuthorityOperation)