cadrumo.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, translated_message=None)[source]¶
Bases:
CadrumoErrorBase error raised by financial-ingest providers.
Subclasses
core.errors.hierarchy.CadrumoErrorso the application layer can catch every provider failure with oneexceptclause.- Parameters:
- Return type:
None
- code¶
- exception FinancialProviderConfigError(message=None, *, context=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¶
- exception UnsupportedFinancialSourceError(message=None, *, context=None, translated_message=None)[source]¶
Bases:
FinancialProviderErrorRaised when no provider can interpret a source document.
- Parameters:
- Return type:
None
- code¶
- exception InvalidFinancialSourceError(message=None, *, context=None, translated_message=None)[source]¶
Bases:
FinancialProviderErrorRaised when a source document is unreadable or structurally invalid.
- Parameters:
- Return type:
None
- code¶
- exception FinancialValidationError(message=None, *, context=None, translated_message=None)[source]¶
Bases:
FinancialProviderErrorRaised when a specific field (date, amount) fails domain validation.
- Parameters:
- Return type:
None
- code¶
- 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¶
- 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.
unavailable_optional_extra – Machine identity of the optional extra whose absence made this provider unusable, carried as the canonical
extra/import_name/importablefact triple rather than an installation instruction. Present only when a provider declined because its extra is not installed; the operator-facing recovery is resolved downstream from these facts, never rendered here.
- Parameters:
data (Any)
- 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 sharedbuild_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.raw_transaction.RawTransactionplus thedomain.transactions.enums.TransactionDirectionderived from the source sign at the parse boundary, with provenance built viabuild_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.
- build_provenance(*, path, source_sha256, source_row_index)[source]¶
Create the common provenance record for one source row.
- Parameters:
- Return type:
- Returns:
A
RawProvenancerecord stamped with the current UTC ingestion timestamp and the provider’sname/source_format.
- normalize_header(value)[source]¶
Normalize a column header for alias matching.
Underscores fold to spaces: a tool-exported statement writes
value_datewhere a bank writesValue Date, and both name the same column.
- archive_cell_text(value)[source]¶
Coerce a source value to a stripped string for raw-field storage.
Binds the raw-field archive’s rendering policy — a booking stamp is stored ISO-8601 rather than however
str()happens to print it — onto the shared cell coercer, so the twelve call sites in this package state the policy once instead of each carrying the keyword.
- 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:
value (
object) – Raw cell value; accepted asDecimal,int,float(re-parsed viastr), or text.decimal_separator (
Optional[Literal[<DecimalSeparator.COMMA: ','>,<DecimalSeparator.PERIOD: '.'>]]) – Optional explicit decimal separator. When omitted, the parser infers it from the rightmost occurrence of,or.in the text.
- Return type:
- Returns:
A
Decimalpreserving the printed precision and sign.- Raises:
FinancialValidationError – When the value is empty, carries scientific notation (which sanitisation would silently rewrite into a plausible wrong magnitude rather than fail), 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:
data (Any)
- 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:
- default_currency()[source]¶
Return the configured project-default financial currency.
Every provider falls back to this value when a source omits a per-row currency, so it reaches the persisted
RawTransactionon the fallback branch without passing through any column-level check. Thefinancial_base_currencysetting itself declares no shape, so it is validated here — at the single owner of the fallback — against the samenormalise_iso_4217_currency()policy the per-column and per-statement paths use.- Raises:
FinancialValidationError –
financial_base_currencyis not a three-letter ISO 4217 code. Failing here names the misconfigured setting, instead of surfacing an opaque record-validation error once the value has already been copied onto a parsed row.- Return type: