mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-11-13 12:23:44 +01:00
Merge pull request #664 from officialasishkumar/feat/optional-exchanged-amounts
feat(currencies): make displaying exchanged amounts optional
This commit is contained in:
16 files changed
+320
-39
No files matched your search
@@ -103,6 +103,7 @@ class AccountForm(forms.ModelForm):
|
||||
"group",
|
||||
"currency",
|
||||
"exchange_currency",
|
||||
"show_exchanged_amounts",
|
||||
"is_asset",
|
||||
"is_archived",
|
||||
"is_investment",
|
||||
@@ -161,6 +162,7 @@ class AccountForm(forms.ModelForm):
|
||||
Switch("is_archived"),
|
||||
"currency",
|
||||
"exchange_currency",
|
||||
Switch("show_exchanged_amounts"),
|
||||
Switch("is_investment"),
|
||||
# Only relevant for investments: shown while the switch above is on
|
||||
Div(
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Generated by Django 5.2.17 on 2026-11-11 09:49
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("accounts", "0021_alter_account_income_categories_help"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="account",
|
||||
name="show_exchanged_amounts",
|
||||
field=models.BooleanField(
|
||||
default=True,
|
||||
help_text="Show amounts converted to this account's exchange currency next to its transactions and totals.",
|
||||
verbose_name="Show exchanged amounts",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -104,6 +104,14 @@ class Account(SharedObject):
|
||||
blank=True,
|
||||
help_text=_("Default currency for exchange calculations"),
|
||||
)
|
||||
show_exchanged_amounts = models.BooleanField(
|
||||
default=True,
|
||||
verbose_name=_("Show exchanged amounts"),
|
||||
help_text=_(
|
||||
"Show amounts converted to this account's exchange currency next to its "
|
||||
"transactions and totals."
|
||||
),
|
||||
)
|
||||
|
||||
is_asset = models.BooleanField(
|
||||
default=False,
|
||||
@@ -191,6 +199,19 @@ class Account(SharedObject):
|
||||
user = get_current_user()
|
||||
return self.untracked_by.filter(pk=user.pk).exists()
|
||||
|
||||
@property
|
||||
def displayed_exchange_currency(self):
|
||||
"""The exchange currency to show amounts in, or None when they're hidden.
|
||||
|
||||
The account's exchange currency takes precedence over its currency's, so
|
||||
whichever one provides it also decides whether it's shown.
|
||||
"""
|
||||
if self.exchange_currency_id:
|
||||
return self.exchange_currency if self.show_exchanged_amounts else None
|
||||
if self.currency.show_exchanged_amounts:
|
||||
return self.currency.exchange_currency
|
||||
return None
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
if self.exchange_currency == self.currency:
|
||||
|
||||
@@ -80,6 +80,7 @@ class AccountSerializer(serializers.ModelSerializer):
|
||||
"currency_id",
|
||||
"exchange_currency",
|
||||
"exchange_currency_id",
|
||||
"show_exchanged_amounts",
|
||||
"is_asset",
|
||||
"is_investment",
|
||||
"investment_class",
|
||||
|
||||
@@ -29,6 +29,7 @@ class CurrencyForm(forms.ModelForm):
|
||||
"suffix",
|
||||
"code",
|
||||
"exchange_currency",
|
||||
"show_exchanged_amounts",
|
||||
"is_archived",
|
||||
]
|
||||
widgets = {
|
||||
@@ -49,6 +50,7 @@ class CurrencyForm(forms.ModelForm):
|
||||
"prefix",
|
||||
"suffix",
|
||||
"exchange_currency",
|
||||
Switch("show_exchanged_amounts"),
|
||||
)
|
||||
|
||||
if self.instance and self.instance.pk:
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Generated by Django 5.2.17 on 2026-11-11 09:53
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("currencies", "0027_exchangerateservice_retention"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="currency",
|
||||
name="show_exchanged_amounts",
|
||||
field=models.BooleanField(
|
||||
default=True,
|
||||
help_text="Show amounts converted to the exchange currency next to transactions and totals in this currency. Accounts with their own exchange currency use their own setting, and consolidated totals are calculated either way.",
|
||||
verbose_name="Show exchanged amounts",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -31,6 +31,15 @@ class Currency(models.Model):
|
||||
blank=True,
|
||||
help_text=_("Default currency for exchange calculations"),
|
||||
)
|
||||
show_exchanged_amounts = models.BooleanField(
|
||||
default=True,
|
||||
verbose_name=_("Show exchanged amounts"),
|
||||
help_text=_(
|
||||
"Show amounts converted to the exchange currency next to transactions and "
|
||||
"totals in this currency. Accounts with their own exchange currency use "
|
||||
"their own setting, and consolidated totals are calculated either way."
|
||||
),
|
||||
)
|
||||
|
||||
is_archived = models.BooleanField(
|
||||
default=False,
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
from django.db.models import Case, F, When
|
||||
|
||||
|
||||
def shown_exchange_currency(path):
|
||||
"""Query expression for the exchange currency id at ``path`` (an account or a
|
||||
currency lookup, such as ``"account__currency__"``), or NULL when that object
|
||||
hides its exchanged amounts."""
|
||||
return Case(
|
||||
When(
|
||||
**{f"{path}show_exchanged_amounts": True},
|
||||
then=F(f"{path}exchange_currency"),
|
||||
)
|
||||
)
|
||||
@@ -8,6 +8,7 @@ from django.utils import timezone
|
||||
|
||||
from apps.currencies.models import Currency
|
||||
from apps.currencies.utils.convert import convert
|
||||
from apps.currencies.utils.display import shown_exchange_currency
|
||||
from apps.transactions.models import Transaction
|
||||
|
||||
|
||||
@@ -94,7 +95,7 @@ def get_month_by_month_data(year=None, group_by="categories"):
|
||||
"account__currency__decimal_places",
|
||||
"account__currency__prefix",
|
||||
"account__currency__suffix",
|
||||
"account__currency__exchange_currency",
|
||||
exchange_currency_id=shown_exchange_currency("account__currency__"),
|
||||
)
|
||||
.annotate(
|
||||
expense_total=Coalesce(
|
||||
@@ -139,6 +140,7 @@ def get_month_by_month_data(year=None, group_by="categories"):
|
||||
item_name = metric[name_field]
|
||||
month = metric["reference_date__month"]
|
||||
currency_id = metric["account__currency"]
|
||||
exchange_currency_id = metric["exchange_currency_id"]
|
||||
|
||||
# Use a consistent key for None (uncategorized/untagged/no entity)
|
||||
item_key = item_id if item_id is not None else "__none__"
|
||||
@@ -164,7 +166,7 @@ def get_month_by_month_data(year=None, group_by="categories"):
|
||||
"decimal_places": metric["account__currency__decimal_places"],
|
||||
"prefix": metric["account__currency__prefix"],
|
||||
"suffix": metric["account__currency__suffix"],
|
||||
"exchange_currency_id": metric["account__currency__exchange_currency"],
|
||||
"exchange_currency_id": exchange_currency_id,
|
||||
}
|
||||
|
||||
currency_data = {
|
||||
@@ -181,11 +183,9 @@ def get_month_by_month_data(year=None, group_by="categories"):
|
||||
}
|
||||
|
||||
# Handle currency conversion if exchange currency is set
|
||||
if metric["account__currency__exchange_currency"]:
|
||||
if exchange_currency_id:
|
||||
from_currency = Currency.objects.get(id=currency_id)
|
||||
exchange_currency = Currency.objects.get(
|
||||
id=metric["account__currency__exchange_currency"]
|
||||
)
|
||||
exchange_currency = Currency.objects.get(id=exchange_currency_id)
|
||||
|
||||
converted_amount, prefix, suffix, decimal_places = convert(
|
||||
amount=final_total,
|
||||
|
||||
@@ -6,6 +6,7 @@ from django.db.models.functions import Coalesce
|
||||
|
||||
from apps.currencies.models import Currency
|
||||
from apps.currencies.utils.convert import convert
|
||||
from apps.currencies.utils.display import shown_exchange_currency
|
||||
from apps.transactions.models import Transaction
|
||||
|
||||
# Grouping levels the overview can be built from. Any ordering of these keys is
|
||||
@@ -25,11 +26,11 @@ CURRENCY_FIELDS = (
|
||||
"account__currency__decimal_places",
|
||||
"account__currency__prefix",
|
||||
"account__currency__suffix",
|
||||
"account__currency__exchange_currency",
|
||||
)
|
||||
|
||||
# Aggregated amounts, plus the totals derived from them. Every one of these is
|
||||
# converted when the account's currency has an exchange currency.
|
||||
# converted when the account's currency has an exchange currency and shows
|
||||
# exchanged amounts.
|
||||
AGGREGATED_FIELDS = (
|
||||
"expense_current",
|
||||
"expense_projected",
|
||||
@@ -78,7 +79,12 @@ def _sum_of(transaction_type, is_paid):
|
||||
def _aggregate(transactions_queryset, group_fields, name_field):
|
||||
"""Sum income/expense per currency, grouped by the given fields."""
|
||||
return (
|
||||
transactions_queryset.values(*group_fields, name_field, *CURRENCY_FIELDS)
|
||||
transactions_queryset.values(
|
||||
*group_fields,
|
||||
name_field,
|
||||
*CURRENCY_FIELDS,
|
||||
exchange_currency_id=shown_exchange_currency("account__currency__"),
|
||||
)
|
||||
.annotate(
|
||||
expense_current=_sum_of(Transaction.Type.EXPENSE, is_paid=True),
|
||||
expense_projected=_sum_of(Transaction.Type.EXPENSE, is_paid=False),
|
||||
@@ -120,11 +126,9 @@ def _build_currency_data(metric, showing):
|
||||
"total": currency_data[total],
|
||||
}
|
||||
|
||||
if metric["account__currency__exchange_currency"]:
|
||||
if metric["exchange_currency_id"]:
|
||||
from_currency = Currency.objects.get(id=metric["account__currency"])
|
||||
exchange_currency = Currency.objects.get(
|
||||
id=metric["account__currency__exchange_currency"]
|
||||
)
|
||||
exchange_currency = Currency.objects.get(id=metric["exchange_currency_id"])
|
||||
|
||||
exchanged = {}
|
||||
for field in AGGREGATED_FIELDS + TOTAL_FIELDS:
|
||||
|
||||
@@ -7,6 +7,7 @@ from django.db.models.functions import Coalesce
|
||||
|
||||
from apps.currencies.models import Currency
|
||||
from apps.currencies.utils.convert import convert
|
||||
from apps.currencies.utils.display import shown_exchange_currency
|
||||
from apps.transactions.models import Transaction
|
||||
|
||||
|
||||
@@ -83,7 +84,7 @@ def get_year_by_year_data(group_by="categories"):
|
||||
"account__currency__decimal_places",
|
||||
"account__currency__prefix",
|
||||
"account__currency__suffix",
|
||||
"account__currency__exchange_currency",
|
||||
exchange_currency_id=shown_exchange_currency("account__currency__"),
|
||||
)
|
||||
.annotate(
|
||||
expense_total=Coalesce(
|
||||
@@ -126,6 +127,7 @@ def get_year_by_year_data(group_by="categories"):
|
||||
item_name = metric[name_field]
|
||||
year = metric["reference_date__year"]
|
||||
currency_id = metric["account__currency"]
|
||||
exchange_currency_id = metric["exchange_currency_id"]
|
||||
|
||||
# Use a consistent key for None (uncategorized/untagged/no entity)
|
||||
item_key = item_id if item_id is not None else "__none__"
|
||||
@@ -151,7 +153,7 @@ def get_year_by_year_data(group_by="categories"):
|
||||
"decimal_places": metric["account__currency__decimal_places"],
|
||||
"prefix": metric["account__currency__prefix"],
|
||||
"suffix": metric["account__currency__suffix"],
|
||||
"exchange_currency_id": metric["account__currency__exchange_currency"],
|
||||
"exchange_currency_id": exchange_currency_id,
|
||||
}
|
||||
|
||||
currency_data = {
|
||||
@@ -168,11 +170,9 @@ def get_year_by_year_data(group_by="categories"):
|
||||
}
|
||||
|
||||
# Handle currency conversion if exchange currency is set
|
||||
if metric["account__currency__exchange_currency"]:
|
||||
if exchange_currency_id:
|
||||
from_currency = Currency.objects.get(id=currency_id)
|
||||
exchange_currency = Currency.objects.get(
|
||||
id=metric["account__currency__exchange_currency"]
|
||||
)
|
||||
exchange_currency = Currency.objects.get(id=exchange_currency_id)
|
||||
|
||||
converted_amount, prefix, suffix, decimal_places = convert(
|
||||
amount=final_total,
|
||||
|
||||
@@ -573,24 +573,21 @@ class Transaction(OwnedObject):
|
||||
super().delete(*args, **kwargs)
|
||||
|
||||
def exchanged_amount(self):
|
||||
if self.account.exchange_currency:
|
||||
converted_amount, prefix, suffix, decimal_places = convert(
|
||||
self.amount,
|
||||
to_currency=self.account.exchange_currency,
|
||||
from_currency=self.account.currency,
|
||||
date=self.date,
|
||||
return self._exchanged_to(
|
||||
self.account.exchange_currency or self.account.currency.exchange_currency
|
||||
)
|
||||
if converted_amount:
|
||||
return {
|
||||
"amount": converted_amount,
|
||||
"prefix": prefix,
|
||||
"suffix": suffix,
|
||||
"decimal_places": decimal_places,
|
||||
}
|
||||
elif self.account.currency.exchange_currency:
|
||||
|
||||
def displayed_exchanged_amount(self):
|
||||
"""The exchanged amount to display, or None when it's hidden."""
|
||||
return self._exchanged_to(self.account.displayed_exchange_currency)
|
||||
|
||||
def _exchanged_to(self, exchange_currency):
|
||||
if not exchange_currency:
|
||||
return None
|
||||
|
||||
converted_amount, prefix, suffix, decimal_places = convert(
|
||||
self.amount,
|
||||
to_currency=self.account.currency.exchange_currency,
|
||||
to_currency=exchange_currency,
|
||||
from_currency=self.account.currency,
|
||||
date=self.date,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
from datetime import date, datetime, timezone as dt_timezone
|
||||
from decimal import Decimal
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test import TestCase, override_settings
|
||||
from django.urls import reverse
|
||||
|
||||
from apps.accounts.models import Account, AccountGroup
|
||||
from apps.common.middleware.thread_local import delete_current_user, write_current_user
|
||||
from apps.currencies.models import Currency, ExchangeRate
|
||||
from apps.insights.utils.month_by_month import get_month_by_month_data
|
||||
from apps.insights.utils.year_by_year import get_year_by_year_data
|
||||
from apps.transactions.models import Transaction, TransactionCategory
|
||||
from apps.transactions.utils.calculations import (
|
||||
calculate_account_totals,
|
||||
calculate_currency_totals,
|
||||
)
|
||||
|
||||
|
||||
@override_settings(
|
||||
STORAGES={
|
||||
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
|
||||
"staticfiles": {
|
||||
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"
|
||||
},
|
||||
},
|
||||
WHITENOISE_AUTOREFRESH=True,
|
||||
)
|
||||
class ExchangedAmountsTestBase(TestCase):
|
||||
def setUp(self):
|
||||
self.user = get_user_model().objects.create_user(
|
||||
email="exchanged@test.com", password="testpass123"
|
||||
)
|
||||
self.client.login(username="exchanged@test.com", password="testpass123")
|
||||
write_current_user(self.user)
|
||||
self.addCleanup(delete_current_user)
|
||||
|
||||
self.usd = Currency.objects.create(
|
||||
code="USD", name="US Dollar", decimal_places=2, prefix="$ "
|
||||
)
|
||||
self.eur = Currency.objects.create(
|
||||
code="EUR",
|
||||
name="Euro",
|
||||
decimal_places=2,
|
||||
prefix="€ ",
|
||||
exchange_currency=self.usd,
|
||||
)
|
||||
ExchangeRate.objects.create(
|
||||
from_currency=self.eur,
|
||||
to_currency=self.usd,
|
||||
rate=Decimal("2"),
|
||||
date=datetime(2026, 1, 1, tzinfo=dt_timezone.utc),
|
||||
)
|
||||
self.account = Account.objects.create(
|
||||
name="Euro Account",
|
||||
group=AccountGroup.objects.create(name="Group"),
|
||||
currency=self.eur,
|
||||
)
|
||||
self.transaction = Transaction.objects.create(
|
||||
account=self.account,
|
||||
type=Transaction.Type.INCOME,
|
||||
is_paid=True,
|
||||
date=date(2026, 3, 10),
|
||||
reference_date=date(2026, 3, 1),
|
||||
amount=Decimal("100"),
|
||||
description="Salary",
|
||||
category=TransactionCategory.objects.create(name="Work"),
|
||||
)
|
||||
|
||||
def _hide_for(self, obj):
|
||||
obj.show_exchanged_amounts = False
|
||||
obj.save()
|
||||
|
||||
|
||||
class TransactionExchangedAmountTests(ExchangedAmountsTestBase):
|
||||
def test_shown_by_default(self):
|
||||
exchanged = self.transaction.displayed_exchanged_amount()
|
||||
|
||||
self.assertEqual(exchanged["amount"], Decimal("200"))
|
||||
self.assertEqual(exchanged["prefix"], "$ ")
|
||||
|
||||
def test_currency_can_hide_it(self):
|
||||
self._hide_for(self.eur)
|
||||
self.transaction.refresh_from_db()
|
||||
|
||||
self.assertIsNone(self.transaction.displayed_exchanged_amount())
|
||||
# Only hidden from display, the API keeps returning it
|
||||
self.assertEqual(self.transaction.exchanged_amount()["amount"], Decimal("200"))
|
||||
|
||||
def test_account_exchange_currency_follows_the_account_setting(self):
|
||||
self.account.exchange_currency = self.usd
|
||||
self.account.save()
|
||||
self._hide_for(self.eur)
|
||||
self.transaction.refresh_from_db()
|
||||
|
||||
self.assertEqual(
|
||||
self.transaction.displayed_exchanged_amount()["amount"], Decimal("200")
|
||||
)
|
||||
|
||||
self._hide_for(self.account)
|
||||
self.transaction.refresh_from_db()
|
||||
|
||||
self.assertIsNone(self.transaction.displayed_exchanged_amount())
|
||||
|
||||
def test_monthly_list_hides_it(self):
|
||||
url = reverse("monthly_transactions_list", kwargs={"month": 3, "year": 2026})
|
||||
|
||||
response = self.client.get(url, HTTP_HX_REQUEST="true")
|
||||
self.assertContains(response, "exchanged-amount")
|
||||
|
||||
self._hide_for(self.eur)
|
||||
response = self.client.get(url, HTTP_HX_REQUEST="true")
|
||||
self.assertContains(response, f'data-transaction-id="{self.transaction.id}"')
|
||||
self.assertNotContains(response, "exchanged-amount")
|
||||
|
||||
|
||||
class TotalsExchangedAmountTests(ExchangedAmountsTestBase):
|
||||
def test_currency_totals_hide_it_but_keep_consolidating(self):
|
||||
self._hide_for(self.eur)
|
||||
|
||||
totals = calculate_currency_totals(Transaction.objects.all(), deep_search=True)
|
||||
|
||||
self.assertNotIn("exchanged", totals[self.eur.id])
|
||||
self.assertEqual(
|
||||
totals[self.usd.id]["consolidated"]["total_final"], Decimal("200")
|
||||
)
|
||||
|
||||
def test_currency_totals_hide_it_for_currencies_found_by_deep_search(self):
|
||||
brl = Currency.objects.create(code="BRL", name="Real", decimal_places=2)
|
||||
self.usd.exchange_currency = brl
|
||||
self.usd.save()
|
||||
|
||||
totals = calculate_currency_totals(Transaction.objects.all(), deep_search=True)
|
||||
self.assertIn("exchanged", totals[self.usd.id])
|
||||
|
||||
self._hide_for(self.usd)
|
||||
totals = calculate_currency_totals(Transaction.objects.all(), deep_search=True)
|
||||
self.assertNotIn("exchanged", totals[self.usd.id])
|
||||
|
||||
def test_account_totals_follow_the_account_setting(self):
|
||||
self.account.exchange_currency = self.usd
|
||||
self.account.save()
|
||||
|
||||
totals = calculate_account_totals(Transaction.objects.all())
|
||||
self.assertEqual(
|
||||
totals[self.account.id]["exchanged"]["total_final"], Decimal("200")
|
||||
)
|
||||
|
||||
self._hide_for(self.account)
|
||||
totals = calculate_account_totals(Transaction.objects.all())
|
||||
self.assertNotIn("exchanged", totals[self.account.id])
|
||||
|
||||
def test_insights_hide_it(self):
|
||||
def exchanged_totals():
|
||||
month_data = get_month_by_month_data(year=2026)
|
||||
year_data = get_year_by_year_data()
|
||||
return [
|
||||
month_data["grand_total"]["currencies"][self.eur.id],
|
||||
month_data["month_totals"][3]["currencies"][self.eur.id],
|
||||
year_data["grand_total"]["currencies"][self.eur.id],
|
||||
year_data["year_totals"][2026]["currencies"][self.eur.id],
|
||||
]
|
||||
|
||||
for total in exchanged_totals():
|
||||
self.assertEqual(total["exchanged"]["final_total"], Decimal("200"))
|
||||
|
||||
self._hide_for(self.eur)
|
||||
for total in exchanged_totals():
|
||||
self.assertNotIn("exchanged", total)
|
||||
|
||||
def test_net_worth_keeps_the_consolidated_total(self):
|
||||
self._hide_for(self.eur)
|
||||
|
||||
response = self.client.get(reverse("net_worth"), {"view_type": "current"})
|
||||
|
||||
currency_net_worth = response.context["currency_net_worth"]
|
||||
self.assertNotIn("exchanged", currency_net_worth[self.eur.id])
|
||||
self.assertEqual(
|
||||
currency_net_worth[self.usd.id]["consolidated"]["total_final"],
|
||||
Decimal("200"),
|
||||
)
|
||||
self.assertContains(response, "Consolidated")
|
||||
@@ -6,6 +6,7 @@ from django.db.models.functions import Coalesce
|
||||
|
||||
from apps.transactions.models import Transaction
|
||||
from apps.currencies.utils.convert import convert
|
||||
from apps.currencies.utils.display import shown_exchange_currency
|
||||
from apps.currencies.models import Currency
|
||||
|
||||
|
||||
@@ -29,6 +30,7 @@ def calculate_currency_totals(
|
||||
"account__currency__exchange_currency__decimal_places",
|
||||
"account__currency__exchange_currency__prefix",
|
||||
"account__currency__exchange_currency__suffix",
|
||||
"account__currency__show_exchanged_amounts",
|
||||
)
|
||||
.annotate(
|
||||
expense_current=Coalesce(
|
||||
@@ -177,6 +179,9 @@ def calculate_currency_totals(
|
||||
converted_val if converted_val is not None else Decimal("0")
|
||||
)
|
||||
|
||||
# Consolidated totals below need the exchanged values either way;
|
||||
# the currency's setting only hides them from display
|
||||
if total["account__currency__show_exchanged_amounts"]:
|
||||
currency_data["exchanged"] = exchanged_details
|
||||
|
||||
if exchange_currency_obj_for_this_total.id not in currencies_using_exchange:
|
||||
@@ -255,6 +260,7 @@ def calculate_currency_totals(
|
||||
converted_val if converted_val is not None else Decimal("0")
|
||||
)
|
||||
|
||||
if db_currency.show_exchanged_amounts:
|
||||
currency_data_for_db_currency["exchanged"] = (
|
||||
exchanged_details_for_db_currency
|
||||
)
|
||||
@@ -399,7 +405,7 @@ def calculate_account_totals(transactions_queryset, ignore_empty=False):
|
||||
"account__currency__decimal_places",
|
||||
"account__currency__prefix",
|
||||
"account__currency__suffix",
|
||||
"account__exchange_currency",
|
||||
exchange_currency_id=shown_exchange_currency("account__"),
|
||||
).annotate(
|
||||
expense_current=Coalesce(
|
||||
Sum(
|
||||
@@ -466,8 +472,8 @@ def calculate_account_totals(transactions_queryset, ignore_empty=False):
|
||||
account_id = total["account"]
|
||||
currency = Currency.objects.get(id=total["account__currency__id"])
|
||||
exchange_currency = (
|
||||
Currency.objects.get(id=total["account__exchange_currency"])
|
||||
if total["account__exchange_currency"]
|
||||
Currency.objects.get(id=total["exchange_currency_id"])
|
||||
if total["exchange_currency_id"]
|
||||
else None
|
||||
)
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
:decimal_places="leg.account.currency.decimal_places"
|
||||
:color="color"></c-amount.display>
|
||||
{% if not dummy %}
|
||||
{% with exchanged=leg.exchanged_amount %}
|
||||
{% with exchanged=leg.displayed_exchanged_amount %}
|
||||
{% if exchanged %}
|
||||
<c-amount.display
|
||||
:amount="exchanged.amount"
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
</div>
|
||||
{# Exchange Rate#}
|
||||
{% if not dummy %}
|
||||
{% with exchanged=transaction.exchanged_amount %}
|
||||
{% with exchanged=transaction.displayed_exchanged_amount %}
|
||||
{% if exchanged %}
|
||||
<div class="exchanged-amount mb-2 lg:mb-0">
|
||||
<c-amount.display
|
||||
|
||||
Reference in new issue
Block a user