Metadata-Version: 2.4
Name: statementproof
Version: 0.1.0
Summary: Extract transactions from bank statement PDFs -- and prove the extraction is right, or say it isn't.
License: MIT
Keywords: bank-statement,pdf,csv,bookkeeping,accounting,reconciliation,extraction,converter,quickbooks,xero
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Topic :: Office/Business :: Financial :: Accounting
Classifier: Topic :: Text Processing :: Filters
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pdfplumber>=0.10
Provides-Extra: dev
Requires-Dist: reportlab>=4.0; extra == "dev"
Dynamic: license-file

# statementproof

Bank statement PDF → CSV extraction that **tells you when it got it wrong.**

Most converters hand you output with no way to know if it is correct. A bank
statement is one of the few documents carrying its own checksum — the balances.

## What it does differently

**1. Geometry, not regex.** The most-cited converter failure is *"columns shift,
debit and credit values land in the wrong places."* That happens because
`extract_text()` flattens a 2-D layout and discards x-position — the only signal that
distinguishes a debit column from a credit column. This reads word coordinates and
clusters money columns by their right edges.

**2. Column roles from arithmetic, not headers.** Which column is the running balance
is decided by behaviour, not by header text — header text differs across banks and is
often missing on continuation pages.

**3. Sign from the balance chain.** If the balance went down, it was a debit,
regardless of whether a minus glyph survived extraction.

**4. It refuses to bluff.** With nothing to check against, output is `UNVERIFIED` —
never a silent pass.

## Verdicts

| verdict | meaning |
|---|---|
| `VERIFIED` | transactions reproduce the statement's own balances |
| `FAILED` | they do not — **with the offending row named** |
| `UNVERIFIED` | statement carries no balances to check against |

## What it CANNOT catch — read this

The check is **arithmetic consistency**, not correctness. It cannot see errors that do
not disturb the arithmetic:

- junk rows with a `0.00` amount (page headers picked up as transactions)
- wrong dates
- garbled or truncated descriptions
- two errors that cancel out

**"Provably arithmetically consistent" is the honest claim. "Provably correct" is not,
and is not made here.**

## Free test mode — try it on your own statement

```bash
python -m statementproof YOUR_STATEMENT.pdf              # validate, print a report
python -m statementproof YOUR_STATEMENT.pdf --csv out.csv
python -m statementproof YOUR_STATEMENT.pdf --diagnostic # shareable layout report
```

**It runs entirely on your machine. Nothing is uploaded, nothing is stored, no file is
written unless you name one with `--csv`.**

That is not a policy, it is a property of the code, and it is tested:
`tests/test_privacy.py` parses every module's AST and **fails the build if any
networking library is imported anywhere in the package.**

### The `--diagnostic` flag, and why it exists

The single thing that would most improve this tool is a library of real statement
*layouts*. A layout can be described without describing anyone's money, so
`--diagnostic` prints exactly that: column positions, column density, date-token
**shapes** (`DD/DD`, not `10/02`), and where extraction broke.

It contains **no amounts, no balances, no descriptions, no dates, no account numbers,
no names, and not even the filename.** It prints to your screen so you can read the
whole thing before deciding whether to share it. The tool never sends it anywhere —
there is no code that could.

Those exclusions are asserted by tests against a known statement, not just intended.

## Status

Early, and scoped to **text-layer PDFs only** — statements downloaded from a bank
portal. Scans and photographs are not supported; the tool detects them and says so
rather than producing garbage.

**7/7 layouts extracted exactly, 0 false assurances.** Six are synthetic; the seventh
is reproduced from a real bank's published specimen and is the useful one — it found
three bugs the synthetic set never could, including `MM/DD` dates with no year, which
alone produced **0/21 extracted** while the synthetic suite still reported 6/6.

⚠️ **Passing a test suite written by the author of the code under test is worth very
little.** Six invented layouts passed while a real bank's date format extracted
nothing. **No real customer file has been processed yet** — which is what the free
test mode above is for.

    python statementproof/tests/make_statements.py     # build synthetic corpus
    python statementproof/tests/make_real_derived.py   # build real-bank-derived layout
    python statementproof/tests/score.py               # score extraction
    python statementproof/tests/test_failure_modes.py  # validator behaviour
    python statementproof/tests/test_privacy.py        # privacy promises
