cashapply 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cashapply-0.1.0/LICENSE +21 -0
- cashapply-0.1.0/PKG-INFO +231 -0
- cashapply-0.1.0/README.md +200 -0
- cashapply-0.1.0/pyproject.toml +58 -0
- cashapply-0.1.0/setup.cfg +4 -0
- cashapply-0.1.0/src/cashapply/__init__.py +23 -0
- cashapply-0.1.0/src/cashapply/cli.py +166 -0
- cashapply-0.1.0/src/cashapply/engine.py +563 -0
- cashapply-0.1.0/src/cashapply/extract.py +188 -0
- cashapply-0.1.0/src/cashapply/io_csv.py +135 -0
- cashapply-0.1.0/src/cashapply/models.py +239 -0
- cashapply-0.1.0/src/cashapply/reconcile.py +329 -0
- cashapply-0.1.0/src/cashapply.egg-info/PKG-INFO +231 -0
- cashapply-0.1.0/src/cashapply.egg-info/SOURCES.txt +20 -0
- cashapply-0.1.0/src/cashapply.egg-info/dependency_links.txt +1 -0
- cashapply-0.1.0/src/cashapply.egg-info/entry_points.txt +2 -0
- cashapply-0.1.0/src/cashapply.egg-info/requires.txt +6 -0
- cashapply-0.1.0/src/cashapply.egg-info/top_level.txt +1 -0
- cashapply-0.1.0/tests/test_engine.py +114 -0
- cashapply-0.1.0/tests/test_extract.py +36 -0
- cashapply-0.1.0/tests/test_io_cli.py +76 -0
- cashapply-0.1.0/tests/test_reconcile.py +55 -0
cashapply-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SybilGambleyyu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
cashapply-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cashapply
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Deterministic cash application: match bank payments to open invoices with an auditable trail
|
|
5
|
+
Author: SybilGambleyyu
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/SybilGambleyyu/cashapply
|
|
8
|
+
Project-URL: Repository, https://github.com/SybilGambleyyu/cashapply
|
|
9
|
+
Project-URL: Issues, https://github.com/SybilGambleyyu/cashapply/issues
|
|
10
|
+
Keywords: accounting,accounts-receivable,cash-application,reconciliation,invoice-matching,remittance,open-item,bookkeeping
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Office/Business :: Financial :: Accounting
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: rapidfuzz>=3.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
28
|
+
Requires-Dist: build; extra == "dev"
|
|
29
|
+
Requires-Dist: twine; extra == "dev"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# cashapply
|
|
33
|
+
|
|
34
|
+
**Deterministic cash application for accounts receivable.**
|
|
35
|
+
|
|
36
|
+
Match bank payments and remittance lines to open invoices — with a confidence score and a plain-English reason for every match. No black-box model: every suggestion is auditable.
|
|
37
|
+
|
|
38
|
+
Also includes **bank ↔ book (GL) reconciliation** for classic statement matching.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install cashapply
|
|
42
|
+
|
|
43
|
+
cashapply apply -p payments.csv -i invoices.csv -o matches.csv
|
|
44
|
+
cashapply reconcile -b bank.csv -g book.csv -o pairs.csv
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Why this exists
|
|
48
|
+
|
|
49
|
+
AR clerks and controllers still burn hours every week on cash application:
|
|
50
|
+
|
|
51
|
+
1. A payment hits the bank for `$1,650.00`
|
|
52
|
+
2. The memo says something like `paying both open invoices` or `INV-1004`
|
|
53
|
+
3. Someone hunts through the open AR aging to find which invoice(s) it covers
|
|
54
|
+
|
|
55
|
+
Enterprise tools (HighRadius, BlackLine, Versapay, …) automate this — at enterprise prices. Lightweight open-source matching engines you can drop into a script, a notebook, or a small firm’s workflow are scarce.
|
|
56
|
+
|
|
57
|
+
**cashapply** is that engine:
|
|
58
|
+
|
|
59
|
+
| Need | What cashapply does |
|
|
60
|
+
|------|---------------------|
|
|
61
|
+
| Memo says `INV-1001` | Reference extraction → high-confidence apply |
|
|
62
|
+
| One payment covers two invoices | Subset-sum multi-invoice match |
|
|
63
|
+
| Partial payment on account | Partial apply with customer-name check |
|
|
64
|
+
| Bank line vs GL line | Two-sided ledger reconciliation |
|
|
65
|
+
| Auditor asks “why?” | Every match has `reasons[]` and a score |
|
|
66
|
+
|
|
67
|
+
It is **not** a full ERP, lockbox OCR product, or ML remittance reader. It takes clean structured inputs (CSV / Python objects) and returns scored matches you can post or review.
|
|
68
|
+
|
|
69
|
+
## Install
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install cashapply
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Requires Python 3.10+.
|
|
76
|
+
|
|
77
|
+
## Quick start (CLI)
|
|
78
|
+
|
|
79
|
+
### Cash application
|
|
80
|
+
|
|
81
|
+
**invoices.csv**
|
|
82
|
+
|
|
83
|
+
```csv
|
|
84
|
+
id,amount,customer,invoice_date,due_date,reference
|
|
85
|
+
INV-1001,1500.00,Acme Corporation,2024-05-01,2024-05-31,PO-7781
|
|
86
|
+
INV-1002,275.50,Acme Corporation,2024-05-15,2024-06-14,
|
|
87
|
+
INV-1004,1200.00,Initech LLC,2024-06-10,2024-07-10,
|
|
88
|
+
INV-1005,450.00,Initech LLC,2024-06-12,2024-07-12,
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**payments.csv**
|
|
92
|
+
|
|
93
|
+
```csv
|
|
94
|
+
id,amount,date,memo,payer
|
|
95
|
+
P-01,1500.00,2024-05-28,ACH BILL PAY INV-1001,Acme Corporation
|
|
96
|
+
P-04,1650.00,2024-07-15,paying both open invoices,Initech LLC
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
cashapply apply -p payments.csv -i invoices.csv -o matches.csv --json result.json
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Example summary:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
=== Cash application summary ===
|
|
107
|
+
Payments: 2
|
|
108
|
+
Invoices: 4
|
|
109
|
+
Matches: 2 (auto-apply ≥0.85: 2)
|
|
110
|
+
...
|
|
111
|
+
[AUTO] pay P-01 → INV-1001 $1500.00 conf=0.97 (reference_full)
|
|
112
|
+
• memo reference 'INV-1001' (known_id) → invoice INV-1001
|
|
113
|
+
[AUTO] pay P-04 → INV-1004, INV-1005 $1650.00 conf=0.93 (multi_invoice)
|
|
114
|
+
• multi-invoice: 1650.00 = sum of ['INV-1004', 'INV-1005'] (...)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Bank ↔ book reconciliation
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
cashapply reconcile -b bank.csv -g gl.csv -o pairs.csv
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Python API
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
from decimal import Decimal
|
|
127
|
+
from cashapply import Invoice, Payment, MatchConfig, match_payments
|
|
128
|
+
|
|
129
|
+
invoices = [
|
|
130
|
+
Invoice(id="INV-1001", amount=Decimal("1500.00"), customer="Acme Corporation"),
|
|
131
|
+
Invoice(id="INV-1002", amount=Decimal("275.50"), customer="Acme Corporation"),
|
|
132
|
+
]
|
|
133
|
+
payments = [
|
|
134
|
+
Payment(id="P-01", amount=Decimal("1500.00"), memo="ACH INV-1001", payer="Acme Corp"),
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
result = match_payments(payments, invoices)
|
|
138
|
+
|
|
139
|
+
for m in result.high_confidence(0.85):
|
|
140
|
+
print(m.payment_id, "→", m.invoice_ids, m.confidence, m.reasons)
|
|
141
|
+
|
|
142
|
+
# Full audit payload
|
|
143
|
+
print(result.to_dict())
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Bank / GL reconciliation
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from cashapply.reconcile import LedgerLine, reconcile_ledgers
|
|
150
|
+
from datetime import date
|
|
151
|
+
from decimal import Decimal
|
|
152
|
+
|
|
153
|
+
bank = [LedgerLine("b1", Decimal("100"), date(2024, 5, 1), "ACME DEP")]
|
|
154
|
+
book = [LedgerLine("g1", Decimal("100"), date(2024, 5, 1), "Acme deposit")]
|
|
155
|
+
result = reconcile_ledgers(bank, book)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## How matching works
|
|
159
|
+
|
|
160
|
+
Matches are tried in order; each invoice is claimed at most once per run:
|
|
161
|
+
|
|
162
|
+
1. **Reference** — invoice numbers / PO / refs extracted from the payment memo (and exact known-id scan)
|
|
163
|
+
2. **Exact amount** — open balance equals payment; boosted by customer-name and date proximity; penalized when ambiguous
|
|
164
|
+
3. **Multi-invoice** — payment equals the sum of a small set of open invoices (same customer preferred)
|
|
165
|
+
4. **Partial** — payment is less than one larger invoice, only with reasonable customer-name agreement
|
|
166
|
+
5. **Name + amount band** — strong name match with short-pay / near amount
|
|
167
|
+
|
|
168
|
+
Confidence is a number in `[0, 1]`. Default rule of thumb:
|
|
169
|
+
|
|
170
|
+
- **≥ 0.85** — safe to auto-post (still keep the reason trail)
|
|
171
|
+
- **0.40–0.85** — queue for human review
|
|
172
|
+
- **< 0.40** — not reported (stays unmatched)
|
|
173
|
+
|
|
174
|
+
Tune with `MatchConfig`:
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
cfg = MatchConfig(
|
|
178
|
+
amount_tolerance=Decimal("0.05"),
|
|
179
|
+
date_window_days=60,
|
|
180
|
+
allow_partial=True,
|
|
181
|
+
max_invoices_per_payment=5,
|
|
182
|
+
auto_apply_threshold=0.90,
|
|
183
|
+
min_report_confidence=0.50,
|
|
184
|
+
)
|
|
185
|
+
result = match_payments(payments, invoices, cfg)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## CSV columns
|
|
189
|
+
|
|
190
|
+
Flexible headers — any of these work:
|
|
191
|
+
|
|
192
|
+
| Role | Accepted column names |
|
|
193
|
+
|------|------------------------|
|
|
194
|
+
| Invoice id | `id`, `invoice_id`, `invoice`, `invoice_number`, `number` |
|
|
195
|
+
| Amount | `amount`, `open_amount`, `balance`, `total` |
|
|
196
|
+
| Customer | `customer`, `customer_name`, `name`, `client` |
|
|
197
|
+
| Payment id | `id`, `payment_id`, `txn_id`, `transaction_id` |
|
|
198
|
+
| Memo | `memo`, `description`, `narrative`, `reference`, `remittance` |
|
|
199
|
+
| Payer | `payer`, `customer`, `name`, `payor` |
|
|
200
|
+
|
|
201
|
+
## Examples
|
|
202
|
+
|
|
203
|
+
This repo ships runnable samples:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
git clone https://github.com/SybilGambleyyu/cashapply
|
|
207
|
+
cd cashapply
|
|
208
|
+
pip install -e .
|
|
209
|
+
cashapply apply -p examples/payments.csv -i examples/invoices.csv
|
|
210
|
+
cashapply reconcile -b examples/bank.csv -g examples/book.csv
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## What this is not
|
|
214
|
+
|
|
215
|
+
- Not OCR for PDF lockbox images (feed it structured data)
|
|
216
|
+
- Not a full AR suite (credit memos, deductions workflows, ERP posting adapters — bring your own)
|
|
217
|
+
- Not a neural net: accuracy comes from rules, fuzzy names, and combinatorial matching you can explain
|
|
218
|
+
|
|
219
|
+
If you need PDF remittance capture, pair cashapply with your existing extraction step and pass the resulting tables in.
|
|
220
|
+
|
|
221
|
+
## Development
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
python -m venv .venv && source .venv/bin/activate
|
|
225
|
+
pip install -e ".[dev]"
|
|
226
|
+
pytest -q
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## License
|
|
230
|
+
|
|
231
|
+
MIT — use it in internal tools, products, and client work.
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# cashapply
|
|
2
|
+
|
|
3
|
+
**Deterministic cash application for accounts receivable.**
|
|
4
|
+
|
|
5
|
+
Match bank payments and remittance lines to open invoices — with a confidence score and a plain-English reason for every match. No black-box model: every suggestion is auditable.
|
|
6
|
+
|
|
7
|
+
Also includes **bank ↔ book (GL) reconciliation** for classic statement matching.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install cashapply
|
|
11
|
+
|
|
12
|
+
cashapply apply -p payments.csv -i invoices.csv -o matches.csv
|
|
13
|
+
cashapply reconcile -b bank.csv -g book.csv -o pairs.csv
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Why this exists
|
|
17
|
+
|
|
18
|
+
AR clerks and controllers still burn hours every week on cash application:
|
|
19
|
+
|
|
20
|
+
1. A payment hits the bank for `$1,650.00`
|
|
21
|
+
2. The memo says something like `paying both open invoices` or `INV-1004`
|
|
22
|
+
3. Someone hunts through the open AR aging to find which invoice(s) it covers
|
|
23
|
+
|
|
24
|
+
Enterprise tools (HighRadius, BlackLine, Versapay, …) automate this — at enterprise prices. Lightweight open-source matching engines you can drop into a script, a notebook, or a small firm’s workflow are scarce.
|
|
25
|
+
|
|
26
|
+
**cashapply** is that engine:
|
|
27
|
+
|
|
28
|
+
| Need | What cashapply does |
|
|
29
|
+
|------|---------------------|
|
|
30
|
+
| Memo says `INV-1001` | Reference extraction → high-confidence apply |
|
|
31
|
+
| One payment covers two invoices | Subset-sum multi-invoice match |
|
|
32
|
+
| Partial payment on account | Partial apply with customer-name check |
|
|
33
|
+
| Bank line vs GL line | Two-sided ledger reconciliation |
|
|
34
|
+
| Auditor asks “why?” | Every match has `reasons[]` and a score |
|
|
35
|
+
|
|
36
|
+
It is **not** a full ERP, lockbox OCR product, or ML remittance reader. It takes clean structured inputs (CSV / Python objects) and returns scored matches you can post or review.
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install cashapply
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Requires Python 3.10+.
|
|
45
|
+
|
|
46
|
+
## Quick start (CLI)
|
|
47
|
+
|
|
48
|
+
### Cash application
|
|
49
|
+
|
|
50
|
+
**invoices.csv**
|
|
51
|
+
|
|
52
|
+
```csv
|
|
53
|
+
id,amount,customer,invoice_date,due_date,reference
|
|
54
|
+
INV-1001,1500.00,Acme Corporation,2024-05-01,2024-05-31,PO-7781
|
|
55
|
+
INV-1002,275.50,Acme Corporation,2024-05-15,2024-06-14,
|
|
56
|
+
INV-1004,1200.00,Initech LLC,2024-06-10,2024-07-10,
|
|
57
|
+
INV-1005,450.00,Initech LLC,2024-06-12,2024-07-12,
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**payments.csv**
|
|
61
|
+
|
|
62
|
+
```csv
|
|
63
|
+
id,amount,date,memo,payer
|
|
64
|
+
P-01,1500.00,2024-05-28,ACH BILL PAY INV-1001,Acme Corporation
|
|
65
|
+
P-04,1650.00,2024-07-15,paying both open invoices,Initech LLC
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
cashapply apply -p payments.csv -i invoices.csv -o matches.csv --json result.json
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Example summary:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
=== Cash application summary ===
|
|
76
|
+
Payments: 2
|
|
77
|
+
Invoices: 4
|
|
78
|
+
Matches: 2 (auto-apply ≥0.85: 2)
|
|
79
|
+
...
|
|
80
|
+
[AUTO] pay P-01 → INV-1001 $1500.00 conf=0.97 (reference_full)
|
|
81
|
+
• memo reference 'INV-1001' (known_id) → invoice INV-1001
|
|
82
|
+
[AUTO] pay P-04 → INV-1004, INV-1005 $1650.00 conf=0.93 (multi_invoice)
|
|
83
|
+
• multi-invoice: 1650.00 = sum of ['INV-1004', 'INV-1005'] (...)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Bank ↔ book reconciliation
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
cashapply reconcile -b bank.csv -g gl.csv -o pairs.csv
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Python API
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from decimal import Decimal
|
|
96
|
+
from cashapply import Invoice, Payment, MatchConfig, match_payments
|
|
97
|
+
|
|
98
|
+
invoices = [
|
|
99
|
+
Invoice(id="INV-1001", amount=Decimal("1500.00"), customer="Acme Corporation"),
|
|
100
|
+
Invoice(id="INV-1002", amount=Decimal("275.50"), customer="Acme Corporation"),
|
|
101
|
+
]
|
|
102
|
+
payments = [
|
|
103
|
+
Payment(id="P-01", amount=Decimal("1500.00"), memo="ACH INV-1001", payer="Acme Corp"),
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
result = match_payments(payments, invoices)
|
|
107
|
+
|
|
108
|
+
for m in result.high_confidence(0.85):
|
|
109
|
+
print(m.payment_id, "→", m.invoice_ids, m.confidence, m.reasons)
|
|
110
|
+
|
|
111
|
+
# Full audit payload
|
|
112
|
+
print(result.to_dict())
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Bank / GL reconciliation
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
from cashapply.reconcile import LedgerLine, reconcile_ledgers
|
|
119
|
+
from datetime import date
|
|
120
|
+
from decimal import Decimal
|
|
121
|
+
|
|
122
|
+
bank = [LedgerLine("b1", Decimal("100"), date(2024, 5, 1), "ACME DEP")]
|
|
123
|
+
book = [LedgerLine("g1", Decimal("100"), date(2024, 5, 1), "Acme deposit")]
|
|
124
|
+
result = reconcile_ledgers(bank, book)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## How matching works
|
|
128
|
+
|
|
129
|
+
Matches are tried in order; each invoice is claimed at most once per run:
|
|
130
|
+
|
|
131
|
+
1. **Reference** — invoice numbers / PO / refs extracted from the payment memo (and exact known-id scan)
|
|
132
|
+
2. **Exact amount** — open balance equals payment; boosted by customer-name and date proximity; penalized when ambiguous
|
|
133
|
+
3. **Multi-invoice** — payment equals the sum of a small set of open invoices (same customer preferred)
|
|
134
|
+
4. **Partial** — payment is less than one larger invoice, only with reasonable customer-name agreement
|
|
135
|
+
5. **Name + amount band** — strong name match with short-pay / near amount
|
|
136
|
+
|
|
137
|
+
Confidence is a number in `[0, 1]`. Default rule of thumb:
|
|
138
|
+
|
|
139
|
+
- **≥ 0.85** — safe to auto-post (still keep the reason trail)
|
|
140
|
+
- **0.40–0.85** — queue for human review
|
|
141
|
+
- **< 0.40** — not reported (stays unmatched)
|
|
142
|
+
|
|
143
|
+
Tune with `MatchConfig`:
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
cfg = MatchConfig(
|
|
147
|
+
amount_tolerance=Decimal("0.05"),
|
|
148
|
+
date_window_days=60,
|
|
149
|
+
allow_partial=True,
|
|
150
|
+
max_invoices_per_payment=5,
|
|
151
|
+
auto_apply_threshold=0.90,
|
|
152
|
+
min_report_confidence=0.50,
|
|
153
|
+
)
|
|
154
|
+
result = match_payments(payments, invoices, cfg)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## CSV columns
|
|
158
|
+
|
|
159
|
+
Flexible headers — any of these work:
|
|
160
|
+
|
|
161
|
+
| Role | Accepted column names |
|
|
162
|
+
|------|------------------------|
|
|
163
|
+
| Invoice id | `id`, `invoice_id`, `invoice`, `invoice_number`, `number` |
|
|
164
|
+
| Amount | `amount`, `open_amount`, `balance`, `total` |
|
|
165
|
+
| Customer | `customer`, `customer_name`, `name`, `client` |
|
|
166
|
+
| Payment id | `id`, `payment_id`, `txn_id`, `transaction_id` |
|
|
167
|
+
| Memo | `memo`, `description`, `narrative`, `reference`, `remittance` |
|
|
168
|
+
| Payer | `payer`, `customer`, `name`, `payor` |
|
|
169
|
+
|
|
170
|
+
## Examples
|
|
171
|
+
|
|
172
|
+
This repo ships runnable samples:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
git clone https://github.com/SybilGambleyyu/cashapply
|
|
176
|
+
cd cashapply
|
|
177
|
+
pip install -e .
|
|
178
|
+
cashapply apply -p examples/payments.csv -i examples/invoices.csv
|
|
179
|
+
cashapply reconcile -b examples/bank.csv -g examples/book.csv
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## What this is not
|
|
183
|
+
|
|
184
|
+
- Not OCR for PDF lockbox images (feed it structured data)
|
|
185
|
+
- Not a full AR suite (credit memos, deductions workflows, ERP posting adapters — bring your own)
|
|
186
|
+
- Not a neural net: accuracy comes from rules, fuzzy names, and combinatorial matching you can explain
|
|
187
|
+
|
|
188
|
+
If you need PDF remittance capture, pair cashapply with your existing extraction step and pass the resulting tables in.
|
|
189
|
+
|
|
190
|
+
## Development
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
python -m venv .venv && source .venv/bin/activate
|
|
194
|
+
pip install -e ".[dev]"
|
|
195
|
+
pytest -q
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
MIT — use it in internal tools, products, and client work.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cashapply"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Deterministic cash application: match bank payments to open invoices with an auditable trail"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "SybilGambleyyu" },
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"accounting",
|
|
17
|
+
"accounts-receivable",
|
|
18
|
+
"cash-application",
|
|
19
|
+
"reconciliation",
|
|
20
|
+
"invoice-matching",
|
|
21
|
+
"remittance",
|
|
22
|
+
"open-item",
|
|
23
|
+
"bookkeeping",
|
|
24
|
+
]
|
|
25
|
+
classifiers = [
|
|
26
|
+
"Development Status :: 4 - Beta",
|
|
27
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
28
|
+
"Intended Audience :: Developers",
|
|
29
|
+
"License :: OSI Approved :: MIT License",
|
|
30
|
+
"Programming Language :: Python :: 3",
|
|
31
|
+
"Programming Language :: Python :: 3.10",
|
|
32
|
+
"Programming Language :: Python :: 3.11",
|
|
33
|
+
"Programming Language :: Python :: 3.12",
|
|
34
|
+
"Programming Language :: Python :: 3.13",
|
|
35
|
+
"Topic :: Office/Business :: Financial :: Accounting",
|
|
36
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
37
|
+
]
|
|
38
|
+
dependencies = [
|
|
39
|
+
"rapidfuzz>=3.0",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[project.optional-dependencies]
|
|
43
|
+
dev = ["pytest>=7.0", "build", "twine"]
|
|
44
|
+
|
|
45
|
+
[project.scripts]
|
|
46
|
+
cashapply = "cashapply.cli:main"
|
|
47
|
+
|
|
48
|
+
[project.urls]
|
|
49
|
+
Homepage = "https://github.com/SybilGambleyyu/cashapply"
|
|
50
|
+
Repository = "https://github.com/SybilGambleyyu/cashapply"
|
|
51
|
+
Issues = "https://github.com/SybilGambleyyu/cashapply/issues"
|
|
52
|
+
|
|
53
|
+
[tool.setuptools.packages.find]
|
|
54
|
+
where = ["src"]
|
|
55
|
+
|
|
56
|
+
[tool.pytest.ini_options]
|
|
57
|
+
testpaths = ["tests"]
|
|
58
|
+
pythonpath = ["src"]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
cashapply — deterministic cash application and open-item matching.
|
|
3
|
+
|
|
4
|
+
Match bank payments (or remittance lines) to open invoices with scored,
|
|
5
|
+
auditable reasons. No ML black boxes: every match explains itself.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from cashapply.engine import MatchConfig, match_payments
|
|
9
|
+
from cashapply.models import Invoice, Match, MatchResult, Payment
|
|
10
|
+
from cashapply.reconcile import ReconcileConfig, reconcile_ledgers
|
|
11
|
+
|
|
12
|
+
__version__ = "0.1.0"
|
|
13
|
+
__all__ = [
|
|
14
|
+
"Invoice",
|
|
15
|
+
"Payment",
|
|
16
|
+
"Match",
|
|
17
|
+
"MatchResult",
|
|
18
|
+
"MatchConfig",
|
|
19
|
+
"match_payments",
|
|
20
|
+
"ReconcileConfig",
|
|
21
|
+
"reconcile_ledgers",
|
|
22
|
+
"__version__",
|
|
23
|
+
]
|