Source code for aeat.application.transactions._diagnostics
"""Typed diagnostic records for the ledger-import use case.The CLI uses a closed catalogue of importdiagnostic kinds emitted by ``aeat app ledger import PATH --provider PROVIDER --verify``:- ``original-file`` — records the caller-supplied original source file path when it is available, so import reports can distinguish the parsed input path from the source artefact the operator intended to verify.- ``gap`` — detects calendar gaps in the imported transaction stream so the operator notices a missing month / week / day without rerunning analytics.- ``duplicate`` — flags an imported transaction whose stable id matches one already in the ledger so re-importing the same bank statement does not double-count.- ``parser`` — reports a parser-level problem (malformed cell, unknown column, encoding hint mismatch) without aborting the import outright.The CLI consumes :class:`~aeat.application.transactions.LedgerImportDiagnostic`records via:func:`~aeat.application.transactions.build_ledger_import_diagnostic` and rendersthem grouped by ``severity`` and ``kind``.See Also: :class:`~aeat.application.transactions.LedgerImportDiagnosticKind`, :class:`~aeat.application.transactions.LedgerImportDiagnostic`, and :func:`~aeat.application.transactions.build_ledger_import_diagnostic`."""from__future__importannotationsfromenumimportStrEnumfrompathlibimportPathfrompydanticimportBaseModel,Field,field_validatorfrom...coreimportSTRICT_FROZEN_CONFIGfrom...core.errorsimportBaseSeverityfrom...core.i18nimportTranslatableastr
[docs]classLedgerImportDiagnosticKind(StrEnum):"""Closed catalogue of ledger-import diagnostic categories."""ORIGINAL_FILE="original-file"GAP="gap"DUPLICATE="duplicate"PARSER="parser"
[docs]classLedgerImportDiagnostic(BaseModel):"""One typed diagnostic emitted by the ledger import use-case. Attributes: kind: Closed :class:`~aeat.application.transactions.LedgerImportDiagnosticKind`. severity: :class:`~aeat.core.errors.BaseSeverity`. message: A strictly-typed :class:`~aeat.core.i18n.Translatable` key. source_path: Optional pointer at the source artefact the diagnostic refers to (input file, provider name, etc.). source_locator: Optional sub-path inside ``source_path`` (row index, column name, period range) the diagnostic scopes to. affected_transaction_ids: Tuple of stable transaction identifiers the diagnostic refers to. Empty for file-wide diagnostics (e.g., a malformed header). """model_config=STRICT_FROZEN_CONFIGkind:LedgerImportDiagnosticKindseverity:BaseSeveritymessage:trsource_path:Path|None=Nonesource_locator:str|None=Field(default=None,max_length=256)affected_transaction_ids:tuple[str,...]=()@field_validator("message")@classmethoddef_require_authoritative_message(cls,value:str)->str:"""Reject diagnostics without an authoritative Spanish message."""from...core.i18nimporttrifnotvalueornotstr(value).strip():raiseValueError("message must be a non-empty Translatable key")# tr() humanises unknown keys into a readable fallback, so a plain# round-trip comparison no longer detects missing entries. Pass a# unique sentinel default: a key with no Spanish catalogue entry# renders back the sentinel verbatim.sentinel=f"\x00no-translation\x00{value}"rendered=tr(str(value),locale="es",default=sentinel)ifrendered==sentinel:raiseValueError(f"message key {value!r} has no authoritative Spanish translation")returnvalue@field_validator("source_locator")@classmethoddef_trim_source_locator(cls,value:str|None)->str|None:"""Trim the source locator while rejecting blank-but-not-None values."""ifvalueisNone:returnNonetrimmed=value.strip()ifnottrimmed:raiseValueError("source_locator must not be blank when provided")returntrimmed
[docs]defbuild_ledger_import_diagnostic(*,kind:LedgerImportDiagnosticKind,severity:BaseSeverity,message:tr,source_path:Path|None=None,source_locator:str|None=None,affected_transaction_ids:tuple[str,...]=(),)->LedgerImportDiagnostic:"""Construct a diagnostic with the canonical field order. Centralised factory so adding new optional metadata later means extending this helper rather than every emit site. The returned :class:`~aeat.application.transactions.LedgerImportDiagnostic` preserves the closed :class:`~aeat.application.transactions.LedgerImportDiagnosticKind` and :class:`~aeat.core.errors.BaseSeverity` values the CLI groups by. """returnLedgerImportDiagnostic(kind=kind,severity=severity,message=message,source_path=source_path,source_locator=source_locator,affected_transaction_ids=affected_transaction_ids,)