Files
dennis 236053cd7f 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.
2026-06-15 14:41:04 +02:00

4.6 KiB

Three-Way Django Analysis Engine — Coop ↔ Uniconta ↔ Bank

Session-specific implementation notes for building a maintainable three-source reconciliation layer in a Django-based internal finance app.

When to use this pattern

Use when the task has three sources with different semantic roles:

  • ERP/accounting ledger, e.g. Uniconta = invoiced/booked postings
  • Counterparty/customer statement, e.g. Coop kontoudtog = settlement/detail source
  • Bank CSV = actual cash movement / reality check

Do not treat the result as a legal claim automatically. The engine should produce review flags and explainable evidence.

Keep the analysis engine separate from import parsing and HTTP views:

apps/core/parsers.py                    # source-specific import/normalization
apps/core/three_way.py                  # pure-ish analysis functions
apps/core/management/commands/*.py      # import/analyze CLI commands
apps/reconciliation/views.py            # dashboard/report presentation only
apps/reconciliation/templates/...       # HTML tables/cards

Good public functions in the analysis module:

analyze_three_way_year(year)
build_monthly_summary(year)
build_invoice_statuses(year)
find_unexplained_bank_payments(year)
build_bonus_adjustment_summary(year)

Views should call these functions and render results; avoid embedding reconciliation logic in templates or views.

Batch import command

For folder-based source projects, add a deterministic command that imports all configured source directories:

python manage.py import_all_sources --base-dir /app/source_data

Expected source layout:

Fra Coop/
Fra Uniconta/
Fra bank/

The command should:

  1. Walk each known directory.
  2. Infer source from directory, not filename alone.
  3. Preserve original file, sheet and row identifiers.
  4. Store row-level import errors.
  5. Print counts per file and total imported/error counts.
  6. Be safe to rerun or clearly document duplicate behavior.

Three-way reporting outputs

A useful first report page should show:

  • possible amounts the counterparty may owe (Mulige beløb Coop skylder)
  • unexplained bank postings (Uforklarede bankposter)
  • invoice statuses analyzed
  • months with material differences
  • month-level totals across all three sources
  • bonus/credit/offset summary by period

For each proposed issue, include evidence:

  • amount difference
  • relevant date range
  • invoice/reference, if present
  • source counts
  • which fields matched and which did not
  • score/status and whether manual review is required

Interpretation pitfall

Large totals such as “possible amount owed” are analysis flags, not final claims.

Reasons:

  • One bank payment can cover many Coop/Uniconta postings.
  • One Coop settlement can cover many invoices.
  • Dates may differ between invoice date, statement date, settlement date and bank date.
  • Bonus/credit notes may be non-cash accruals or period-based offsets.
  • Open-post lists are status snapshots and should be treated separately from ledger/history.

The UI and README should label these as review candidates until group matching and manual approval confirm them.

Matching strategy after basic import

Start with conservative layers:

  1. Invoice/reference + absolute amount + date tolerance.
  2. Remaining amount + date tolerance.
  3. Month-level totals across all three sources.
  4. Many-to-one and one-to-many group matching.
  5. Bonus/credit period reconciliation.
  6. Manual approval/rejection with audit log.

For bank matching, prioritize group matching:

bank payment = sum of many Coop settlement lines = sum of many Uniconta invoices

Use the bank as cash-flow validation, not necessarily as a row-level join target.

Tests to include

Minimum tests for this class of feature:

  • invoice is paid when Uniconta, Coop and bank agree
  • invoice is flagged when Coop/bank evidence indicates a residual amount
  • bank payment without Coop/Uniconta support is flagged as unexplained
  • monthly summary calculates Bank-Coop and Bank-Uniconta differences correctly

For Django, add a lightweight test settings module if the normal dev database requires services/permissions:

config/settings/test.py  # SQLite, fast local tests

Then run:

DJANGO_SETTINGS_MODULE=config.settings.test python manage.py test

Documentation to update

When this pattern is implemented, update:

  • README.md: installation, Docker, import flow, analysis commands, limitations
  • ANALYSE.md or ANALYSE_TREVEJS.md: source findings and interpretation
  • HANDOFF.md: current status and next recommended steps
  • TODO.md/ROADMAP.md: group matching, manual review, bonus expansion