Initial import: 10 Radix skills across 3 categories
Kategoriserer danske regnskabs-/ERP-skills, ERP-data-afstemnings-
skill og to devops-workflows (gitea-issue-agent, kanban-workflows)
som Radix-medlemmers AI-agenter kan dele.
Struktur:
skills/<kategori>/<skill-navn>/SKILL.md
skills/<kategori>/<skill-navn>/{references,templates,scripts}/
Indekseret af index.json (genereret af scripts/rebuild_index.py).
Kør `python3 scripts/rebuild_index.py --check` i CI for at fange
synkroniseringsfejl.
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
# Bank Reconciliation — Django Implementation Guide
|
||||
|
||||
Practical implementation notes for extending a two-source Coop↔Uniconta reconciliation webapp with bank CSV support.
|
||||
|
||||
## Model extensions
|
||||
|
||||
Add these fields to `Transaction` (one migration):
|
||||
```python
|
||||
account_number = models.CharField(max_length=50, blank=True, db_index=True)
|
||||
bank_text = models.TextField(blank=True)
|
||||
valuta_date = models.DateField(null=True, blank=True)
|
||||
```
|
||||
|
||||
Also extend `SourceFile.source` choices and `Transaction.source`/`match_type` choices to include `("bank", "Bank")` and `("bank_total", "Bank-samlet")`.
|
||||
|
||||
## Bank CSV parser
|
||||
|
||||
Key implementation in `apps/import_/parsers.py`:
|
||||
|
||||
- **Encodings**: Try `latin-1` first, then `utf-8`. Most Danish bank exports use latin-1.
|
||||
- **Delimiter**: `;` (semicolon)
|
||||
- **Header mapping**: Fuzzy-match by lowercasing, stripping null bytes and quotes:
|
||||
- `dato` → date column
|
||||
- `valør`/`valuta` → value date column
|
||||
- `tekst`/`beskrivelse` → text column
|
||||
- `bel` (prefix) → amount column
|
||||
- `saldo` → balance column
|
||||
- `reference`/`bilagsrefer` → reference column
|
||||
- **Date parse**: `DD.MM.YYYY` → split by dots → `datetime(int(parts[2]), int(parts[1]), int(parts[0])).date()`
|
||||
- **Amount parse**:
|
||||
1. Strip `\xa0` (nbsp), `\u202f` (narrow nbsp), `" DKK"`
|
||||
2. If `.` and `,` both present → remove dots, replace comma with dot
|
||||
3. If only comma → replace with dot
|
||||
4. Convert to `Decimal`
|
||||
- **Bulk create**: Use `Transaction.objects.bulk_create(txns_to_create)` for performance
|
||||
- **Account extraction**: Regex `r"(\d{8,})"` on text field to find kundenummer/kontonummer
|
||||
|
||||
## Bank ↔ Coop match (many-to-one)
|
||||
|
||||
Group bank payments by `(account_number, year, month)` and match against the sum of unmatched Coop transactions in the same bucket.
|
||||
|
||||
```python
|
||||
bank_txns = Transaction.objects.filter(source="bank", year=year).values(
|
||||
"account_number", "year", "month"
|
||||
).annotate(total=Sum("amount"), count=Count("id"))
|
||||
|
||||
for bt in bank_txns:
|
||||
coop_sum = Transaction.objects.filter(
|
||||
source="coop", year=bt["year"], month=bt["month"],
|
||||
match_status="unmatched", account_number=bt["account_number"],
|
||||
).aggregate(s=Sum("amount"))["s"]
|
||||
|
||||
if coop_sum is not None and abs(bt["total"] - coop_sum) <= TOLERANCE:
|
||||
# Create MatchGroup linking bank bucket to Coop bucket
|
||||
...
|
||||
```
|
||||
|
||||
**Note**: Bank amounts are positive (indbetalinger), Coop RE/Bankoverførsel amounts are negative. Compare absolute values. Set `match_type="bank_total"`.
|
||||
|
||||
## Views/templates changes
|
||||
|
||||
- **Dashboard**: Add `bank_count`, `bank_sum` cards; add `bank_count` / `bank_sum` columns to monthly overview table
|
||||
- **Upload form**: Add `<option value="bank">Bank (Kontobevaegelser .csv)</option>`
|
||||
- **Reconciliation list**: Add `bank` to source filter; render bank badge with `.badge-warning`
|
||||
- **Run match page**: Document match phases: Reference → ZV↔Faktura → RE/Betaling → General → Many-to-one → Bank-total
|
||||
|
||||
## Discovered pitfalls
|
||||
|
||||
1. **Migrations not detected**: If `makemigrations` reports "No changes detected", check whether a prior manual migration already captured the changes. Run `migrate --check` first.
|
||||
2. **Docker file path mismatch**: When testing via `docker compose exec`, container filesystem may differ from host. Use the web UI upload form for file import testing.
|
||||
3. **Bank text homogeneity**: All transactions from the same counterparty often have identical text (e.g. `"Coop 0010014808 -SE MEDD."`). Text similarity is useless for matching. Rely on `account_number` + date+amount grouping.
|
||||
4. **Bank negatives are rare but critical**: Negative bank amounts (~1-2%) represent bonus/modregning/retur. Do NOT match these against positive Coop invoices. Flag separately for bonus reconciliation.
|
||||
5. **Bank total may differ slightly from Coop sum**: Normal difference is ±0.1% (fees, rounding). Use tolerance matching (e.g. ±5 DKK) for bank-total matches.
|
||||
Reference in New Issue
Block a user