cadrumo.domain.calculations.registry.schema_exports module

Export and fixed-width record schema models.

This module owns the wire-facing declaration models so casilla and relation surfaces remain independent of fixed-width rendering policy.

class ExportLineEnding(*values)[source]

Bases: StrEnum

The line terminator a fixed-width export record is written with.

CRLF
LF
NONE
class ExportRecordRepeat(*values)[source]

Bases: StrEnum

What a repeating export record iterates over.

BINDING_ROWS
PROJECTION_ROWS
class RecordDiscriminatorRequirement(*values)[source]

Bases: StrEnum

Whether a discriminator field must be blank or filled.

BLANK
NON_BLANK
class FilingEnvelopePrefixRole(*values)[source]

Bases: StrEnum

Closed roles an AEAT variable-envelope prefix declares, in source order.

ONE vocabulary for every modelo, because the designs share one grammar: the thirty-five bundled 328-byte envelopes all open with the <T…> record identifier and an <AUX> block carrying the product identity between reserved spans. What differs between designs is which of these roles a given design prints and how wide each one is – both facts the declaration carries – not what the roles MEAN.

Declared in this order deliberately: a declaration’s roles must appear as a subsequence of this tuple, so a design that omits a role stays admissible while one that reorders the grammar refuses.

COMPOSED_OPENING_TAG is the one genuine alternative rather than an omission. Some designs (Modelo 200, 100, 714) print the whole seventeen-byte <T200020250A0000> identifier as ONE source row instead of the six separate rows Modelo 303 prints, so the two spellings are mutually exclusive and FilingEnvelopeDefinition refuses a declaration carrying both.

COMPOSED_OPENING_TAG
OPENING_TAG
MODELO
DISCRIMINANT
FILING_YEAR
PERIOD
RECORD_TYPE
AUX_OPENING_TAG
PRE_PROGRAM_FILLER
PROGRAM_IDENTIFIER
BETWEEN_IDENTITIES_FILLER
DEVELOPER_TAX_ID
POST_DEVELOPER_FILLER
AUX_CLOSING_TAG
class FilingEnvelopeCloserDerivation(*values)[source]

Bases: StrEnum

Closed algorithms deriving a variable envelope’s closing identifier.

RELATIVE_CLOSER_V1
class FilingEnvelopeTotalDerivation(*values)[source]

Bases: StrEnum

Closed algorithms deriving a variable envelope’s declared total.

EMITTED_BYTE_TOTAL_V1
class FilingEnvelopePrefixFieldDeclaration(**data)[source]

Bases: RegistryModel

One reviewed envelope prefix role and its exact emitted byte length.

role: FilingEnvelopePrefixRoleValue
length: int
class FilingEnvelopeDefinition(**data)[source]

Bases: RegistryModel

Static variable-envelope declaration carried by a generated layout.

This is a declaration of the envelope grammar, not an instance filing payload. Period, product identity values, body occurrences, bytes, totals, and digests are deliberately absent: the application filing boundary owns those facts when it renders an approved draft.

Modelo-agnostic BY CONSTRUCTION rather than by convention. Every fact that used to be a Modelo 303 constant in code – the DP30300 identity, the thirteen-slot prefix, the 328-byte extent – is declared data here, so the same type carries Modelo 200’s eight-slot composed-tag prefix and Modelo 309’s thirteen without a branch. The modelo itself is deliberately NOT a field: the layout is owned by exactly one revision of one modelo, and the render path reads that identity from the selected snapshot rather than letting a second, drift-capable spelling of it exist here.

schema_version: Literal[1]
source_ref: SourceRefId
source_sha256: ContentDigest
record_identity: str
prefix_fields: tuple[FilingEnvelopePrefixFieldDeclaration, ...]
prefix_extent: int
body_record_ids: tuple[RecordId, ...]
product_identity_requirement: Literal['aeat-product-software-identity-v1']
closer_derivation: FilingEnvelopeCloserDerivationValue
total_derivation: FilingEnvelopeTotalDerivationValue
AUXILIARY_ENVELOPE_HEADER_SCHEMA_VERSION: Final[Literal[1]]

Wire version of the auxiliary-envelope header declaration. Named rather than written as a bare default so the number has one home: a reader can find every site bound to this format, and a version bump cannot land on the model while a writer stamping the old number silently disagrees with it.

class AuxiliaryEnvelopeHeaderDefinition(**data)[source]

Bases: RegistryModel

