diff --git a/src/fava/application.py b/src/fava/application.py index 45dceffee..a25c0e034 100644 --- a/src/fava/application.py +++ b/src/fava/application.py @@ -230,6 +230,7 @@ def _setup_template_config(fava_app: Flask, *, incognito: bool) -> None: fava_app.add_template_filter(template_filters.flag_to_type) fava_app.add_template_filter(template_filters.format_currency) fava_app.add_template_filter(template_filters.meta_items) + fava_app.add_template_filter(template_filters.price_base) fava_app.add_template_filter( template_filters.replace_numbers if incognito diff --git a/src/fava/template_filters.py b/src/fava/template_filters.py index 9ed099c5b..481836065 100644 --- a/src/fava/template_filters.py +++ b/src/fava/template_filters.py @@ -51,6 +51,12 @@ def format_currency(value: Decimal, currency: str | None = None) -> str: return g.ledger.format_decimal(value or ZERO, currency) +def price_base(value: Decimal) -> Decimal: + """Return a scaled base amount for displaying a price.""" + exponent = max(0, round(-value.log10())) if value > ZERO else 0 + return Decimal(10) ** exponent + + FLAGS_TO_TYPES = {"*": "cleared", "!": "pending"} diff --git a/src/fava/templates/_journal_table.html b/src/fava/templates/_journal_table.html index 8b170e11b..490b4ab3e 100644 --- a/src/fava/templates/_journal_table.html +++ b/src/fava/templates/_journal_table.html @@ -1,4 +1,4 @@ -{% from 'macros/_commodity_macros.html' import render_num, render_amount %} +{% from 'macros/_commodity_macros.html' import render_num, render_amount, render_price %} {% set short_type = { 'balance': 'Bal', @@ -133,7 +133,7 @@ {{ posting.cost.number|incognito }} {{ posting.cost.currency }} {{- ', {}'.format(posting.cost.date) if posting.cost.date else '' }} {{- ', "{}"'.format(posting.cost.label) if posting.cost.label else '' }} - {{ render_amount(ledger, posting.price) }} + {{ render_price(ledger, posting.units.currency, posting.price) }}
{{ _render_metadata(posting.meta|meta_items) }} diff --git a/src/fava/templates/macros/_commodity_macros.html b/src/fava/templates/macros/_commodity_macros.html index 29ede2c8a..d87162c80 100644 --- a/src/fava/templates/macros/_commodity_macros.html +++ b/src/fava/templates/macros/_commodity_macros.html @@ -11,3 +11,14 @@ {% macro render_num(ledger, currency, number) -%} {{ number|format_currency(currency)|incognito }} {{ currency }} {%- endmacro %} + +{% macro render_price(ledger, base_currency, price) %} +{% if price is none or price.number is none -%} + +{%- else -%} + {% set base = price.number|price_base -%} + + {{- base|format_currency(base_currency)|incognito }} {{ base_currency }} = {{ (base * price.number)|format_currency(price.currency)|incognito }} {{ price.currency -}} + +{%- endif %} +{% endmacro %} diff --git a/tests/test_template_filters.py b/tests/test_template_filters.py index 15ca7e009..d5b8a9bef 100644 --- a/tests/test_template_filters.py +++ b/tests/test_template_filters.py @@ -6,6 +6,7 @@ from fava.template_filters import basename from fava.template_filters import format_currency from fava.template_filters import passthrough_numbers +from fava.template_filters import price_base from fava.template_filters import replace_numbers if TYPE_CHECKING: # pragma: no cover @@ -29,6 +30,15 @@ def test_format_currency(app: Flask) -> None: assert format_currency(Decimal("2.12")) == "2.12" +def test_price_base() -> None: + assert price_base(Decimal(2)) == Decimal(1) + assert price_base(Decimal("0.5")) == Decimal(1) + assert price_base(Decimal("0.2")) == Decimal(10) + assert price_base(Decimal("0.02")) == Decimal(100) + assert price_base(Decimal("0.000123")) == Decimal(10000) + assert price_base(Decimal()) == Decimal(1) + + def test_basename() -> None: """Get the basename of a file path.""" assert basename(__file__) == "test_template_filters.py" diff --git a/tests/test_templates.py b/tests/test_templates.py index 6478861a5..de5de9b61 100644 --- a/tests/test_templates.py +++ b/tests/test_templates.py @@ -27,6 +27,21 @@ def test_render_amount(app: Flask, example_ledger: FavaLedger) -> None: assert macro(example_ledger, create.amount("10 TEST")) == res +def test_render_price(app: Flask, example_ledger: FavaLedger) -> None: + with app.test_request_context("/long-example/"): + app.preprocess_request() + macro = get_template_attribute( + "macros/_commodity_macros.html", + "render_price", + ) + res = "" + assert macro(example_ledger, "USD", None) == res + res = "100.00 USD = 2.00 TEST" + assert macro(example_ledger, "USD", create.amount("0.02 TEST")) == res + res = "1.00 TEST = 2.00 USD" + assert macro(example_ledger, "TEST", create.amount("2 USD")) == res + + def test_account_name(app: Flask, example_ledger: FavaLedger) -> None: with app.test_request_context("/long-example/"): app.preprocess_request()