"""Strict pydantic v2 schema for parsed AEAT justificantes.The :class:`Justificante` record is the boundary-crossing type consumedby downstream subpackages (the submission engine and the statusreader). It is *frozen* and *strict* so callers can rely ondeterministic field types, and so mutating it after parse requires anexplicit :meth:`pydantic.BaseModel.model_copy`."""from__future__importannotationsfromdatetimeimportdatetimefromdecimalimportDecimalfromenumimportStrEnumfrompathlibimportPathfrompydanticimportAnyHttpUrl,BaseModel,Field,ValidationInfo,field_validatorfrom...coreimportSTRICT_FROZEN_CONFIG,Period,PeriodError
[docs]classJustificanteParserBackend(StrEnum):"""Closed set of supported parser backends. Attributes: PDFPLUMBER: Fidelity-first default backend. """PDFPLUMBER="PDFPLUMBER"
[docs]classJustificante(BaseModel):"""Parsed AEAT *justificante de presentación* receipt. A ``Justificante`` represents a single successful filing receipt produced by AEAT after a modelo has been submitted. Every field is either pulled verbatim from the PDF body or derived deterministically from the source file (``source_pdf_sha256``, ``parsed_at``). Attributes: csv: Código Seguro de Verificación — the short AEAT-assigned hash used to verify the document on the Sede electrónica. modelo: String ID of the modelo the receipt belongs to. References the modelo catalogue in :mod:`domain.modelos`. period: Typed filing period resolved from the AEAT period token printed on the receipt and ``ejercicio``. ejercicio: Four-digit tax year as printed on the receipt, when present. ``None`` for receipts that omit the label. presentation_id: AEAT's internal ``Número de justificante`` if present on the receipt; ``None`` when the modelo does not print a separate presentation ID. presented_at: Timestamp AEAT stamped on the receipt at submission. tax_id: NIF/NIE of the taxpayer who filed (the *autónomo* owner). total_a_ingresar: Amount to be paid in, if the receipt includes one. total_a_devolver: Amount to be refunded, if the receipt includes one. verification_url: AEAT URL printed on the receipt where the CSV can be re-verified against the Sede electrónica. source_pdf_path: Privacy-preserving source reference derived from the source PDF digest. source_pdf_sha256: Lowercase hex sha-256 of the source PDF bytes. parsed_at: UTC wall-clock time the parse finished. """model_config=STRICT_FROZEN_CONFIGcsv:str=Field(...,min_length=4,max_length=64)modelo:str=Field(...,min_length=1,max_length=16)ejercicio:str|None=Field(default=None,max_length=8)period:Periodpresentation_id:str|None=Field(default=None,max_length=64)presented_at:datetimetax_id:str=Field(...,min_length=4,max_length=32)total_a_ingresar:Decimal|None=Nonetotal_a_devolver:Decimal|None=Noneverification_url:AnyHttpUrlsource_pdf_path:Pathsource_pdf_sha256:str=Field(...,pattern=r"^[0-9a-f]{64}$")parsed_at:datetime@field_validator("period",mode="before")@classmethoddef_coerce_printed_period(cls,raw_period:object,info:ValidationInfo)->object:ejercicio=info.data.get("ejercicio")ifnotisinstance(raw_period,str):returnraw_periodifnotisinstance(ejercicio,str)ornotejercicio.isdigit():returnraw_periodtry:returnPeriod.from_year_and_code(int(ejercicio),raw_period)exceptPeriodError:returnraw_period