Static total-less page-zero header declaration carried by a generated layout.

The fixed 328-byte header that opens a design whose records are fixed (Modelo 232’s DR23200; Modelo 390’s page zero). It shares the filing envelope’s prefix grammar – the same thirteen roles in canonical order and the same emitted literals – and declares no body, closer or total: the layout’s records are the payload.

A declaration, not an instance payload, for the same reason its envelope sibling is: period and product-identity values are filing-instance facts the application filing boundary owns at render time.

schema_version: Literal[1]
source_ref: SourceRefId
source_sha256: ContentDigest
record_identity: str
prefix_fields: tuple[FilingEnvelopePrefixFieldDeclaration, ...]
prefix_extent: int
product_identity_requirement: Literal['aeat-product-software-identity-v1']
class ProjectionEndpointDeclaration(**data)[source]

Bases: RegistryModel

Revision-owned admission and evidence for one typed filing endpoint.

The declaration is deliberately independent of a generated layout. It makes the semantic endpoint identity and its legal/source grounding an immutable revision fact before record-design coordinates are generated.

id: ProjectionEndpointId

what it projects, never where.

Derived from the declaration’s own typed reference – the projection kind and the semantic axes of that reference – so the same endpoint carries the same identity in every edition that declares it, and the family inherits along a predecessor chain instead of being restated in full. The numbered casilla a reference resolves to is deliberately not part of it: that box is the edition’s address for the endpoint and renumbers between editions, while the endpoint does not.

Type:

The endpoint’s edition-free identity

projection_ref: FilingProjectionRef
legal_refs: LegalRefs
source_refs: SourceRefs
class ExportFieldDefinition(**data)[source]

Bases: RegistryModel

Declare one field’s position, value, and rendering constraints in an export.

id: ExportFieldId
offset: OneBasedExportOffset | None
length: int | None
kind: CasillaFieldKindValue
casilla_id: CasillaId | None
binding: BindingId | None
literal: str | None
producer_key: FilingProducerKey | None
projection_ref: FilingProjectionRef | None
draft_attribute: ExportDraftAttribute | None
computed_key: ExportComputedKey | None
data_type: ExportFieldDataType
required: bool
padding: ExportPaddingValue
justification: ExportJustificationValue
date_format: str | None
decimals: int | None
signed: bool
sign_position: ExportSignPositionValue
required_for: Literal['natural_person'] | None

A requirement the design states for one taxpayer legal form only.

Modelo 390 marks the sujeto pasivo nombre “OBLIGATORIO (persona fisica)”: required when the taxpayer is a natural person, and meaningless for an entity. A layout cannot know the filer, so the condition is carried here and evaluated at export against the filing’s own taxpayer.

design_type: Literal['N', 'Num'] | None

The numeric type the official record design prints for this slot.

AEAT’s type vocabulary: “N: numerico con signo”, “Num: numerico sin signo”. Carried by generated layouts from the design row they derive from, so the sign is held to the design at the registry boundary rather than trusted: a field typed N must be signed and one typed Num must not be. A hand- authored layout has no design row to supply it and declares none.

value_policy: ExportValuePolicyValue
allowed_values: tuple[str, ...] | None
legal_refs: LegalRefs
source_refs: SourceRefs
property endpoint_casilla_id: CasillaId | None

Return the exact numbered endpoint carried by this field, if any.

wire_shape()[source]

Project the declared shape onto the axes a value policy constrains.

Return type:

tuple[str, str, str, bool, str | None]

validate_allowed_values()[source]

Constrain an exact reviewed semantic integer domain.

Return type:

None

validate_value_policy()[source]

Require each explicit policy to match its complete fixed-width shape.

Return type:

None

validate_decimals()[source]

Require an explicit scale on every decimal field.

AEAT fixed-width slots carry decimals implicitly: the diseño de registro declares them as “N enteros y M decimales” filling the whole slot, with no separator byte. The scale is not derivable from the length – Modelo 200 pairs length 9 with 2 decimals and length 7 with 4 – so it has to be declared per field, and a field that omits it cannot be rendered.

Return type:

None

class RecordDiscriminator(**data)[source]

Bases: RegistryModel

Record-shape discriminator for record types that share literal prefixes.

A record’s literal-prefix matcher cannot tell two records apart when they share their leading literal fields (AEAT models several Tipo-2 record sub-shapes that all start with the same record-type literal). The discriminator declares a contiguous byte range whose populated-or-blank pattern uniquely identifies this record subtype, letting the parser pick the correct record while reading binding-row sequences.

