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:
@@ -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
@@ -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 %}
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user