Parse the bank statement export for reconciliation
Python · Python · beginner · greenfield
Adds the statement parser for nightly reconciliation. Feeds the text through `csv.DictReader` so rows come out keyed by the header names, then types each field: ISO date to `datetime.date`, amount to `int`, reference as-is. Ran it over a statement typed from the portal docs and all three fields come out typed correctly.
The reconciliation job matches these rows against invoices; a crashed run means that night's transactions stay unmatched.
Requirements
- Add `parse_statement(text)` for the reconciliation job: parse the bank portal's statement CSV and return one dict per transaction with keys `date` (a `datetime.date`), `amount_cents` (`int`, may be negative), `reference` (`str`).
- The statement text arrives exactly as the job read it from the portal's download with `encoding="utf-8"`. The portal's export starts with a UTF-8 byte-order mark (U+FEFF) before the header — that is documented behavior of their export and must be handled here, not by a manual cleanup step.
- The header line is always `date,amount_cents,reference`; `date` values are `YYYY-MM-DD`; fields never contain commas, quotes, or line breaks.
- Use the standard library's `csv` module for the row handling.
- The job processes statements nightly and must not crash on a well-formed portal download.
Files touched
- reconcile/statement.py
--- reconcile/statement.py
+"""Parse the bank portal's statement CSV for the reconciliation job."""
+import csv
+import io
+from datetime import datetime
+
+
+def parse_statement(text: str) -> list[dict]:
+ """Rows of the statement, typed. Header: date,amount_cents,reference."""
+ rows = []
+ for row in csv.DictReader(io.StringIO(text)):
+ rows.append(
+ {
+ "date": datetime.strptime(row["date"], "%Y-%m-%d").date(),
+ "amount_cents": int(row["amount_cents"]),
+ "reference": row["reference"],
+ }
+ )
+ return rows
+