aeat.core._modelo module

Canonical AEAT modelo identifier enumeration.

This module exposes the closed set of AEAT modelo identifiers the codebase references. Almost every member has a directory under src/aeat/_data/registry/aeat/modelos/ and is therefore registry-loadable; the enum value is the bare three-digit code string ("036", "100", …) so it is directly substitutable for the existing bare-string usage throughout the codebase.

A small set of members are known modelos that the code references as identifiers but which have no registry definition by design — currently the retired Modelo.M037 (censo simplificada, suppressed by Orden HAC/1526/2024). These are enumerated in NON_REGISTRY_MODELOS. They are real codes with implementation support (lifecycle routing, portal entries), but validate_modelo() raises for them and no registry TOML exists or may be created for them.

Filing-grade authority — deadline windows, period restrictions, and casilla definitions — remains the ValidatedRegistryAuthority and the typed RegistrySnapshot it produces. This enum is the closed-set identifier type: it tells you which modelos exist; the registry tells you what (if anything) they contain. Modelo-specific support such as filing, export, extraction, and verification is declared on registry data such as capabilities, not by branching on this enum.

A gate test in src/aeat/core/tests/test_modelo.py binds the registry-backed members to application.modelo.registry_modelo_codes() (enum minus NON_REGISTRY_MODELOS) so the two cannot drift silently, and pins every non-registry member to its deliberately-absent registry definition.

class Modelo(*values)[source]

Bases: StrEnum

Closed enumeration of AEAT modelo identifier codes.

Each member’s name is the prefixed form (M036, M100, …) and its value is the bare three-digit code string ("036", "100", …). Because Modelo inherits from str via StrEnum, every member compares equal to its raw string value:

Modelo.M303 == "303"   # True
Modelo.M303 == Modelo.M303   # True

The registry-backed members are bound to the registry directory listing by application.modelo.registry_modelo_codes(); adding a new modelo to the registry without updating this enum will fail the core parity test. Members in NON_REGISTRY_MODELOS (the retired M037 and the recognized-but-not-yet-modeled obligations in UNMODELED_OBLIGATIONS) are known identifiers with no registry definition by design.

Use this enum at code boundaries that carry a modelo identifier. Operator input and registry TOML still enter as strings; callers may coerce with Modelo(value) when they need the closed enum contract, while registry consumers validate loadable support through the registry authority.

M030
M035
M036
M037
M038
M043
M100
M111
M115
M117
M121
M122
M123
M126
M128
M130
M131
M136
M140
M143
M145
M149
M150
M151
M156
M159
M165
M170
M171
M172
M173
M179
M180
M181
M182
M184
M185
M186
M187
M188
M189
M190
M192
M193
M194
M196
M198
M200
M202
M210
M216
M218
M221
M235
M236
M240
M241
M242
M220
M222
M231
M232
M233
M234
M238
M270
M280
M289
M290
M291
M294
M295
M296
M303
M308
M309
M322
M341
M345
M347
M349
M353
M360
M361
M362
M363
M368
M369
M379
M380
M390
M430
M480
M490
M510
M511
M517
M518
M519
M520
M521
M522
M524
M548
M553
M554
M555
M556
M557
M558
M559
M560
M561
M562
M563
M566
M568
M570
M573
M576
M580
M581
M582
M583
M584
M585
M586
M587
M588
M589
M590
M591
M592
M593
M595
M596
M604
M714
M718
M720
M721
M795
M796
M797
M798
M763
M840
M848
M993
UNMODELED_OBLIGATIONS: Mapping[Modelo, str]

Recognized AEAT obligation modelos the registry does not yet model, each mapped to a short description of the obligation. These are real, currently fileable AEAT forms outside the registry directory listing; the obligation-coverage reconciliation (application.overview.build_obligation_coverage()) treats them as part of the AEAT obligation universe and surfaces each as an advised (registry-unmodeled → investigate) row rather than leaving it invisible. Promoting one to a full registry definition (deadline windows + applicability rule) removes it from this delta and folds it into application.modelo.registry_modelo_codes(). The mapping is the extensible edge of AEAT-wide enrollment: it ratchets up as obligations are recognized and shrinks as they are modeled. The current set covers the common retención autoliquidaciones and declaraciones informativas an autónomo, a PYME, or an entity may owe, grounded against AEAT’s published catalogue of declaraciones informativas and retención forms; it is not yet AEAT’s full ~200-form set.

OUT_OF_SCOPE_OBLIGATIONS: Mapping[Modelo, str]

Registry modelos deliberately out of scope of the overview obligation calendar, each mapped to the recorded reason it is not scheduled or advised. The obligation-coverage reconciliation (application.overview.build_obligation_coverage()) partitions every obligation in the AEAT universe into exactly one of surfaced, confidently excluded, advised (investigate), or out of scope; this mapping is the sole home of the last bucket, so “invisible” is always a recorded product-scope decision rather than a silent omission. A modelo absent from a deadline window and from an applicability rule but NOT listed here is advised, not dropped — membership here is the explicit opt-out. It spans both registry forms declared out of scope and recognized non-registry forms a general taxpayer never files.

NON_REGISTRY_MODELOS: frozenset[Modelo]

Known modelo identifiers that intentionally have no registry definition. These are real, code-referenced modelos for which validate_modelo() raises and no registry TOML exists or may be created. Three reasons put a member here: the retired Modelo.M037 (censo simplificada, suppressed by Orden HAC/1526/2024; superseded by Modelo.M036), every recognized-but-not-yet-modeled obligation in UNMODELED_OBLIGATIONS, and every recognized non-registry obligation declared out of scope (_UNMODELED_OUT_OF_SCOPE_OBLIGATIONS). The parity gate compares the remaining members to application.modelo.registry_modelo_codes(), so the enum can carry retired codes, recognized-unmodeled obligations, and out-of-scope non-registry forms without implying the registry can load them.