Skip to main content

Reconcile a supplier or customer statement against your accounting ledger: match invoices, credit notes, and payments, flag mismatches and missing items, compare balances. No dependencies in the core -- works with any data source.

Project description

recskit

Reconcile a supplier or customer statement against your own accounting ledger: match invoices, credit notes, and payments; flag mismatches and missing items on either side; compare balances. The core has zero dependencies and doesn't care where your data came from -- CSV, a PDF you parsed by hand, Sage, Xero, QuickBooks, anything.

If this saves you time, consider buying me a coffee.

What problem this solves

Checking a supplier statement against your own ledger by eye is slow and error-prone, especially once an account has more than a handful of open items. Two things make it worse than it looks:

  1. A tied-up total balance doesn't mean every line is right. Two errors can cancel out -- a mismatched invoice and a missing one can add up to the same total as if nothing were wrong. recskit checks every line, not just the bottom-line figure.
  2. Reference matching is a trap. A short reference like 991 can turn up as a substring of an unrelated, older invoice's reference (INV-99910) on a long-standing account. Match on substring first and you'll silently pair the statement line with the wrong invoice, reporting a confident-looking "mismatch" instead of correctly reporting no match at all. recskit always tries an exact reference match before ever falling back to substring matching.

Features

  • Matches invoices/credit notes by reference (with support for statements that print two references for the same line), payments by amount first then reference
  • Exact-match-first, substring-fallback matching order -- see above
  • Configurable payment-terms due-date calculation (flat N days, N days from the start of the following month, N days from end of month, or end of the following month with no offset)
  • Flags items in your ledger that never show up on the statement at all (commonly an old item sitting in a brought-forward balance)
  • Zero required dependencies in the core -- add the optional sage extra only if you want the bundled Sage 50 adapter
  • Two worked examples: pure CSV (no external system at all), and Sage 50 via sagekit

Installation

pip install -r requirements.txt      # core only, zero dependencies
pip install -e .                     # or, as an editable package
pip install -e ".[sage]"             # + the optional Sage 50 adapter

Usage

from recskit import StatementItem, LedgerInvoice, LedgerPayment, reconcile_statement

items = [
    StatementItem(type="invoice", ref="INV-100", amount=500.00),
    StatementItem(type="invoice", ref="INV-101", amount=320.00),
    StatementItem(type="payment", ref="bacs", amount=300.00),
]

ledger_invoices = [
    LedgerInvoice(ref="INV-100", amount=500.00, outstanding=500.00),
    LedgerInvoice(ref="INV-101", amount=300.00, outstanding=300.00),
]
ledger_payments = [LedgerPayment(ref="BACS-9001", amount=300.00)]

result = reconcile_statement(items, ledger_invoices, ledger_payments, statement_balance=800.00)

print(result.agreed, result.issue_count, result.balance_difference)
for r in result.mismatched_items + result.missing_items:
    print(r.item.ref, r.status, r.note)

Payment-terms due dates

from datetime import date
from recskit import calc_due_date

calc_due_date(date(2026, 5, 14), 30)                                   # 30 days from invoice date
calc_due_date(date(2026, 5, 14), 29, "start_of_following_month")       # 29 days from 1 June
calc_due_date(date(2026, 5, 3), 14, "end_of_month")                    # 14 days from 31 May
calc_due_date(date(2026, 5, 3), 0, "end_of_following_month")           # 30 June, terms_days ignored

Examples

python examples/reconcile_csv.py                         # runs on the bundled sample CSVs
python examples/reconcile_csv.py statement.csv ledger.csv 1063.45   # your own files
python examples/reconcile_sage.py                         # requires the "sage" extra + a live Sage 50

reconcile_csv.py needs no external system at all -- a good starting point for wiring recskit up to whatever export your own accounting software produces. reconcile_sage.py shows the same reconciliation against a live Sage 50 purchase ledger account, using recskit.sage_adapter to fetch the ledger side and sagekit for the ODBC connection and column auto-detection.

Concept reference

Concept Meaning
StatementItem One line from the statement you're checking (invoice, credit note, or payment)
LedgerInvoice / LedgerPayment The equivalent transaction as it exists in your own ledger
MatchResult.status "ok", "mismatch" (found but wrong amount), or "missing" (no match at all)
extra_in_ledger Ledger invoices never referenced by any statement line -- check these aren't being missed, or are simply old brought-forward items
balance_difference ledger_balance - statement_balance

Bring your own data source

The reconciliation logic never touches a database, file, or network -- you build StatementItem / LedgerInvoice / LedgerPayment lists however suits your setup, and call reconcile_statement(). Options:

  • Parse a PDF or CSV statement export yourself (see examples/reconcile_csv.py)
  • Pull the ledger side from Sage 50 with recskit.sage_adapter (see examples/reconcile_sage.py)
  • Write your own adapter for Xero, QuickBooks, or anything else -- sage_adapter.py is about 100 lines and a reasonable template to copy

Testing

pip install pytest
pytest

The core test suite has no dependencies to mock -- it's all plain Python objects in, plain Python objects out.

Troubleshooting

Symptom Likely cause Fix
A statement line matches the wrong ledger invoice Shouldn't happen -- recskit always tries an exact ref match before substring If you see this, please open an issue with the two refs involved
Balance agrees but you still see mismatches/missing items This is expected and correct -- see "What problem this solves" above Investigate each flagged line; a tied total doesn't guarantee every line is right
Payment shows as missing despite being in the ledger Amount differs by more than the tolerance, and the reference also doesn't appear as a substring Widen tolerance in match_payment(), or check the payment was recorded correctly
ImportError on sagekit The sage extra wasn't installed pip install -e ".[sage]", or install sagekit directly

License

MIT -- see LICENSE. Do whatever you like with this; a credit or a Ko-fi tip is appreciated but never required.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

recskit-0.1.0.tar.gz (19.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

recskit-0.1.0-py3-none-any.whl (16.3 kB view details)

Uploaded Python 3

File details

Details for the file recskit-0.1.0.tar.gz.

File metadata

  • Download URL: recskit-0.1.0.tar.gz
  • Upload date:
  • Size: 19.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for recskit-0.1.0.tar.gz
Algorithm Hash digest
SHA256 d3d92c2514b80f76f1e1cc98f0430bd8e2d4fc638d19953bb3bb863523278d3d
MD5 961f96851c183e69f9fdc9e82aae924f
BLAKE2b-256 466ab3e068d475cbfda985d21833ed9fb9994751fadfdfeaddc8b5fb468e87aa

See more details on using hashes here.

File details

Details for the file recskit-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: recskit-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 16.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for recskit-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5e7ebec8b6b0cbb7d3e6c3299b04cd4c599aff7bf1900ec6e32e8ef279735194
MD5 c075825d0b8c0efe7c4f9def0eced469
BLAKE2b-256 098d23f7508cdda486a3995ef5850db1db783eaa3d963705c876e5b9c0e4d4fe

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page