236053cd7f
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.
168 lines
5.8 KiB
Markdown
168 lines
5.8 KiB
Markdown
# Reconciliation Webapp Architecture (Django)
|
|
|
|
Reference architecture for building a webapp on top of a reconciliation engine.
|
|
Based on Coop-Uniconta project (Radix), May 2026.
|
|
|
|
## Stack
|
|
|
|
- **Backend**: Django 4.2 + Django REST Framework
|
|
- **Database**: MariaDB 11.4
|
|
- **Cache/Queue**: Redis + Celery (for background matching)
|
|
- **Frontend**: Django Templates + vanilla JS (no React needed for internal tools)
|
|
- **Deployment**: Docker Compose (web, db, redis)
|
|
- **Auth**: Django built-in + admin, later MS Entra/OIDC
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
backend/
|
|
config/
|
|
settings/base.py # Shared config
|
|
settings/local.py # Dev overrides (DEBUG=True, admin enabled)
|
|
urls.py # URL routing
|
|
wsgi.py / asgi.py # Entry points
|
|
celery.py # Celery app
|
|
apps/
|
|
core/ # Models: Transaction, MatchResult, InvoiceStatus, AuditLog, BonusRule
|
|
models.py
|
|
admin.py
|
|
matching.py # 5-phase match engine
|
|
import_/ # Excel parsers
|
|
parsers.py # Coop + Uniconta import with preview
|
|
reconciliation/ # Views: dashboard, import, matching, reports
|
|
views.py
|
|
templates/
|
|
templates/
|
|
base.html # Dark-themed layout (sidebar nav + main content)
|
|
static/
|
|
css/app.css # Dark theme: --bg-primary: #0f172a, --accent: #3b82f6
|
|
manage.py
|
|
entrypoint.sh # wait-for-db + migrate + runserver
|
|
Dockerfile # python:3.12-slim
|
|
requirements.txt
|
|
```
|
|
|
|
## Data Model
|
|
|
|
```
|
|
AccountingYear (year, start_date, end_date)
|
|
SourceFile (source, file_name, file_path, row_count, parsed_count, error_count, accounting_year)
|
|
Transaction (source_file, source, year, month, date, amount, abs_amount,
|
|
doc_type, reference, faktura, text, text_normalized,
|
|
konteringstype, bilag, doc_number,
|
|
match_status, match_type, match_score, match_group,
|
|
original_data JSON)
|
|
MatchResult (group_id, match_type, score, status,
|
|
transaction_a, transaction_b, additional_b_ids JSON,
|
|
amount_diff, date_diff_days, explanation, comment,
|
|
approved_by, approved_at)
|
|
InvoiceStatus (faktura_number, accounting_year, invoice_total, paid_total, remaining,
|
|
status, invoice_date, first_invoice_text,
|
|
coop_payment_count, uniconta_payment_count, invoice_count)
|
|
AuditLog (action, user, match_result, details JSON)
|
|
BonusRule (name, year, period_start, period_end, conditions JSON, percentage, min_amount)
|
|
BonusCalculation (rule, transaction, calculated_amount, status)
|
|
```
|
|
|
|
## Key Design Decisions
|
|
|
|
1. **Store original_data as JSON** — Always preserve raw Excel row data for traceability.
|
|
2. **Separate `amount` and `abs_amount`** — `amount` keeps original sign for net calculations; `abs_amount` for matching.
|
|
3. **MatchResult captures both sides** — Primary match (transaction_a → transaction_b) + optional additional_b_ids for many-to-one.
|
|
4. **InvoiceStatus is computed, not stored per-transaction** — Recalculated after each matching run.
|
|
5. **AuditLog for every manual action** — Match approval, rejection, bonus adjustment.
|
|
|
|
## Match Engine Integration
|
|
|
|
The match engine runs as a Celery task triggered from the web UI:
|
|
|
|
```python
|
|
# apps/core/matching.py
|
|
AMT_TOL = Decimal("5.00")
|
|
DATE_TOL_REF = 21
|
|
DATE_TOL_GEN = 21
|
|
|
|
def run_matching(accounting_year=None):
|
|
# Phase 1: Reference match (RE↔Faktura, RG↔Kreditnota)
|
|
# Phase 2: ZV↔Faktura
|
|
# Phase 3: RE↔Betaling without reference
|
|
# Phase 4: General abs(amount)+date
|
|
# Phase 5: Many-to-one (sum of 2-3 Uni = 1 Coop)
|
|
# Bulk create MatchResult, bulk update Transaction statuses
|
|
```
|
|
|
|
## UI Pages
|
|
|
|
| Page | Purpose |
|
|
|------|---------|
|
|
| Dashboard | Year selector, stat cards, invoice status summary, monthly overview |
|
|
| Import | Upload Excel, show preview, map columns if auto-detection fails |
|
|
| Reconciliation List | Filterable table of all transactions, paginated (50/page) |
|
|
| Transaction Detail | Raw data, normalized data, potential matches, match history |
|
|
| Faktura Status | All invoices with BETALT/DELVIST/UBETALT, filterable |
|
|
| Reports | By type, by month, match summary, export to Excel |
|
|
| Run Match | Trigger background matching for selected year |
|
|
|
|
## Dark Theme CSS Variables
|
|
|
|
```css
|
|
:root {
|
|
--bg-primary: #0f172a;
|
|
--bg-secondary: #1e293b;
|
|
--bg-tertiary: #334155;
|
|
--text-primary: #f8fafc;
|
|
--text-secondary: #94a3b8;
|
|
--accent: #3b82f6;
|
|
--success: #22c55e;
|
|
--warning: #f59e0b;
|
|
--danger: #ef4444;
|
|
--border: #334155;
|
|
--radius: 8px;
|
|
}
|
|
```
|
|
|
|
Inspired by Radix-ERP visual style: sidebar navigation, card-based stats,
|
|
data tables with badges, filter bars above tables.
|
|
|
|
## Docker Compose
|
|
|
|
```yaml
|
|
services:
|
|
web:
|
|
build: ./backend
|
|
command: ["web-dev"]
|
|
ports: ["8000:8000"]
|
|
depends_on:
|
|
db: {condition: service_healthy}
|
|
env_file: [.env]
|
|
db:
|
|
image: mariadb:11.4
|
|
environment:
|
|
MARIADB_DATABASE: ${DB_NAME}
|
|
MARIADB_USER: ${DB_USER}
|
|
MARIADB_PASSWORD: ${DB_PASSWORD}
|
|
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
|
|
healthcheck:
|
|
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
|
redis:
|
|
image: redis:7-alpine
|
|
```
|
|
|
|
## Deployment Notes
|
|
|
|
- `python manage.py migrate` runs on container startup via entrypoint.sh
|
|
- `python manage.py createsuperuser` for first admin login
|
|
- Uploads stored in MEDIA_ROOT (mounted volume in production)
|
|
- Static files collected via `collectstatic` for production (nginx)
|
|
|
|
## Next Steps for a New Reconciliation Project
|
|
|
|
1. Copy project structure from template
|
|
2. Adapt parsers for the specific Excel formats
|
|
3. Run analysis script to determine match strategy
|
|
4. Configure AMT_TOL and DATE_TOL based on data quality
|
|
5. Build import UI with column mapping fallback
|
|
6. Implement match engine phases iteratively
|
|
7. Add manual match/approve/reject with audit log
|
|
8. Add reports and bonus calculation framework
|