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.
3.4 KiB
Django Accounting UI Formatting
Former skill: django-accounting-ui-formatting.
Use this when changing how money, balances, invoices, payments, totals, or differences are displayed in a Django accounting/reconciliation webapp.
Core rule
Accounting amounts must be formatted deliberately and consistently. Do not rely on ad-hoc floatformat:2 everywhere if the app has locale-specific expectations.
For Danish Radix accounting/reconciliation UIs, render amounts with:
- thousands separator:
. - decimal separator:
, - exactly 2 decimals
- minus sign before the formatted number
Example:
1234567.89 -> 1.234.567,89
-1234567.89 -> -1.234.567,89
0 -> 0,00
Recommended Django implementation
Create a shared template filter in a common app, e.g.:
backend/apps/common/templatetags/amount_format.py
Example filter:
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
from django import template
register = template.Library()
@register.filter
def amount_dk(value):
if value is None or value == "":
return "0,00"
try:
amount = Decimal(str(value)).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
except (InvalidOperation, ValueError, TypeError):
return value
sign = "-" if amount < 0 else ""
amount = abs(amount)
formatted = f"{amount:,.2f}"
return sign + formatted.replace(",", "X").replace(".", ",").replace("X", ".")
Ensure templatetags/__init__.py exists.
In templates:
{% load amount_format %}
{{ amount|amount_dk }} kr
What to update
Search all relevant templates for amount displays, especially:
floatformat:2- table columns named Beløb, Sum, Difference, Rest, Betalt
- dashboard cards
- report tables
- invoice/payment status pages
- transaction detail pages
Replace monetary floatformat:2 with the shared amount filter. Leave percentages, scores, counts, and raw JSON/raw imported data alone unless the user explicitly asks otherwise.
Tests
Follow TDD for behavior changes:
- Add a failing unit/template test for the filter:
template = Template("{% load amount_format %}{{ value|amount_dk }}")
assert template.render(Context({"value": Decimal("1234567.89")})) == "1.234.567,89"
- Add at least one rendered-page regression test for the main UI path, asserting:
- formatted amount is present, e.g.
1.234.567,89 - raw unformatted amount is absent, e.g.
1234567.89
- Run the relevant app tests and system check in the project runtime, usually Docker Compose for Radix internal apps:
docker compose exec -T web python manage.py test apps.core --settings=config.settings.test -v 2
docker compose exec -T web python manage.py check
Verification
Before finalizing:
- Confirm there are no remaining monetary
|floatformat:2usages in the changed templates. - Confirm templates that use
amount_dkinclude{% load amount_format %}. - Restart the web container if the running app needs to load a new template tag module:
docker compose restart web
Pitfalls
- Do not alter raw JSON/raw import displays; those are audit data and should preserve original values.
- Do not apply money formatting to percentages or match scores.
- Avoid Python float math for money formatting; use
Decimalto avoid binary rounding surprises. - If the app already has a localization/formatting utility, extend that instead of adding a second competing filter.