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.
123 lines
3.4 KiB
Markdown
123 lines
3.4 KiB
Markdown
# 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:
|
|
|
|
```text
|
|
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.:
|
|
|
|
```text
|
|
backend/apps/common/templatetags/amount_format.py
|
|
```
|
|
|
|
Example filter:
|
|
|
|
```python
|
|
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:
|
|
|
|
```django
|
|
{% 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:
|
|
|
|
```python
|
|
template = Template("{% load amount_format %}{{ value|amount_dk }}")
|
|
assert template.render(Context({"value": Decimal("1234567.89")})) == "1.234.567,89"
|
|
```
|
|
|
|
2. 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`
|
|
|
|
3. Run the relevant app tests and system check in the project runtime, usually Docker Compose for Radix internal apps:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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.
|