Metadata-Version: 2.4
Name: statementproof
Version: 0.1.1
Summary: Extract transactions from bank statement PDFs -- and prove the extraction is right, or say it isn't.
License: MIT
Project-URL: Homepage, https://github.com/OrbitalKeyAi/statementproof
Project-URL: Documentation, https://orbitalkeyai.github.io/statementproof/
Project-URL: Source, https://github.com/OrbitalKeyAi/statementproof
Project-URL: Issues, https://github.com/OrbitalKeyAi/statementproof/issues
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

**Convert bank statement PDFs to CSV — and find out when the conversion is wrong.**

Most PDF-to-CSV converters hand you rows with no way to check them. The best of them
(monopoly) does check totals — but tells you only that *something* is wrong, somewhere. The expensive failure isn't a crash you notice — it's one debit read as a credit,
which reconciles to nothing and turns up weeks later inside a client's books.

A bank statement is one of the few documents that carries its own checksum: the balances.
statementproof uses them.

```
$ statementproof october.pdf

  opening balance : 69.96
  closing balance : 586.71
  transactions    : 21 extracted, 0 skipped

  VERIFIED  (21 transactions, checks: chain, aggregate)
```

And when a row is misread:

```
  FAILED  (21 transactions, checks: chain, aggregate)
    [chain_break] row 14: running balance does not follow:
    534.66 +37.07 should give 571.73, statement shows 497.59 (off by -74.14)
```

## Install

```bash
pip install statementproof
```

Python 3.9+. One dependency (`pdfplumber`).

## Usage

```bash
# check a statement and print the report
statementproof statement.pdf

# write the transactions to CSV
statementproof statement.pdf --csv transactions.csv

# layout report you can share — contains no financial data
statementproof statement.pdf --diagnostic
```

Exit code is `0` when verified, `1` otherwise, so it drops into a script.

## Three verdicts, and it will not bluff

| verdict | meaning |
|---|---|
| `VERIFIED` | the rows reproduce the statement's opening and closing balances, and each running balance follows from the last |
| `FAILED` | they don't — **with the row number, expected figure, and size of the gap** |
| `UNVERIFIED` | the statement carries no balances to check against. Output may be perfect; nothing proves it, so nothing is claimed |

A converter that returns a clean-looking CSV it cannot vouch for is the problem this
exists to solve, so `UNVERIFIED` is never dressed up as success.

## Your statement never leaves your machine

This tool asks you to point it at a document carrying your name, address, account
number, employer, and every merchant you used last month. "Nothing uploaded" can't be a
policy — it has to be a property of the code.

**No networking library is imported anywhere in this package**, and
[`tests/test_privacy.py`](statementproof/tests/test_privacy.py) parses every module's
AST and **fails the build if one ever is.** No file is written unless you name it.

The `--diagnostic` flag exists because a library of real statement *layouts* is what
would most improve this tool — and a layout can be described without describing anyone's
money. It reports column positions, column density, date-token **shapes** (`DD/DD`, not
`10/02`), and where extraction broke. It contains no amounts, balances, descriptions,
dates, account numbers, names, or even the filename. It prints to your screen so you can
read all of it before deciding whether to share it. The tool never sends it anywhere.

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

## How it works

**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 two-dimensional page and discards the x-position — the only signal separating a debit
column from a credit column. statementproof reads each number's coordinates and clusters
the money columns by their right edges.

**Column roles from arithmetic, not headers.** Which column holds the running balance is
decided by behaviour, because header wording differs by bank and often vanishes on
continuation pages.

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

## Limitations — read these first

**Text-layer PDFs only.** A statement downloaded from your bank's website normally has a
text layer; a scan or a phone photo does not. Given one, statementproof says so rather
than inventing rows. **OCR is not built yet.**

**Arithmetic consistency, not correctness.** It cannot see an error that leaves the
totals intact:

- a page header picked up as a row with a `0.00` amount
- a wrong date or a truncated description
- two errors that cancel out

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

**It has not met your bank.** Seven layouts extract exactly — six written by the author,
one reproduced from a real bank's published specimen. That seventh immediately found
three bugs the other six could not, including `MM/DD` dates with no year, which took
extraction from 21/21 to 0/21 while the author's own suite still reported 6/6 passing.
**Passing tests you wrote yourself is worth very little.**

If it fails on your statement, run `--diagnostic` and
[open an issue](https://github.com/OrbitalKeyAi/statementproof/issues) with that output.
It contains no financial data, and each new layout makes the parser better for everyone.

## Tests

```bash
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 vs ground truth
python statementproof/tests/test_failure_modes.py   # validator behaviour
python statementproof/tests/test_privacy.py         # privacy promises
```

Current: **7/7 layouts extracted exactly · 9/9 failure-mode tests · 8/8 privacy checks ·
0 false assurances.**

"False assurance" — reporting `VERIFIED` on a bad extraction — is the number that
matters. A validator that green-lights an error is worse than no validator.

## License

MIT.