offset: OneBasedExportOffset
length: int
requires: RecordDiscriminatorRequirementField
class ExportRecordDefinition(**data)[source]

Bases: RegistryModel

Declare one record type and its ordered field authority in an export layout.

id: RecordId
record_type: str
order: NonNegativeInt
encoding: ExportEncodingValue
line_ending: ExportLineEndingField
required: bool
repeat: ExportRecordRepeatField | None
binding_record: str | None
row_field_casilla_ids: Annotated[Mapping[str, CasillaId], FROZEN_MAPPING]
discriminator: RecordDiscriminator | None
requires_positive_casilla_id: CasillaId | None
fields: tuple[ExportFieldDefinition, ...]
repeat_field_family_failure(*, allow_unresolved_binding_record=True)[source]

Return the exact repeat/payload-family contradiction, if any.

Return type:

str | None

Parameters:

allow_unresolved_binding_record (bool)

class XmlDictionaryPathOverride(**data)[source]

Bases: RegistryModel

One official dictionary row whose declared path AEAT itself got wrong.

The bundled .properties dictionaries are official AEAT evidence bytes and are never edited: their value is that they are what AEAT published, defects included. So where a row’s path contradicts AEAT’s own XSD, the correction is declared here instead, beside the layout that consumes it.

Each override carries the evidence for itself in reason. That is not decoration: the override asserts that AEAT’s own published dictionary is wrong about a row, which is a claim a later reader must be able to audit rather than take on trust. “It disagrees with the XSD” is not sufficient grounds on its own, because AEAT republishes these schemas mid-year — so which of two AEAT artefacts to believe is a reviewed judgement about a specific row, never a fact derivable from the disagreement itself.

field_id: str
path: str
reason: str
class ExportLayoutDefinition(**data)[source]

Bases: RegistryModel

Declare the complete wire layout used to render an official export.

id: ExportLayoutId
format: ExportLayoutFormatValue
dictionary_source_ref: SourceRefId | None
source_refs: SourceRefs
legal_refs: LegalRefs
records: tuple[ExportRecordDefinition, ...]
filing_envelope: FilingEnvelopeDefinition | None

Variable-composition wrapper this fixed-width layout’s records live inside.

Absent for the majority of layouts, whose records ARE the payload. Present where the official design declares a Total: Variable envelope, in which case the records are its body members rather than the whole file.

auxiliary_envelope_header: AuxiliaryEnvelopeHeaderDefinition | None

Total-less 328-byte page-zero header emitted before this layout’s records.

Absent for every layout whose design declares no such header. Present where the design prints one (Modelo 232’s DR23200; Modelo 390’s page zero): the records remain the payload and the header is emitted as their prefix. A layout declaring both this and a filing envelope is refused, since no design composes a variable body behind a total-less page-zero header.

dictionary_path_overrides: tuple[XmlDictionaryPathOverride, ...]

Dictionary rows whose AEAT-declared path is corrected before use.

Applied where the dictionary is read rather than where it is rendered, because the writer and the export parser resolve their rows from the same call: a correction applied to only one of them would make an artefact verify as drift against itself.

aux_idioma: DeclaracionIdiomaValue

Language the declaration’s mandatory Aux/Idioma element declares.

The AEAT dictionary that drives an xml_dictionary render describes no Aux row at all, in any bundled revision, while every XSD makes the block mandatory and first — so the value cannot come from the dictionary and is declared here instead, beside the format that requires it.

aux_version: str | None

Producer token the declaration’s mandatory Aux/VERSION element carries.

Deliberately has NO default, and that is load-bearing rather than caution. AEAT declares the element as tipo_String4L — four characters against a permissive pattern, with no enumeration, no annotation, and no worked example carrying a genuine value anywhere in the bundled corpus. So a plausible token such as "1.00" would VALIDATE while asserting something nothing verified, the document would start passing our own checks, and the gap would stop being reported at all.

Two sources that look authoritative are not. A real AEAT-submitted declaration in the fixture corpus carries <VERSION>2.02</VERSION>, which is a redaction placeholder — the sanitiser assigned sequential field-position indices, and its siblings include an ECIVIL of 6 where the schema admits only 1-4. A governed fact would require legal evidence that this value lacks; declaring one would have meant inventing a citation.

Absent, the export refuses rather than emitting a partial Aux — which would be invalid regardless, since the element is minOccurs="1".