Source code for aeat.adapters.inbound.declaracion._detect
"""Template-revision detection for declaración PDFs.AEAT prints the form code + año + period in a header / footer stamp.``detect_template_revision`` peeks at the first page text; returns``None`` on ambiguity so the caller can fall back to explicit``--modelo --año`` flags. Detection resolves template identity only; therevision tag selects the registry ``declaracion_pdf`` extraction profilefor the matched modelo and ejercicio.The detector returns :class:`~adapters.inbound.declaracion.TemplateRevision`records; it does not load:class:`~domain.calculations.registry.RegistrySnapshot` data or choose anextraction profile. Registry validation happens in:func:`~adapters.inbound.declaracion.parse_declaracion`."""from__future__importannotationsimportrefrompathlibimportPathfrom._parsersimportextract_pages_textfrom._schemaimportTemplateRevision_HEADER_MODELO_RE=re.compile(r"Modelo\s*[:\-]?\s*(?P<modelo>\d{3}[A-Z]?)",re.IGNORECASE)_HEADER_EJERCICIO_RE=re.compile(r"Ejercicio\s*[:\-]?\s*(?P<ejercicio>\d{4})",re.IGNORECASE)# Real AEAT declaraciones print ``Aprobado por Orden HAC/{number}/{año}`` on# the footer; when present it pins the template revision precisely. Absent,# we fall back to a ``{ejercicio}.01`` sentinel._ORDEN_HAC_RE=re.compile(r"Orden\s+HAC/\s*(?P<number>\d+)\s*/\s*(?P<año>\d{4})",re.IGNORECASE,)
[docs]defdetect_template_revision(pdf_path:Path)->TemplateRevision|None:"""Peek at the PDF's first page and return a :class:`TemplateRevision`. Returns ``None`` if the header does not yield both a modelo and an ejercicio. Revision resolution: 1. If the PDF prints an ``Orden HAC/N/YYYY`` footer, use ``"{año}.orden-{N}"``. 2. Else fall back to ``"{ejercicio}.01"``. The fallback is deliberately narrow: the registry must refuse any revision it has not explicitly registered. The ``Ejercicio`` stamp is searched across the first two pages of the header cluster — some Modelo 100 pre-2022 justificantes print the ``INFORMACIÓN DE LA PRESENTACIÓN`` cover page without the ``Ejercicio YYYY`` line, deferring that marker to page 2. """returndetect_template_revision_from_pages(extract_pages_text(pdf_path))
[docs]defdetect_template_revision_from_pages(pages:tuple[str,...])->TemplateRevision|None:"""Return a :class:`TemplateRevision` from already-extracted PDF page text. Args: pages: Per-page text extracted from a declaración PDF. Returns: The detected :class:`TemplateRevision`, or ``None`` when the header pages do not carry enough modelo/year signal. """ifnotpages:returnNone# Union of pages 1-2 header region: some Modelo 100 PDFs print# the ``Ejercicio YYYY`` stamp on page 2, not page 1.head_span="\n".join(pages[:min(2,len(pages))])[:4000]tail=pages[-1][-2000:]modelo_match=_HEADER_MODELO_RE.search(head_span)ejercicio_match=_HEADER_EJERCICIO_RE.search(head_span)ifmodelo_matchisNoneorejercicio_matchisNone:returnNonemodelo=modelo_match.group("modelo")ejercicio=ejercicio_match.group("ejercicio")orden_match=_ORDEN_HAC_RE.search(head_span)or_ORDEN_HAC_RE.search(tail)iforden_matchisnotNone:revision=f"{orden_match.group('año')}.orden-{orden_match.group('number')}"else:revision=f"{ejercicio}.01"returnTemplateRevision(modelo=modelo,año=int(ejercicio),revision=revision,detected_from="header",)