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

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

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:

  1. 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"
  1. 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
  1. 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:2 usages in the changed templates.
  • Confirm templates that use amount_dk include {% 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 Decimal to avoid binary rounding surprises.
  • If the app already has a localization/formatting utility, extend that instead of adding a second competing filter.