aeat.adapters.inbound.financial.providers._base module¶
Shared provider contracts and ingest helpers.
Defines the FinancialProvider ABC together with the validation
record ProviderValidation, the provider error hierarchy
(FinancialProviderError, InvalidFinancialSourceError,
UnsupportedFinancialSourceError, BankStatementParseError),
and the parsing / provenance helpers concrete providers reuse to emit
RawTransaction records with consistent
RawProvenance.
Providers emit ParsedLedgerRow objects: the source sign is consumed at
the adapter boundary into a TransactionDirection,
while the stored raw amount is an absolute magnitude.
Provider corpus discipline¶
Every concrete FinancialProvider subclass must declare two
class-level corpus attributes:
verification_sourceA string literal describing how the corpus fixtures were obtained:
"real_bank_corpus_pdf"— sanitised PDFs from real bank statements held by the operator."synthetic_from_bank_published_text"— PDFs generated from sanitised text dumps published in third-party open-source corpora (e.g., portfolio-performance); structure matches real layouts but origin is reconstructed."no_corpus"— no corpus is currently held. The provider must also setprovisional_pending_specimen = True.
provisional_pending_specimenTruewhen no real-corpus PDF has been parsed to confirm the provider produces correct output. Providers withno_corpusmust set thisTrue; providers with a confirmed corpus round-trip set itFalse.
The test suite in test_pdf_n26.py and the detection-invariant test
enforce these attributes at collection time so a newly enrolled provider
cannot silently ship without the declaration.
- CorpusVerificationSource¶
Allowed values for
FinancialProvider.verification_source.alias of
Literal[‘real_bank_corpus_pdf’, ‘synthetic_from_bank_published_text’, ‘no_corpus’]
- exception FinancialProviderError(message=None, *, context=None, suggestion=None, translated_message=None)[source]¶
Bases:
AeatErrorBase error raised by financial-ingest providers.
Subclasses
core.errors.AeatErrorso the application layer can catch every provider failure with oneexceptclause.- Parameters:
- Return type:
None
- code: ClassVar[ErrorCode]¶
- exception FinancialProviderConfigError(message=None, *, context=None, suggestion=None, translated_message=None)[source]¶
Bases:
FinancialProviderErrorRaised when a
FinancialProvidersubclass declaration is invalid.Fired by
__init_subclass__when the concrete provider class is missing or carries an invalidverification_sourceorprovisional_pending_specimenclass variable.- Parameters:
- Return type:
None
- code: ClassVar[ErrorCode]¶
- exception UnsupportedFinancialSourceError(message=None, *, context=None, suggestion=None, translated_message=None)[source]¶
Bases:
FinancialProviderErrorRaised when no provider can interpret a source document.
- Parameters:
- Return type:
None
- code: ClassVar[ErrorCode]¶
- exception InvalidFinancialSourceError(message=None, *, context=None, suggestion=None, translated_message=None)[source]¶
Bases:
FinancialProviderErrorRaised when a source document is unreadable or structurally invalid.
- Parameters:
- Return type:
None
- code: ClassVar[ErrorCode]¶
- exception FinancialValidationError(message=None, *, context=None, suggestion=None, translated_message=None)[source]¶
Bases:
FinancialProviderErrorRaised when a specific field (date, amount) fails domain validation.
- Parameters:
- Return type:
None
- code: ClassVar[ErrorCode]¶
- exception BankStatementParseError(message=None, *, missing=(), malformed=(), ambiguous=(), coverage=None)[source]¶
Bases:
FinancialProviderErrorRaised when a bank statement PDF cannot be fully parsed.
Carries structured attributes that allow callers to assert on the failure kind without parsing the message string — the same pattern as
DeclaracionParseErrorandJustificanteParseError.This error is appropriate for PDF-specific parse failures where the document was accepted by format detection but extraction produced incomplete or malformed results. Low-level structural failures (file unreadable, wrong bank marker) continue to raise
InvalidFinancialSourceError.- Variables:
missing – Tuple of field or row identifiers that produced no match in the PDF text (e.g. a required header field absent).
malformed – Tuple of field identifiers whose captured value could not be coerced to the target type (date, amount, currency).
ambiguous – Tuple of field identifiers that matched more than one region in the PDF.
coverage – Fraction of expected transaction rows successfully extracted (
Decimal).Nonewhen the error is not a coverage failure.
- Parameters:
- Return type:
None
- code: ClassVar[ErrorCode]¶
- class ProviderValidation(**data)[source]¶
Bases:
BaseModelTyped validation result returned before ingest.
- Variables:
is_valid – Whether the source document can be ingested.
warnings – Human-readable warning strings; non-empty even when
is_validis True (e.g., a missing currency column).detected_encoding – Provider-specific encoding marker, when applicable (CSV byte decoding, OFX parser tag, etc.).
detected_dialect – Compact provider-specific dialect / layout description used in operator diagnostics.
- Parameters:
- is_valid: bool¶
- warnings: tuple[str, ...]¶
- detected_encoding: str | None¶
- detected_dialect: str | None¶
- class FinancialProvider[source]¶
Bases:
ABCAbstract base class for file-backed raw transaction providers.
Concrete subclasses must declare
name,supported_extensions,source_format,verification_source, andprovisional_pending_specimenand implementingest()plusvalidate_source(). The shared_build_provenance()helper centralisesRawProvenanceconstruction so every emittedRawTransactioncarries consistent provenance metadata.See the module docstring for the corpus discipline contract that
verification_sourceandprovisional_pending_specimenjointly enforce.- Variables:
name – Stable provider identifier embedded in synthetic transaction ids and provenance records.
supported_extensions – Lowercase file extensions (including the leading dot) the provider accepts.
source_format – Source-format enum used for provenance.
verification_source – Corpus provenance declaration; one of
"real_bank_corpus_pdf","synthetic_from_bank_published_text", or"no_corpus".provisional_pending_specimen –
Truewhen no confirmed corpus round-trip has been performed for this provider. Providers withverification_source = "no_corpus"must set thisTrue.
-
source_format:
ClassVar[SourceFormat]¶
-
verification_source:
ClassVar[Literal['real_bank_corpus_pdf','synthetic_from_bank_published_text','no_corpus']]¶
- can_handle(path)[source]¶
Return whether the provider is a plausible match for
path.- Parameters:
path (
Path) – Candidate source document.- Return type:
- Returns:
True if
pathexists and its extension is insupported_extensions. Content sniffing is left tovalidate_source()anddetect_provider().
- abstractmethod ingest(path)[source]¶
Yield parsed ledger rows from
path.Implementations must produce one
ParsedLedgerRowper source row (viabuild_raw_transaction()), each carrying a magnitudedomain.transactions.RawTransactionplus thedomain.transactions.TransactionDirectionderived from the source sign at the parse boundary, with provenance built via_build_provenance().- Parameters:
path (
Path) – Source document to ingest.- Return type:
- Returns:
An iterator that yields one parsed ledger row per source row.
- Raises:
InvalidFinancialSourceError – When the document cannot be parsed or a row is malformed.
- abstractmethod validate_source(path)[source]¶
Validate
pathbefore ingesting it.- Parameters:
path (
Path) – Candidate source document.- Return type:
- Returns:
A
ProviderValidationdescribing whether the document is ingestable and surfacing any warnings.
- parse_date_value(value, *, day_first=True, label='date')[source]¶
Parse a bank-statement date or date-time into a
date.
- parse_amount_value(value, *, decimal_separator=None)[source]¶
Parse bank-export numeric text into
Decimalwithout float coercion.Float coercion is forbidden because bank exports carry exact cents; intermediate floats would silently round (e.g.,
1234.56→1234.5599999...). The parser preserves the sign convention of the source — bracketed(123.45)and trailing-minus123.45-both decode to a negativedecimal.Decimal.- Parameters:
- Return type:
- Returns:
A
Decimalpreserving the printed precision and sign.- Raises:
FinancialValidationError – When the value is empty or cannot be parsed.
- synthesize_transaction_id(*, provider_name, source_sha256, source_row_index)[source]¶
Build a deterministic synthetic transaction identifier.
- class ParsedLedgerRow(**data)[source]¶
Bases:
BaseModelOne parsed source row: a magnitude
RawTransaction+ its flow.The provider observes the bank export’s sign (or native debit/credit signal) once, at the parse boundary, to choose a
TransactionDirection; it then stores the absolute magnitude onrawand discards the sign. Downstream the import action carriesdirectionstraight onto theTransaction, never re-deriving flow from a sign that no longer exists.- Variables:
raw – The verbatim per-row
RawTransactioncarrying the non-negative magnitude amount.direction – The authoritative flow direction derived from the source sign at the parse boundary.
- Parameters:
raw (RawTransaction)
direction (TransactionDirection)
- raw: RawTransaction¶
- direction: TransactionDirection¶
- direction_from_signed_amount(signed_amount)[source]¶
Map a source-signed amount to the authoritative
TransactionDirection.A negative source amount is an OUTGOING flow (money out); a positive source amount is INCOMING (money in). The sign is consumed exactly once here, at the adapter boundary, and discarded; the stored amount is the absolute magnitude. A zero amount carries no flow and is rejected by
build_raw_transaction()before this is called.- Return type:
- Parameters:
signed_amount (Decimal)
- build_raw_transaction(*, provider, path, source_sha256, source_row_index, provider_transaction_id, booked_date, value_date, amount, currency, counterparty, description, raw_fields)[source]¶
Create one
ParsedLedgerRowfrom a source-signed amount.amountis the source-signed value as the parser read it. Its sign is consumed here to choose theTransactionDirection, then the storedRawTransactioncarries the absolute magnitude (flow lives indirection, never in the sign). A zero-amount row carries no flow and is refused at the parse boundary, consistent with the manual ledger path.- Raises:
InvalidFinancialSourceError – When
amountis zero.- Return type:
- Parameters: