Add support for 'leading' digits in render_currency

This commit is contained in:
Oliver Walters 2026-06-19 23:54:53 +00:00
parent 75a7db6015
commit 2153a4e231
3 changed files with 54 additions and 7 deletions

View File

@ -556,9 +556,28 @@ The locale is resolved in the following priority order:
1. **Explicit `locale=` argument** — highest priority, always wins
2. **Server `LANGUAGE_CODE`** — fallback
#### Leading Digits
The `leading` argument specifies the minimum number of digits to render before the decimal point (zero-padded). This works identically to `leading` in [`format_number`](#format_number):
```html
{% raw %}
{% load report %}
<!-- default: no padding -->
{% render_currency order.total_price currency='USD' %}
<!-- output: $1.23 -->
<!-- force at least 4 integer digits -->
{% render_currency order.total_price currency='USD' leading=4 %}
<!-- output: $0,001.23 -->
{% endraw %}
```
#### Custom Format Strings
The `fmt` argument accepts a [Unicode number pattern](https://unicode.org/reports/tr35/tr35-numbers.html#Number_Format_Patterns) string (same syntax as [`format_number`](#custom-format-strings)). **When `fmt` is provided, it takes complete priority over `decimal_places` and `max_decimal_places`** — those arguments are ignored.
The `fmt` argument accepts a [Unicode number pattern](https://unicode.org/reports/tr35/tr35-numbers.html#Number_Format_Patterns) string (same syntax as [`format_number`](#custom-format-strings)). **When `fmt` is provided, it takes complete priority over `decimal_places`, `max_decimal_places`, and `leading`** — those arguments are ignored.
The `locale`, `currency`, `multiplier`, and `include_symbol` arguments are still applied when `fmt` is set.

View File

@ -797,12 +797,13 @@ def modulo(x: Any, y: Any, cast: Optional[type] = None) -> Any:
@register.simple_tag
def render_currency(
money,
money: Money | str | int | float | Decimal,
decimal_places: Optional[int] = None,
currency: Optional[str] = None,
multiplier: Optional[Decimal] = None,
max_decimal_places: Optional[int] = None,
include_symbol: bool = True,
leading: Optional[int] = None,
fmt: Optional[str] = None,
locale: Optional[str] = None,
**kwargs,
@ -816,12 +817,14 @@ def render_currency(
decimal_places: Minimum (forced) decimal places, e.g. decimal_places=2 gives '.00'. Defaults to the locale/currency standard.
max_decimal_places: Maximum decimal places (optional digits beyond decimal_places), e.g. max_decimal_places=4 allows up to 4.
include_symbol: If True, include the currency symbol in the output
fmt: Optional Babel number pattern string. When provided, takes priority over all decimal_places options.
leading: Minimum number of leading digits to render before the decimal point (default = 1)
fmt: Optional Babel number pattern string. When provided, takes priority over all other formatting options.
locale: Optional locale override (e.g. 'en-us', 'de-de'). Defaults to server LANGUAGE_CODE.
"""
if money in [None, '']:
return '-'
# If the supplied value is *not* a Money instance, attempt to convert it into one
if not isinstance(money, Money):
try:
money = Money(
@ -829,9 +832,7 @@ def render_currency(
currency or get_global_setting('INVENTREE_DEFAULT_CURRENCY'),
)
except Exception:
raise ValidationError(
'render_currency: invalid money value: ' + repr(money)
)
raise ValidationError(f'render_currency: invalid money value - {money!r}')
if currency is not None:
try:
@ -844,7 +845,7 @@ def render_currency(
money *= Decimal(str(multiplier).strip())
except Exception:
raise ValidationError(
'render_currency: invalid multiplier value: ' + repr(multiplier)
f'render_currency: invalid multiplier value - {multiplier!r}'
)
locale = get_locale(locale)
@ -870,6 +871,15 @@ def render_currency(
pattern.frac_prec = (decimal_places, max(decimal_places, max_decimal_places))
if leading is not None:
try:
leading = int(leading) or 0
except (ValueError, TypeError):
leading = 0
if leading > 0:
min_int, max_int = pattern.int_prec
pattern.int_prec = (max(leading, min_int), max(leading, max_int))
return pattern.apply(
money.amount,
locale,

View File

@ -609,6 +609,24 @@ class ReportTagTest(PartImageTestMixin, InvenTreeTestCase):
report_tags.render_currency(m, fmt='0.0000', decimal_places=2), '1234.5600'
)
# Test leading digits
m_small = Money(1.23, 'USD')
self.assertEqual(
report_tags.render_currency(m_small, leading=4, locale='en-us'), '$0,001.23'
)
# leading=1 is the default — no change
self.assertEqual(
report_tags.render_currency(m_small, leading=1, locale='en-us'), '$1.23'
)
# invalid leading falls back gracefully
self.assertEqual(
report_tags.render_currency(m_small, leading='x', locale='en-us'), '$1.23'
)
# fmt takes priority over leading
self.assertEqual(
report_tags.render_currency(m_small, leading=6, fmt='#,##0.00'), '1.23'
)
def test_render_currency_locale_override(self):
"""Explicit locale= kwarg takes priority over global setting and system locale."""
m = Money(1234.56, 'USD')