cadrumo.adapters.inbound.financial.providers._mapped_tabular module

Statement ingest for tabular exports of no recognised fixed layout.

MappedTabularProvider is the last provider detection tries. Every exact fixed-layout parser — the bank CSV layouts, the spreadsheet, OFX, the N26 statement PDF — is offered the file first, and this lane sees it only when none of them claimed it. That ordering is load-bearing: a known bank export must keep taking its exact parser, because an exact layout match is a deterministic read of a known structure, while this lane’s read depends on a column-role mapping decided per file. Letting the fallback shadow an exact provider would silently turn the deterministic parse into an inferred one.

The lane itself is deterministic end to end. core.tabular resolves the file’s shape, a mapping supplies one FieldRole per column, and _tabular_projection copies each cell into its role byte-for-byte. Only the mapping is a judgement, and it is made once per file over a closed allow-list of roles, never over a cell value.

A file is never refused for a row or a column the product does not understand: an unmapped column is reported, and a row that will not parse is reported and skipped while every other row still imports.

See also

default_tabular_mapping_resolver()

The single wiring point through which detection obtains a mapping.

MAPPED_TABULAR_EXTENSIONS: frozenset[str]

Extensions this lane will consider. A delimited text export can arrive under any of them; content normalization decides whether it is really tabular.

REQUIRED_STATEMENT_ROLES: frozenset[FieldRole]

Roles a bank-movement row cannot do without. FieldRole is shared with the invoice-book lane and carries no statement-specific date or movement amount member, so a statement’s booked date reads under INVOICE_DATE and its amount under GRAND_TOTAL.

TabularMappingResolver

Supplies one role per column for a normalized table, or None when it cannot.

alias of Callable[[NormalizedTable], ColumnRoleMapping | None]

default_tabular_mapping_resolver()[source]

Return the mapping resolver detection uses, or None when none is installed.

The single wiring point between this lane and whatever establishes column roles. It resolves to the semantic column-role mapper, which decides one role per column from the file’s header vocabulary alone, once per file. Every cell is still copied by _tabular_projection under that mapping, so nothing about how values reach a row changed when this stopped returning None.

Return type:

Callable[[NormalizedTable], ColumnRoleMapping | None] | None

class MappedTabularProvider(mapping_resolver=None)[source]

Bases: FinancialProvider

Ingest bank movements from a tabular export under a column-role mapping.

Variables:

mapping_resolver – Supplies the per-file column-role mapping. Defaults to default_tabular_mapping_resolver(), so production wiring has one home; tests and callers holding their own mapping pass it directly.

Parameters:

mapping_resolver (TabularMappingResolver | None)

name: ClassVar[str]
supported_extensions: ClassVar[frozenset[str]]
source_format: ClassVar[SourceFormat]
verification_source: ClassVar[CorpusVerificationSource]
provisional_pending_specimen: ClassVar[bool]
validate_source(path)[source]

Report whether the source normalizes and maps, and what was not understood.

Validation is a full dry projection: every column the mapping did not establish and every row that will not parse is reported here, so the operator sees the whole picture before ingest rather than one row at a time. The source stays valid as long as at least one row parses.

Return type:

ProviderValidation

Parameters:

path (Path)

ingest(path)[source]

Yield one ParsedLedgerRow per importable row of the mapped table.

A row that will not parse is logged and skipped; the rest still import.

Raises:

InvalidFinancialSourceError – The source does not normalize, or no column-role mapping could be established for it.

Return type:

Iterator[ParsedLedgerRow]

Parameters:

path (Path)