@fuzzle/opencode-accountant 0.4.6 → 0.5.0-next.1
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.
- package/README.md +5 -9
- package/agent/accountant.md +27 -87
- package/dist/index.js +588 -990
- package/docs/architecture/import-context.md +674 -0
- package/docs/tools/classify-statements.md +84 -7
- package/docs/tools/import-pipeline.md +611 -0
- package/docs/tools/import-statements.md +43 -5
- package/docs/tools/reconcile-statement.md +529 -0
- package/package.json +3 -4
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
# reconcile-statement Tool
|
|
2
|
+
|
|
3
|
+
The `reconcile-statement` tool validates that imported bank statement transactions result in the correct closing balance. It compares the expected balance (from CSV metadata or manual input) against the actual balance calculated by hledger.
|
|
4
|
+
|
|
5
|
+
This tool is **restricted to the accountant agent only**.
|
|
6
|
+
|
|
7
|
+
**Important**: This tool requires a `contextId` from a prior `classify-statements` and `import-statements` run. The context provides the account number needed to find the correct CSV file, especially when multiple accounts exist for the same provider/currency. See [Import Context Architecture](../architecture/import-context.md) for details.
|
|
8
|
+
|
|
9
|
+
## Arguments
|
|
10
|
+
|
|
11
|
+
| Argument | Type | Required | Default | Description |
|
|
12
|
+
| ---------------- | ------ | -------- | ------- | -------------------------------------------------------------------- |
|
|
13
|
+
| `contextId` | string | Yes | - | Context ID from `classify-statements` (e.g., UUID) |
|
|
14
|
+
| `closingBalance` | string | No | - | Manual closing balance override (e.g., `"CHF 2324.79"`) |
|
|
15
|
+
| `account` | string | No | - | Manual hledger account override (e.g., `"assets:bank:ubs:checking"`) |
|
|
16
|
+
|
|
17
|
+
### Context-Based Operation
|
|
18
|
+
|
|
19
|
+
The tool operates exclusively via import contexts:
|
|
20
|
+
|
|
21
|
+
1. Loads context from `.memory/{contextId}.json`
|
|
22
|
+
2. Uses context's `accountNumber` to find the correct CSV file
|
|
23
|
+
3. Uses context's `closingBalance` (unless manually overridden)
|
|
24
|
+
4. Uses context's `filePath` to locate the imported CSV
|
|
25
|
+
5. Updates context with reconciliation results
|
|
26
|
+
|
|
27
|
+
**No fallback behavior**: If `contextId` is not provided or invalid, the tool will fail immediately. This ensures correct CSV file selection in multi-account scenarios.
|
|
28
|
+
|
|
29
|
+
## Output Format
|
|
30
|
+
|
|
31
|
+
### Success - Balance Reconciled
|
|
32
|
+
|
|
33
|
+
When the actual balance matches the expected balance:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"success": true,
|
|
38
|
+
"account": "assets:bank:ubs:checking",
|
|
39
|
+
"expectedBalance": "CHF 5432.10",
|
|
40
|
+
"actualBalance": "CHF 5432.10",
|
|
41
|
+
"lastTransactionDate": "2026-02-28",
|
|
42
|
+
"csvFile": "import/done/ubs/chf/ubs-0235-90250546.0-transactions-2026-02-01-to-2026-02-28.csv",
|
|
43
|
+
"metadata": {
|
|
44
|
+
"from-date": "2026-02-01",
|
|
45
|
+
"until-date": "2026-02-28",
|
|
46
|
+
"opening-balance": "4123.45",
|
|
47
|
+
"closing-balance": "5432.10",
|
|
48
|
+
"currency": "CHF",
|
|
49
|
+
"account-number": "0235-90250546.0"
|
|
50
|
+
},
|
|
51
|
+
"note": "✓ Balance reconciled successfully"
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Failure - Balance Mismatch
|
|
56
|
+
|
|
57
|
+
When the actual balance doesn't match the expected balance:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"success": false,
|
|
62
|
+
"account": "assets:bank:ubs:savings",
|
|
63
|
+
"expectedBalance": "CHF 12500.00",
|
|
64
|
+
"actualBalance": "CHF 12490.00",
|
|
65
|
+
"difference": "CHF -10.00",
|
|
66
|
+
"lastTransactionDate": "2026-02-24",
|
|
67
|
+
"csvFile": "import/done/ubs/chf/ubs-0235-90250546.1-transactions-2026-02-22-to-2026-02-24.csv",
|
|
68
|
+
"error": "Balance mismatch: expected CHF 12500.00, got CHF 12490.00 (difference: CHF -10.00)",
|
|
69
|
+
"hint": "Check for missing transactions or incorrect rules. Review transactions around 2026-02-24."
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Error - Context Not Found
|
|
74
|
+
|
|
75
|
+
When the context ID is invalid or context file doesn't exist:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"success": false,
|
|
80
|
+
"error": "Failed to load import context: abc123-...",
|
|
81
|
+
"hint": "Ensure the context ID is valid and the context file exists in .memory/"
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Error - CSV File Not Found
|
|
86
|
+
|
|
87
|
+
When the CSV file referenced by the context doesn't exist:
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"success": false,
|
|
92
|
+
"error": "CSV file not found: import/done/ubs/chf/transactions.csv",
|
|
93
|
+
"hint": "The file may have been moved or deleted. Context ID: abc123-..."
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Error - No Closing Balance
|
|
98
|
+
|
|
99
|
+
When closing balance cannot be determined from context, CSV metadata, or manual override:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"success": false,
|
|
104
|
+
"csvFile": "import/done/revolut/eur/account-statement_2026-02.csv",
|
|
105
|
+
"error": "No closing balance found in CSV metadata or data",
|
|
106
|
+
"hint": "Provide closingBalance parameter manually. Example retry: reconcile-statement --contextId abc123-... --closingBalance \"EUR 1234.56\"",
|
|
107
|
+
"metadata": {
|
|
108
|
+
"from-date": "2026-02-01",
|
|
109
|
+
"until-date": "2026-02-28",
|
|
110
|
+
"currency": "EUR"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Error - Account Not Determined
|
|
116
|
+
|
|
117
|
+
When hledger account cannot be determined from rules file or manual override:
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"success": false,
|
|
122
|
+
"csvFile": "import/done/ubs/chf/transactions.csv",
|
|
123
|
+
"error": "Could not determine account from rules file",
|
|
124
|
+
"hint": "Add 'account1 assets:bank:...' to ledger/rules/ubs-....rules or retry with: reconcile-statement --contextId abc123-... --account \"assets:bank:...\""
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## How It Works
|
|
129
|
+
|
|
130
|
+
The reconciliation process follows these steps:
|
|
131
|
+
|
|
132
|
+
### Step 1: Load Import Context
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
Load .memory/{contextId}.json
|
|
136
|
+
↓
|
|
137
|
+
Extract:
|
|
138
|
+
- accountNumber (e.g., "0235-90250546.1")
|
|
139
|
+
- closingBalance (e.g., "9001.55")
|
|
140
|
+
- filePath (e.g., "import/done/ubs/chf/...")
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Step 2: Find CSV File
|
|
144
|
+
|
|
145
|
+
The tool uses the `filePath` from the import context to locate the CSV:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
const csvFile = path.join(directory, importContext.filePath);
|
|
149
|
+
// Verify file exists, return error if not found
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Critical**: The file path comes from the context, which was updated when `import-statements` moved the file from `pending/` to `done/`.
|
|
153
|
+
|
|
154
|
+
### Step 3: Determine Closing Balance
|
|
155
|
+
|
|
156
|
+
Priority order:
|
|
157
|
+
|
|
158
|
+
1. **Manual override** (`--closingBalance` parameter)
|
|
159
|
+
2. **Context value** (`context.closingBalance`)
|
|
160
|
+
3. **CSV metadata** (from CSV header, e.g., UBS files)
|
|
161
|
+
4. **CSV data analysis** (inspect last transaction balance)
|
|
162
|
+
|
|
163
|
+
If none available → ERROR (requires manual input)
|
|
164
|
+
|
|
165
|
+
### Step 4: Determine Account
|
|
166
|
+
|
|
167
|
+
Priority order:
|
|
168
|
+
|
|
169
|
+
1. **Manual override** (`--account` parameter)
|
|
170
|
+
2. **Rules file** (`account1` directive in matching `.rules` file)
|
|
171
|
+
|
|
172
|
+
If none available → ERROR (requires manual input or rules file update)
|
|
173
|
+
|
|
174
|
+
### Step 5: Query hledger Balance
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
Run: hledger balance --end {lastTransactionDate + 1 day} {account}
|
|
178
|
+
↓
|
|
179
|
+
Parse output to get actual balance
|
|
180
|
+
↓
|
|
181
|
+
Format as "CHF 5432.10"
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Step 6: Compare Balances
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
expectedBalance (from CSV) vs actualBalance (from hledger)
|
|
188
|
+
↓
|
|
189
|
+
Match? → SUCCESS
|
|
190
|
+
↓
|
|
191
|
+
Mismatch? → FAILURE (with difference calculated)
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Step 7: Update Context
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
Update .memory/{contextId}.json with:
|
|
198
|
+
- reconciledAccount
|
|
199
|
+
- actualBalance
|
|
200
|
+
- lastTransactionDate
|
|
201
|
+
- reconciled: true/false
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Balance Sources
|
|
205
|
+
|
|
206
|
+
### Automatic Balance Detection
|
|
207
|
+
|
|
208
|
+
**UBS**: Closing balance extracted from CSV header metadata (row with "Closing balance:")
|
|
209
|
+
|
|
210
|
+
```csv
|
|
211
|
+
...metadata rows...
|
|
212
|
+
Closing balance:;9001.55;CHF
|
|
213
|
+
...
|
|
214
|
+
Trade date;Trade time;...
|
|
215
|
+
2026-02-22;09:30:00;...
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**Revolut**: No balance in CSV metadata → requires manual input
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
reconcile-statement --contextId abc123-... --closingBalance "EUR 1234.56"
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Balance Normalization
|
|
225
|
+
|
|
226
|
+
The tool normalizes balances to format: `{CURRENCY} {amount}`
|
|
227
|
+
|
|
228
|
+
Examples:
|
|
229
|
+
|
|
230
|
+
- Input: `"CHF 2324.79"` → Used as-is
|
|
231
|
+
- Input: `"2324.79"` (from CSV) + currency from context → `"CHF 2324.79"`
|
|
232
|
+
- Input: `"2,324.79 CHF"` → Parsed and normalized to `"CHF 2324.79"`
|
|
233
|
+
|
|
234
|
+
## Account Detection
|
|
235
|
+
|
|
236
|
+
### Automatic Account Detection
|
|
237
|
+
|
|
238
|
+
Accounts are detected from the matching rules file's `account1` directive:
|
|
239
|
+
|
|
240
|
+
**Example rules file** (`ledger/rules/ubs-0235-90250546.0.rules`):
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
account1 assets:bank:ubs:checking
|
|
244
|
+
account2 expenses:unknown
|
|
245
|
+
|
|
246
|
+
fields date, time, description1, description2, ...
|
|
247
|
+
|
|
248
|
+
if %description1 SALARY
|
|
249
|
+
account2 income:salary
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
The tool reads `account1 assets:bank:ubs:checking` to determine which account to reconcile.
|
|
253
|
+
|
|
254
|
+
### Manual Account Override
|
|
255
|
+
|
|
256
|
+
If rules file doesn't have `account1` or you want to override:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
reconcile-statement --contextId abc123-... --account "assets:bank:ubs:checking"
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## The Multi-Account Problem
|
|
263
|
+
|
|
264
|
+
### Why Context ID is Required
|
|
265
|
+
|
|
266
|
+
**Scenario**: You have two UBS CHF accounts:
|
|
267
|
+
|
|
268
|
+
- Checking: `0235-90250546.0`
|
|
269
|
+
- Savings: `0235-90250546.1`
|
|
270
|
+
|
|
271
|
+
Both CSVs end up in `import/done/ubs/chf/`. Without context IDs, the tool would:
|
|
272
|
+
|
|
273
|
+
❌ Search for: `provider=ubs`, `currency=chf`
|
|
274
|
+
❌ Find: BOTH CSV files
|
|
275
|
+
❌ Select: Most recent file (by modification time)
|
|
276
|
+
❌ Result: Wrong CSV reconciled against wrong account
|
|
277
|
+
|
|
278
|
+
**With context IDs**:
|
|
279
|
+
|
|
280
|
+
✓ Receive: `contextId` for checking account
|
|
281
|
+
✓ Load: Context with `accountNumber="0235-90250546.0"`
|
|
282
|
+
✓ Filter: CSVs by account number in filename
|
|
283
|
+
✓ Find: Exact CSV for checking account
|
|
284
|
+
✓ Result: Correct CSV reconciled against correct account
|
|
285
|
+
|
|
286
|
+
See [Import Context Architecture](../architecture/import-context.md) for technical details.
|
|
287
|
+
|
|
288
|
+
## Typical Workflow
|
|
289
|
+
|
|
290
|
+
### Scenario 1: Automatic Reconciliation (via Pipeline)
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
# Pipeline handles everything automatically
|
|
294
|
+
import-pipeline
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**What happens**:
|
|
298
|
+
|
|
299
|
+
1. `classify-statements` creates contexts for each CSV
|
|
300
|
+
2. `import-statements` imports transactions (one context at a time)
|
|
301
|
+
3. `reconcile-statement` receives contextId automatically
|
|
302
|
+
4. Tool loads context, finds CSV by accountNumber
|
|
303
|
+
5. Compares balances
|
|
304
|
+
6. Updates context with results
|
|
305
|
+
|
|
306
|
+
### Scenario 2: Manual Reconciliation
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
# Step 1: Classify and import first
|
|
310
|
+
classify-statements
|
|
311
|
+
# Output: { "classified": [{ "contextId": "abc123-...", ... }] }
|
|
312
|
+
|
|
313
|
+
import-statements --contextId "abc123-..." --checkOnly false
|
|
314
|
+
|
|
315
|
+
# Step 2: Reconcile using contextId
|
|
316
|
+
reconcile-statement --contextId "abc123-..."
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Scenario 3: Override Closing Balance
|
|
320
|
+
|
|
321
|
+
When CSV doesn't have balance metadata (e.g., Revolut):
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
# Get contextId from classify output
|
|
325
|
+
classify-statements
|
|
326
|
+
# Output: { "classified": [{ "contextId": "xyz789-...", "provider": "revolut", ... }] }
|
|
327
|
+
|
|
328
|
+
# Import
|
|
329
|
+
import-statements --contextId "xyz789-..." --checkOnly false
|
|
330
|
+
|
|
331
|
+
# Reconcile with manual balance
|
|
332
|
+
reconcile-statement --contextId "xyz789-..." --closingBalance "EUR 1234.56"
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Scenario 4: Override Account
|
|
336
|
+
|
|
337
|
+
When rules file is missing `account1` directive:
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
reconcile-statement --contextId "abc123-..." --account "assets:bank:ubs:checking"
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## Troubleshooting
|
|
344
|
+
|
|
345
|
+
### Balance Mismatch
|
|
346
|
+
|
|
347
|
+
**Symptom**: `Balance mismatch: expected CHF 12500.00, got CHF 12490.00 (difference: CHF -10.00)`
|
|
348
|
+
|
|
349
|
+
**Common Causes**:
|
|
350
|
+
|
|
351
|
+
1. **Missing transaction**: Check if a transaction was skipped during import
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
# Compare CSV transaction count vs imported count
|
|
355
|
+
wc -l import/done/ubs/chf/transactions.csv
|
|
356
|
+
hledger register assets:bank:ubs:checking -b 2026-02-01 -e 2026-03-01 | wc -l
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
2. **Skip rule too aggressive**: A rule in the `.rules` file may be skipping legitimate transactions
|
|
360
|
+
|
|
361
|
+
```bash
|
|
362
|
+
# Review skip rules in rules file
|
|
363
|
+
grep "^skip" ledger/rules/ubs-0235-90250546.0.rules
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
3. **Wrong date range**: Transactions outside the CSV's date range affecting balance
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
# Check for transactions after the CSV's until-date
|
|
370
|
+
hledger register assets:bank:ubs:checking -b {until-date}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
4. **Manual entries**: Transactions manually added to journal that aren't in CSV
|
|
374
|
+
```bash
|
|
375
|
+
# Search for manual entries
|
|
376
|
+
grep assets:bank:ubs:checking ledger/2026.journal
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Wrong CSV Reconciled
|
|
380
|
+
|
|
381
|
+
**Symptom**: Reconciling checking account but errors mention savings account transactions
|
|
382
|
+
|
|
383
|
+
**Cause**: This shouldn't happen with context IDs. If it does:
|
|
384
|
+
|
|
385
|
+
1. **Verify context**:
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
jq . .memory/{contextId}.json
|
|
389
|
+
# Check that accountNumber matches the intended account
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
2. **Check CSV filename**:
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
ls import/done/ubs/chf/
|
|
396
|
+
# Verify filename contains correct account number
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
3. **Verify rules file**:
|
|
400
|
+
```bash
|
|
401
|
+
cat ledger/rules/ubs-0235-90250546.0.rules
|
|
402
|
+
# Check that account1 is correct
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Context Not Found
|
|
406
|
+
|
|
407
|
+
**Error**: `Failed to load import context: abc123-...`
|
|
408
|
+
|
|
409
|
+
**Solutions**:
|
|
410
|
+
|
|
411
|
+
1. **Verify context file exists**:
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
ls .memory/abc123-*.json
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
2. **Get valid contextId from classify output**:
|
|
418
|
+
|
|
419
|
+
```bash
|
|
420
|
+
# Re-run classify to get current contextIds
|
|
421
|
+
classify-statements
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
3. **Check for typos**: Ensure contextId is copied correctly (UUIDs are long!)
|
|
425
|
+
|
|
426
|
+
### No Closing Balance
|
|
427
|
+
|
|
428
|
+
**Error**: `No closing balance found in CSV metadata or data`
|
|
429
|
+
|
|
430
|
+
**Solutions**:
|
|
431
|
+
|
|
432
|
+
1. **Provide manual balance**:
|
|
433
|
+
|
|
434
|
+
```bash
|
|
435
|
+
reconcile-statement --contextId abc123-... --closingBalance "CHF 2324.79"
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
2. **Check CSV metadata**: Some providers include balance in header rows
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
head -15 import/done/ubs/chf/transactions.csv
|
|
442
|
+
# Look for "Closing balance" or similar
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
3. **Update provider config**: Add balance extraction rules to `config/import/providers.yaml`
|
|
446
|
+
|
|
447
|
+
### Account Not Determined
|
|
448
|
+
|
|
449
|
+
**Error**: `Could not determine account from rules file`
|
|
450
|
+
|
|
451
|
+
**Solutions**:
|
|
452
|
+
|
|
453
|
+
1. **Add account1 to rules file**:
|
|
454
|
+
|
|
455
|
+
```bash
|
|
456
|
+
# Edit rules file
|
|
457
|
+
echo "account1 assets:bank:ubs:checking" >> ledger/rules/ubs-0235-90250546.0.rules
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
2. **Provide manual account**:
|
|
461
|
+
```bash
|
|
462
|
+
reconcile-statement --contextId abc123-... --account "assets:bank:ubs:checking"
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
## Context Updates
|
|
466
|
+
|
|
467
|
+
After reconciliation, the context is updated with results:
|
|
468
|
+
|
|
469
|
+
**Before reconciliation** (`.memory/abc123-....json`):
|
|
470
|
+
|
|
471
|
+
```json
|
|
472
|
+
{
|
|
473
|
+
"id": "abc123-...",
|
|
474
|
+
"accountNumber": "0235-90250546.0",
|
|
475
|
+
"closingBalance": "5432.10",
|
|
476
|
+
"transactionCount": 42
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
**After successful reconciliation**:
|
|
481
|
+
|
|
482
|
+
```json
|
|
483
|
+
{
|
|
484
|
+
"id": "abc123-...",
|
|
485
|
+
"accountNumber": "0235-90250546.0",
|
|
486
|
+
"closingBalance": "5432.10",
|
|
487
|
+
"transactionCount": 42,
|
|
488
|
+
"reconciledAccount": "assets:bank:ubs:checking",
|
|
489
|
+
"actualBalance": "CHF 5432.10",
|
|
490
|
+
"lastTransactionDate": "2026-02-28",
|
|
491
|
+
"reconciled": true
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
**After failed reconciliation**:
|
|
496
|
+
|
|
497
|
+
```json
|
|
498
|
+
{
|
|
499
|
+
"id": "abc123-...",
|
|
500
|
+
"reconciledAccount": "assets:bank:ubs:checking",
|
|
501
|
+
"actualBalance": "CHF 5422.10",
|
|
502
|
+
"lastTransactionDate": "2026-02-28",
|
|
503
|
+
"reconciled": false
|
|
504
|
+
}
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
This provides an audit trail of reconciliation attempts.
|
|
508
|
+
|
|
509
|
+
## Error Handling
|
|
510
|
+
|
|
511
|
+
### Common Errors
|
|
512
|
+
|
|
513
|
+
| Error | Cause | Solution |
|
|
514
|
+
| ------------------------ | ----------------------------------------- | -------------------------------------------------------------- |
|
|
515
|
+
| Context not found | Invalid contextId or missing context file | Verify contextId from classify-statements output |
|
|
516
|
+
| CSV file not found | File was moved/deleted after import | Re-run import-statements or check file location |
|
|
517
|
+
| No closing balance | CSV metadata missing balance | Provide `--closingBalance` manually |
|
|
518
|
+
| Account not determined | Rules file missing `account1` | Add `account1` directive to rules file or provide `--account` |
|
|
519
|
+
| Balance mismatch | Missing transactions or incorrect rules | Check for skipped transactions, review skip rules |
|
|
520
|
+
| Multiple CSVs match | Account number ambiguous | Shouldn't happen with contexts; check accountNumber in context |
|
|
521
|
+
| hledger query failed | Journal file corrupted or invalid | Run `hledger check` to validate journal |
|
|
522
|
+
| Last transaction missing | Import didn't include all transactions | Check import logs, verify CSV was fully processed |
|
|
523
|
+
|
|
524
|
+
## See Also
|
|
525
|
+
|
|
526
|
+
- [Import Context Architecture](../architecture/import-context.md) - Technical details on context system
|
|
527
|
+
- [classify-statements Tool](classify-statements.md) - Creates contexts
|
|
528
|
+
- [import-statements Tool](import-statements.md) - Updates contexts with import results
|
|
529
|
+
- [import-pipeline Tool](import-pipeline.md) - Orchestrates classify → import → reconcile flow
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fuzzle/opencode-accountant",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0-next.1",
|
|
4
4
|
"description": "An OpenCode accounting agent, specialized in double-entry-bookkepping with hledger",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "ali bengali",
|
|
@@ -28,8 +28,7 @@
|
|
|
28
28
|
"docs"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@opencode-ai/plugin": "
|
|
32
|
-
"convert-csv-to-json": "^3.20.0",
|
|
31
|
+
"@opencode-ai/plugin": "^1.2.6",
|
|
33
32
|
"glob": "^10.3.10",
|
|
34
33
|
"js-yaml": "^4.1.0",
|
|
35
34
|
"minimatch": "^9.0.3",
|
|
@@ -44,7 +43,7 @@
|
|
|
44
43
|
"@types/uuid": "^11.0.0",
|
|
45
44
|
"@typescript-eslint/eslint-plugin": "8.47.0",
|
|
46
45
|
"@typescript-eslint/parser": "8.47.0",
|
|
47
|
-
"bun-types": "
|
|
46
|
+
"bun-types": "^1.3.9",
|
|
48
47
|
"eslint": "^9.39.1",
|
|
49
48
|
"eslint-config-prettier": "10.1.8",
|
|
50
49
|
"eslint-plugin-prettier": "^5.1.3",
|