2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-07-05 06:32:55 +00:00

Report locale updates (#12208)

* Optional 'locale' arg to format_money

- Allows override of system locale when generating reports

* Updated documentation

* Add unit tests

* Handle invalid locale

* Handle invalid locale

* Add new global setting to control currency locale in reports

* Use setting in reports

* Add CHANGELOG entry

* Further unit tests

* Add unit tests for new setting

* Update docs

* More docs

* Refactoring:

- Change REPORT_CURRENCY_LOCALE to REPORT_LOCALE

* Extend unit testing

* Refactor format_number

* Add unit tests for explicit format strings

* Update examples for format_date

* Updated unit  tests

* Cleanup unit tests

* Fix more tests

* Adjust wording

* Remove global setting - simplify code

* Simplify unit tests

* Revert 'min_digits' to 'leading'

* Fix docs

* Refactor the render_currency function

- Move all functionality into report.py

* Cleanup duplicate code

* Updated docs

* Allow user to specify date_format

* Add support for 'leading' digits in render_currency

* Bug fix

* Fix unit test

* Add tests for "include_symbol"
This commit is contained in:
Oliver
2026-06-20 11:00:12 +10:00
committed by GitHub
parent 8a092b4d1d
commit ca16e6ec0a
9 changed files with 711 additions and 248 deletions
+6
View File
@@ -63,3 +63,9 @@ Currency exchange rates are updated periodically, using the configured currency
## Pricing Settings
Refer to the [global settings](../settings/global.md#pricing-and-currency) documentation for more information on available currency settings.
## Rendering Currencies in Reports
Currency values can be rendered in report templates using the [`render_currency`](../report/helpers.md#render_currency) helper function. This function formats a currency amount according to a locale, and supports currency conversion within the template.
See the [report helpers documentation](../report/helpers.md#currency-formatting) for full details and examples.
+252 -14
View File
@@ -320,15 +320,86 @@ The helper function `format_number` allows for some common number formatting opt
show_docstring_description: false
show_source: False
#### Example
#### Examples
```html
{% raw %}
{% load report %}
{% format_number 3.14159265359 decimal_places=5, leading=3 %}
<!-- output: 0003.14159 -->
<!-- Basic usage: strip trailing zeros -->
{% format_number 3.14159265359 decimal_places=5 %}
<!-- output: 3.14159 -->
<!-- Leading zeros with 'leading' option -->
{% format_number 3.14159265359 decimal_places=5 leading=3 %}
<!-- output: 003.14159 -->
<!-- Round to integer -->
{% format_number 3.14159265359 integer=True %}
<!-- output: 3 -->
<!-- Thousands separator -->
{% format_number 9988776.5 decimal_places=2 separator=True %}
<!-- output: 9,988,776.50 -->
<!-- Locale-aware formatting: decimal comma, dot thousands separator -->
{% format_number 9988776.5 decimal_places=2 separator=True locale='de-de' %}
<!-- output: 9.988.776,50 -->
<!-- Scale a value with a multiplier before formatting -->
{% format_number 0.175 multiplier=100 decimal_places=1 %}
<!-- output: 17.5 -->
<!-- Allow up to N significant decimal places, but suppress trailing zeros -->
{% format_number 1234.5 decimal_places=2 max_decimal_places=6 %}
<!-- output: 1234.5 -->
{% endraw %}
```
#### Custom Format Strings
The `fmt` argument accepts a [Unicode number pattern](https://unicode.org/reports/tr35/tr35-numbers.html#Number_Format_Patterns) string (the same syntax used by [Babel](https://babel.pocoo.org/en/latest/numbers.html)). When `fmt` is provided it takes complete priority over the `decimal_places`, `max_decimal_places`, `leading`, and `separator` arguments — those arguments are silently ignored.
The `integer` and `multiplier` arguments **are** still applied to the number before the format string is used.
| Symbol | Meaning |
| --- | --- |
| `0` | Required digit — always rendered, even if zero |
| `#` | Optional digit — suppressed when not significant |
| `,` | Grouping separator (position defines group size) |
| `.` | Decimal separator |
Common patterns:
| Pattern | Example output |
| --- | --- |
| `0` | `1235` |
| `#,##0` | `1,235` |
| `0.00` | `1234.57` |
| `#,##0.00` | `1,234.57` |
| `000` | `007` |
```html
{% raw %}
{% load report %}
<!-- Two decimal places, no grouping -->
{% format_number 1234.5678 fmt='0.00' %}
<!-- output: 1234.57 -->
<!-- Two decimal places with thousands separator -->
{% format_number 1234.5678 fmt='#,##0.00' %}
<!-- output: 1,234.57 -->
<!-- Same pattern, German locale: dot thousands, comma decimal -->
{% format_number 1234.5678 fmt='#,##0.00' locale='de-de' %}
<!-- output: 1.234,57 -->
<!-- Integer with thousands separator, large number -->
{% format_number 9988776655.4321 fmt='#,##0' integer=True %}
<!-- output: 9,988,776,655 -->
{% endraw %}
```
@@ -336,6 +407,25 @@ The helper function `format_number` allows for some common number formatting opt
For rendering date and datetime information, the following helper functions are available:
Both functions resolve their output using the following priority order:
1. **`fmt=` argument** — a [strftime format string](https://docs.python.org/3/library/datetime.html#format-codes). When provided, this takes full priority; `locale` and `date_format` are ignored.
2. **`locale=` argument** — when no `fmt` is given, Babel formats the value using the style set by `date_format` (default `medium`).
3. **Server `LANGUAGE_CODE`** — used as the locale when no `locale=` argument is supplied.
#### Date Format Styles
The `date_format` argument controls how Babel renders the date when locale-aware formatting is used. The four named styles are:
| Style | `format_date` example (en-us, 2025-01-12) | `format_datetime` example (en-us, 2025-01-12 14:30) |
| --- | --- | --- |
| `full` | `Sunday, January 12, 2025` | `Sunday, January 12, 2025 at 2:30:00 PM UTC` |
| `long` | `January 12, 2025` | `January 12, 2025 at 2:30:00 PM UTC` |
| `medium` *(default)* | `Jan 12, 2025` | `Jan 12, 2025, 2:30:00 PM` |
| `short` | `1/12/25` | `1/12/25, 2:30 PM` |
The exact output varies by locale — the table above uses `en-us`.
### format_date
::: report.templatetags.report.format_date
@@ -343,6 +433,35 @@ For rendering date and datetime information, the following helper functions are
show_docstring_description: false
show_source: False
#### Examples
```html
{% raw %}
{% load report %}
<!-- Default: medium style, locale from LANGUAGE_CODE -->
{% format_date my_date %}
<!-- output (en-us): Jan 12, 2025 -->
<!-- Explicit strftime format string — locale and date_format are ignored -->
{% format_date my_date fmt="%d/%m/%Y" %}
<!-- output: 12/01/2025 -->
<!-- Locale-aware, default medium style -->
{% format_date my_date locale='en-us' %}
<!-- output: Jan 12, 2025 -->
<!-- Short style -->
{% format_date my_date locale='en-us' date_format='short' %}
<!-- output: 1/12/25 -->
<!-- Full style -->
{% format_date my_date locale='en-us' date_format='full' %}
<!-- output: Sunday, January 12, 2025 -->
{% endraw %}
```
### format_datetime
::: report.templatetags.report.format_datetime
@@ -350,20 +469,31 @@ For rendering date and datetime information, the following helper functions are
show_docstring_description: false
show_source: False
### Date Formatting
If not specified, these methods return a result which uses ISO formatting. Refer to the [datetime format codes](https://docs.python.org/3/library/datetime.html#format-codes) for more information! |
### Example
A simple example of using the date formatting helper functions:
#### Examples
```html
{% raw %}
{% load report %}
Date: {% format_date my_date timezone="Australia/Sydney" %}
Datetime: {% format_datetime my_datetime format="%d-%m-%Y %H:%M%S" %}
<!-- Default: medium style, locale from LANGUAGE_CODE -->
{% format_datetime my_datetime %}
<!-- output (en-us): Jan 12, 2025, 2:30:00 PM -->
<!-- Explicit strftime format — locale and date_format are ignored -->
{% format_datetime my_datetime fmt="%d-%m-%Y %H:%M" %}
<!-- output: 12-01-2025 14:30 -->
<!-- Locale-aware, default medium style -->
{% format_datetime my_datetime locale='en-us' %}
<!-- output: Jan 12, 2025, 2:30:00 PM -->
<!-- Short style -->
{% format_datetime my_datetime locale='de-de' date_format='short' %}
<!-- output: 12.01.25, 14:30 -->
<!-- Convert to a specific timezone before formatting -->
{% format_datetime my_datetime timezone="Australia/Sydney" locale='en-au' %}
{% endraw %}
```
@@ -373,11 +503,115 @@ Datetime: {% format_datetime my_datetime format="%d-%m-%Y %H:%M%S" %}
The helper function `render_currency` allows for simple rendering of currency data. This function can also convert the specified amount of currency into a different target currency:
::: InvenTree.helpers_model.render_currency
::: report.templatetags.report.render_currency
options:
show_docstring_description: false
show_source: False
#### Decimal Places
When no decimal place arguments are provided, the locale/currency standard is used (e.g. 2 places for USD, 0 for JPY).
`decimal_places` and `max_decimal_places` work the same way as in [`format_number`](#format_number):
| Argument | Effect |
| --- | --- |
| `decimal_places=N` | Forces exactly N decimal digits (zero-padded) |
| `max_decimal_places=M` | Allows up to M decimal digits, suppressing trailing zeros beyond `decimal_places` |
| Both set | Forced minimum of `decimal_places`, optional up to `max_decimal_places` |
| Neither set | Locale/currency default (e.g. 2 for USD) |
```html
{% raw %}
{% load report %}
<!-- locale default for USD: 2 decimal places -->
{% render_currency order.total_price currency='USD' %}
<!-- output: $1,234.56 -->
<!-- force 3 decimal places -->
{% render_currency order.total_price currency='USD' decimal_places=3 %}
<!-- output: $1,234.560 -->
<!-- at least 2, up to 4 — trailing zeros beyond the value are suppressed -->
{% render_currency order.total_price currency='USD' decimal_places=2 max_decimal_places=4 %}
<!-- output: $1,234.5600 or $1,234.56 depending on the value -->
{% endraw %}
```
#### Locale and Symbol Rendering
The locale controls how the currency symbol and separators are rendered. For example, `USD 1234.56` with various locales:
| Locale | Output |
| --- | --- |
| `en-us` | `$1,234.56` |
| `en-gb` | `US$1,234.56` |
| `en-au` | `USD1,234.56` |
| `de-de` | `1.234,56 $` |
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`, `max_decimal_places`, and `leading`** — those arguments are ignored.
The `locale`, `currency`, `multiplier`, and `include_symbol` arguments are still applied when `fmt` is set.
To include the currency symbol in a `fmt` pattern, use the `¤` placeholder. Without it, no symbol appears regardless of `include_symbol`.
| Pattern | Example output (en-us, USD) |
| --- | --- |
| `#,##0.00` | `1,234.56` (no symbol) |
| `¤#,##0.00` | `$1,234.56` |
| `¤#,##0.0000` | `$1,234.5600` |
| `¤ #,##0.00` | `$ 1,234.56` |
```html
{% raw %}
{% load report %}
<!-- No symbol (no ¤ in pattern) -->
{% render_currency order.total_price currency='USD' fmt='#,##0.00' %}
<!-- output: 1,234.56 -->
<!-- Symbol via ¤ placeholder -->
{% render_currency order.total_price currency='USD' fmt='¤#,##0.0000' locale='en-us' %}
<!-- output: $1,234.5600 -->
<!-- fmt + locale: de-de separators -->
{% render_currency order.total_price currency='USD' fmt='#,##0.00' locale='de-de' %}
<!-- output: 1.234,56 -->
<!-- fmt takes priority — decimal_places=2 is ignored -->
{% render_currency order.total_price currency='USD' fmt='0.0000' decimal_places=2 %}
<!-- output: 1234.5600 -->
{% endraw %}
```
#### Example
@@ -392,8 +626,12 @@ The helper function `render_currency` allows for simple rendering of currency da
{% endfor %}
</ul>
<!-- Force 2 decimal places, convert to NZD -->
Total Price: {% render_currency order.total_price currency='NZD' decimal_places=2 %}
<!-- US-style symbol, regardless of server locale -->
Total Price: {% render_currency order.total_price currency='USD' locale='en-us' %}
{% endraw %}
```