Compare commits

..

5 Commits

Author SHA1 Message Date
Herculino Trotta
a461a33dc2 Merge pull request #256
feat(net-worth): display consolidated amounts for currencies without transactions
2025-05-12 20:45:09 -03:00
Herculino Trotta
1213ffebeb feat(transactions:calculations): add deep_search param to return consolidated amounts for currencies without transactions / sort results by their final total 2025-05-12 20:43:47 -03:00
Herculino Trotta
c5a352cf4d feat(networth): only display consolidated amounts if they're different from the total 2025-05-12 20:33:50 -03:00
Herculino Trotta
cfcca54aa6 fix(networth): consolidated color doesn't reflect consolidated amount 2025-05-12 20:29:43 -03:00
Felix
234f8cd669 locale(Ukrainian): update translation
Currently translated at 14.7% (96 of 651 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/uk/
2025-05-12 14:16:54 +00:00
4 changed files with 327 additions and 171 deletions

View File

@@ -32,7 +32,7 @@ def net_worth_current(request):
)
currency_net_worth = calculate_currency_totals(
transactions_queryset=transactions_currency_queryset
transactions_queryset=transactions_currency_queryset, deep_search=True
)
account_net_worth = calculate_account_totals(
transactions_queryset=transactions_account_queryset
@@ -137,7 +137,7 @@ def net_worth_projected(request):
)
currency_net_worth = calculate_currency_totals(
transactions_queryset=transactions_currency_queryset
transactions_queryset=transactions_currency_queryset, deep_search=True
)
account_net_worth = calculate_account_totals(
transactions_queryset=transactions_account_queryset

View File

@@ -9,9 +9,11 @@ from apps.currencies.utils.convert import convert
from apps.currencies.models import Currency
def calculate_currency_totals(transactions_queryset, ignore_empty=False):
def calculate_currency_totals(
transactions_queryset, ignore_empty=False, deep_search=False
):
# Prepare the aggregation expressions
currency_totals = (
currency_totals_from_transactions = (
transactions_queryset.values(
"account__currency",
"account__currency__code",
@@ -19,7 +21,14 @@ def calculate_currency_totals(transactions_queryset, ignore_empty=False):
"account__currency__decimal_places",
"account__currency__prefix",
"account__currency__suffix",
"account__currency__exchange_currency",
"account__currency__exchange_currency", # ID of the exchange currency for the account's currency
# Fields for the exchange currency itself (if account.currency.exchange_currency is set)
# These might be null if not set, so handle appropriately.
"account__currency__exchange_currency__code",
"account__currency__exchange_currency__name",
"account__currency__exchange_currency__decimal_places",
"account__currency__exchange_currency__prefix",
"account__currency__exchange_currency__suffix",
)
.annotate(
expense_current=Coalesce(
@@ -72,36 +81,55 @@ def calculate_currency_totals(transactions_queryset, ignore_empty=False):
.order_by()
)
# First pass: Process basic totals and store all currency data
result = {}
currencies_using_exchange = (
{}
) # Track which currencies use which exchange currencies
# currencies_using_exchange maps:
# exchange_currency_id -> list of [
# { "currency_id": original_currency_id, (the currency that was exchanged FROM)
# "exchanged": { field: amount_in_exchange_currency, ... } (the values of original_currency_id converted TO exchange_currency_id)
# }
# ]
currencies_using_exchange = {}
for total in currency_totals:
# Skip empty currencies if ignore_empty is True
if ignore_empty and all(
total[field] == Decimal("0")
for field in [
"expense_current",
"expense_projected",
"income_current",
"income_projected",
]
# --- First Pass: Process transactions from the queryset ---
for total in currency_totals_from_transactions:
if (
ignore_empty
and not deep_search
and all(
total[field] == Decimal("0")
for field in [
"expense_current",
"expense_projected",
"income_current",
"income_projected",
]
)
):
continue
# Calculate derived totals
currency_id = total["account__currency"]
try:
from_currency_obj = Currency.objects.get(id=currency_id)
except Currency.DoesNotExist:
# This should ideally not happen if database is consistent
continue
exchange_currency_for_this_total_id = total[
"account__currency__exchange_currency"
]
exchange_currency_obj_for_this_total = None
if exchange_currency_for_this_total_id:
try:
# Use pre-fetched values if available, otherwise query
exchange_currency_obj_for_this_total = Currency.objects.get(
id=exchange_currency_for_this_total_id
)
except Currency.DoesNotExist:
pass # Exchange currency might not exist or be set
total_current = total["income_current"] - total["expense_current"]
total_projected = total["income_projected"] - total["expense_projected"]
total_final = total_current + total_projected
currency_id = total["account__currency"]
from_currency = Currency.objects.get(id=currency_id)
exchange_currency = (
Currency.objects.get(id=total["account__currency__exchange_currency"])
if total["account__currency__exchange_currency"]
else None
)
currency_data = {
"currency": {
@@ -120,9 +148,16 @@ def calculate_currency_totals(transactions_queryset, ignore_empty=False):
"total_final": total_final,
}
# Add exchanged values if exchange_currency exists
if exchange_currency:
exchanged = {}
if exchange_currency_obj_for_this_total:
exchanged_details = {
"currency": {
"code": exchange_currency_obj_for_this_total.code,
"name": exchange_currency_obj_for_this_total.name,
"decimal_places": exchange_currency_obj_for_this_total.decimal_places,
"prefix": exchange_currency_obj_for_this_total.prefix,
"suffix": exchange_currency_obj_for_this_total.suffix,
}
}
for field in [
"expense_current",
"expense_projected",
@@ -132,50 +167,142 @@ def calculate_currency_totals(transactions_queryset, ignore_empty=False):
"total_projected",
"total_final",
]:
amount, prefix, suffix, decimal_places = convert(
amount=currency_data[field],
from_currency=from_currency,
to_currency=exchange_currency,
amount_to_convert = currency_data[field]
converted_val, _, _, _ = convert(
amount=amount_to_convert,
from_currency=from_currency_obj,
to_currency=exchange_currency_obj_for_this_total,
)
exchanged_details[field] = (
converted_val if converted_val is not None else Decimal("0")
)
if amount is not None:
exchanged[field] = amount
if "currency" not in exchanged:
exchanged["currency"] = {
"prefix": prefix,
"suffix": suffix,
"decimal_places": decimal_places,
"code": exchange_currency.code,
"name": exchange_currency.name,
}
if exchanged:
currency_data["exchanged"] = exchanged
# Track which currencies are using which exchange currencies
if exchange_currency.id not in currencies_using_exchange:
currencies_using_exchange[exchange_currency.id] = []
currencies_using_exchange[exchange_currency.id].append(
{"currency_id": currency_id, "exchanged": exchanged}
)
currency_data["exchanged"] = exchanged_details
if exchange_currency_obj_for_this_total.id not in currencies_using_exchange:
currencies_using_exchange[exchange_currency_obj_for_this_total.id] = []
currencies_using_exchange[exchange_currency_obj_for_this_total.id].append(
{"currency_id": currency_id, "exchanged": exchanged_details}
)
result[currency_id] = currency_data
# Second pass: Add consolidated totals for currencies that are used as exchange currencies
for currency_id, currency_data in result.items():
if currency_id in currencies_using_exchange:
consolidated = {
"currency": currency_data["currency"].copy(),
"expense_current": currency_data["expense_current"],
"expense_projected": currency_data["expense_projected"],
"income_current": currency_data["income_current"],
"income_projected": currency_data["income_projected"],
"total_current": currency_data["total_current"],
"total_projected": currency_data["total_projected"],
"total_final": currency_data["total_final"],
}
# --- Deep Search: Add transaction-less currencies that are exchange targets ---
if deep_search:
# Iteratively add exchange targets that might not have had direct transactions
# Start with known exchange targets from the first pass
queue = list(currencies_using_exchange.keys())
processed_for_deep_add = set(
result.keys()
) # Track currencies already in result or added by this deep search step
# Add exchanged values from all currencies using this as exchange currency
for using_currency in currencies_using_exchange[currency_id]:
exchanged = using_currency["exchanged"]
while queue:
target_id = queue.pop(0)
if target_id in processed_for_deep_add:
continue
processed_for_deep_add.add(target_id)
if (
target_id not in result
): # If this exchange target had no direct transactions
try:
db_currency = Currency.objects.get(id=target_id)
except Currency.DoesNotExist:
continue
# Initialize data for this transaction-less exchange target currency
currency_data_for_db_currency = {
"currency": {
"code": db_currency.code,
"name": db_currency.name,
"decimal_places": db_currency.decimal_places,
"prefix": db_currency.prefix,
"suffix": db_currency.suffix,
},
"expense_current": Decimal("0"),
"expense_projected": Decimal("0"),
"income_current": Decimal("0"),
"income_projected": Decimal("0"),
"total_current": Decimal("0"),
"total_projected": Decimal("0"),
"total_final": Decimal("0"),
}
# If this newly added transaction-less currency ALSO has an exchange_currency set for itself
if db_currency.exchange_currency:
exchanged_details_for_db_currency = {
"currency": {
"code": db_currency.exchange_currency.code,
"name": db_currency.exchange_currency.name,
"decimal_places": db_currency.exchange_currency.decimal_places,
"prefix": db_currency.exchange_currency.prefix,
"suffix": db_currency.exchange_currency.suffix,
}
}
for field in [
"expense_current",
"expense_projected",
"income_current",
"income_projected",
"total_current",
"total_projected",
"total_final",
]:
converted_val, _, _, _ = convert(
Decimal("0"), db_currency, db_currency.exchange_currency
)
exchanged_details_for_db_currency[field] = (
converted_val if converted_val is not None else Decimal("0")
)
currency_data_for_db_currency["exchanged"] = (
exchanged_details_for_db_currency
)
# Ensure its own exchange_currency is registered in currencies_using_exchange
# and add it to the queue if it hasn't been processed yet for deep add.
target_id_for_this_db_curr = db_currency.exchange_currency.id
if target_id_for_this_db_curr not in currencies_using_exchange:
currencies_using_exchange[target_id_for_this_db_curr] = []
# Avoid adding duplicate entries
already_present_in_cue = any(
entry["currency_id"] == db_currency.id
for entry in currencies_using_exchange[
target_id_for_this_db_curr
]
)
if not already_present_in_cue:
currencies_using_exchange[target_id_for_this_db_curr].append(
{
"currency_id": db_currency.id,
"exchanged": exchanged_details_for_db_currency,
}
)
if target_id_for_this_db_curr not in processed_for_deep_add:
queue.append(target_id_for_this_db_curr)
result[db_currency.id] = currency_data_for_db_currency
# --- Second Pass: Calculate consolidated totals for all currencies in result ---
for currency_id_consolidated, data_consolidated_currency in result.items():
consolidated_data = {
"currency": data_consolidated_currency["currency"].copy(),
"expense_current": data_consolidated_currency["expense_current"],
"expense_projected": data_consolidated_currency["expense_projected"],
"income_current": data_consolidated_currency["income_current"],
"income_projected": data_consolidated_currency["income_projected"],
"total_current": data_consolidated_currency["total_current"],
"total_projected": data_consolidated_currency["total_projected"],
"total_final": data_consolidated_currency["total_final"],
}
if currency_id_consolidated in currencies_using_exchange:
for original_currency_info in currencies_using_exchange[
currency_id_consolidated
]:
exchanged_values_from_original = original_currency_info["exchanged"]
for field in [
"expense_current",
"expense_projected",
@@ -185,10 +312,25 @@ def calculate_currency_totals(transactions_queryset, ignore_empty=False):
"total_projected",
"total_final",
]:
if field in exchanged:
consolidated[field] += exchanged[field]
if field in exchanged_values_from_original:
consolidated_data[field] += exchanged_values_from_original[
field
]
result[currency_id]["consolidated"] = consolidated
result[currency_id_consolidated]["consolidated"] = consolidated_data
# Sort currencies by their final_total or consolidated final_total, descending
result = {
k: v
for k, v in sorted(
result.items(),
reverse=True,
key=lambda item: max(
item[1].get("total_final", Decimal("0")),
item[1].get("consolidated", {}).get("total_final", Decimal("0")),
),
)
}
return result

View File

@@ -8,19 +8,21 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-11 15:47+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"PO-Revision-Date: 2025-05-12 14:16+0000\n"
"Last-Translator: Felix <xnovaua@gmail.com>\n"
"Language-Team: Ukrainian <https://translations.herculino.com/projects/"
"wygiwyh/app/uk/>\n"
"Language: uk\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Weblate 5.11.3\n"
#: apps/accounts/forms.py:24
msgid "Group name"
msgstr ""
msgstr "Назва групи"
#: apps/accounts/forms.py:40 apps/accounts/forms.py:98
#: apps/currencies/forms.py:53 apps/currencies/forms.py:91
@@ -32,7 +34,7 @@ msgstr ""
#: apps/transactions/forms.py:751 apps/transactions/forms.py:903
#: apps/users/forms.py:210 apps/users/forms.py:372
msgid "Update"
msgstr ""
msgstr "Оновлення"
#: apps/accounts/forms.py:48 apps/accounts/forms.py:106
#: apps/common/widgets/tom_select.py:13 apps/currencies/forms.py:61
@@ -60,15 +62,15 @@ msgstr ""
#: templates/rules/fragments/list.html:9 templates/tags/fragments/list.html:9
#: templates/users/fragments/list.html:10
msgid "Add"
msgstr ""
msgstr "Додати"
#: apps/accounts/forms.py:57 templates/accounts/fragments/list.html:26
msgid "Group"
msgstr ""
msgstr "Група"
#: apps/accounts/forms.py:115
msgid "New balance"
msgstr ""
msgstr "Новий баланс"
#: apps/accounts/forms.py:121 apps/dca/forms.py:85 apps/dca/forms.py:92
#: apps/insights/forms.py:118 apps/rules/forms.py:174 apps/rules/forms.py:189
@@ -80,7 +82,7 @@ msgstr ""
#: templates/insights/fragments/category_overview/index.html:63
#: templates/insights/fragments/category_overview/index.html:420
msgid "Category"
msgstr ""
msgstr "Категорія"
#: apps/accounts/forms.py:128 apps/dca/forms.py:101 apps/dca/forms.py:109
#: apps/export_app/forms.py:44 apps/export_app/forms.py:135
@@ -94,7 +96,7 @@ msgstr ""
#: templates/insights/fragments/category_overview/index.html:35
#: templates/tags/fragments/list.html:5 templates/tags/pages/index.html:4
msgid "Tags"
msgstr ""
msgstr "Мітки"
#: apps/accounts/models.py:12 apps/accounts/models.py:29 apps/dca/models.py:13
#: apps/import_app/models.py:14 apps/rules/models.py:13
@@ -113,51 +115,54 @@ msgstr ""
#: templates/tags/fragments/table.html:16
#: templates/users/fragments/list.html:29
msgid "Name"
msgstr ""
msgstr "Ім'я"
#: apps/accounts/models.py:18 apps/accounts/models.py:33
msgid "Account Group"
msgstr ""
msgstr "Група рахунків"
#: apps/accounts/models.py:19 templates/account_groups/fragments/list.html:5
#: templates/account_groups/pages/index.html:4
#: templates/includes/navbar.html:118
msgid "Account Groups"
msgstr ""
msgstr "Групи рахунків"
#: apps/accounts/models.py:39 apps/currencies/models.py:39
#: templates/accounts/fragments/list.html:27
msgid "Currency"
msgstr ""
msgstr "Валюта"
#: apps/accounts/models.py:45 apps/currencies/models.py:27
#: templates/accounts/fragments/list.html:28
msgid "Exchange Currency"
msgstr ""
msgstr "Валюта обміну"
#: apps/accounts/models.py:50 apps/currencies/models.py:32
msgid "Default currency for exchange calculations"
msgstr ""
msgstr "Валюта за замовчуванням для обмінних розрахунків"
#: apps/accounts/models.py:55
msgid "Asset account"
msgstr ""
msgstr "Рахунок активів"
#: apps/accounts/models.py:57
msgid ""
"Asset accounts count towards your Net Worth, but not towards your month."
msgstr ""
"Рахунки активів враховуються у вашій чистій вартості, але не у вашому місяці."
#: apps/accounts/models.py:62 templates/accounts/fragments/list.html:30
#: templates/categories/fragments/list.html:24
#: templates/entities/fragments/list.html:24
#: templates/tags/fragments/list.html:24
msgid "Archived"
msgstr ""
msgstr "Архівовано"
#: apps/accounts/models.py:63
msgid "Archived accounts don't show up nor count towards your net worth"
msgstr ""
"Заархівовані рахунки не відображаються і не враховуються у вашій чистій "
"вартості"
#: apps/accounts/models.py:70 apps/rules/forms.py:166 apps/rules/forms.py:179
#: apps/rules/models.py:30 apps/rules/models.py:242
@@ -165,7 +170,7 @@ msgstr ""
#: apps/transactions/forms.py:768 apps/transactions/models.py:285
#: apps/transactions/models.py:455 apps/transactions/models.py:677
msgid "Account"
msgstr ""
msgstr "Рахунок"
#: apps/accounts/models.py:71 apps/export_app/forms.py:20
#: apps/export_app/forms.py:132 apps/transactions/filters.py:53
@@ -176,15 +181,15 @@ msgstr ""
#: templates/transactions/fragments/summary.html:12
#: templates/transactions/pages/transactions.html:72
msgid "Accounts"
msgstr ""
msgstr "Рахунки"
#: apps/accounts/models.py:84
msgid "Exchange currency cannot be the same as the account's main currency."
msgstr ""
msgstr "Валюта обміну не може збігатися з основною валютою рахунку."
#: apps/accounts/views/account_groups.py:44
msgid "Account Group added successfully"
msgstr ""
msgstr "Групу рахунків успішно додано"
#: apps/accounts/views/account_groups.py:69
#: apps/accounts/views/account_groups.py:152 apps/accounts/views/accounts.py:68
@@ -196,188 +201,196 @@ msgstr ""
#: apps/transactions/views/entities.py:171 apps/transactions/views/tags.py:91
#: apps/transactions/views/tags.py:171
msgid "Only the owner can edit this"
msgstr ""
msgstr "Тільки власник може редагувати його"
#: apps/accounts/views/account_groups.py:82
msgid "Account Group updated successfully"
msgstr ""
msgstr "Групу рахунків успішно оновлено"
#: apps/accounts/views/account_groups.py:111
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:105
#: apps/rules/views.py:162 apps/transactions/views/categories.py:168
#: apps/transactions/views/entities.py:130 apps/transactions/views/tags.py:130
msgid "Item no longer shared with you"
msgstr ""
msgstr "Елемент більше не доступний для вас"
#: apps/accounts/views/account_groups.py:114
msgid "Account Group deleted successfully"
msgstr ""
msgstr "Групу рахунків успішно видалено"
#: apps/accounts/views/account_groups.py:135
#: apps/accounts/views/accounts.py:169 apps/dca/views.py:129
#: apps/rules/views.py:187 apps/transactions/views/categories.py:192
#: apps/transactions/views/entities.py:154 apps/transactions/views/tags.py:154
msgid "Ownership taken successfully"
msgstr ""
msgstr "Успішно прийнято право власності"
#: apps/accounts/views/account_groups.py:165
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:159
#: apps/rules/views.py:218 apps/transactions/views/categories.py:142
#: apps/transactions/views/entities.py:184 apps/transactions/views/tags.py:184
msgid "Configuration saved successfully"
msgstr ""
msgstr "Конфігурацію успішно збережено"
#: apps/accounts/views/accounts.py:44
msgid "Account added successfully"
msgstr ""
msgstr "Рахунок додано успішно"
#: apps/accounts/views/accounts.py:81
msgid "Account updated successfully"
msgstr ""
msgstr "Рахунок успішно оновлено"
#: apps/accounts/views/accounts.py:148
msgid "Account deleted successfully"
msgstr ""
msgstr "Рахунок успішно видалено"
#: apps/accounts/views/balance.py:77
msgid "Balance reconciliation"
msgstr ""
msgstr "Звірка балансу"
#: apps/accounts/views/balance.py:85
msgid "Account balances have been reconciled successfully"
msgstr ""
msgstr "Баланси рахунків успішно звірені"
#: apps/api/fields/transactions.py:27
msgid "Category with this ID does not exist."
msgstr ""
msgstr "Категорії з таким ідентифікатором не існує."
#: apps/api/fields/transactions.py:37
msgid "Invalid category data. Provide an ID or name."
msgstr ""
msgstr "Невірні дані категорії. Вкажіть ідентифікатор або ім'я."
#: apps/api/fields/transactions.py:70
msgid "Tag with this ID does not exist."
msgstr ""
msgstr "Мітки з таким ідентифікатором не існує."
#: apps/api/fields/transactions.py:80
msgid "Invalid tag data. Provide an ID or name."
msgstr ""
msgstr "Неправильні дані мітки. Вкажіть ідентифікатор або ім'я."
#: apps/api/fields/transactions.py:105
msgid "Entity with this ID does not exist."
msgstr ""
msgstr "Об'єкт з таким ідентифікатором не існує."
#: apps/api/fields/transactions.py:115
msgid "Invalid entity data. Provide an ID or name."
msgstr ""
msgstr "Невірні дані про об'єкт. Вкажіть ідентифікатор або ім'я."
#: apps/api/serializers/transactions.py:191
msgid "Either 'date' or 'reference_date' must be provided."
msgstr ""
msgstr "Необхідно вказати або 'date', або 'reference_date'."
#: apps/common/fields/forms/dynamic_select.py:71
#: apps/common/fields/forms/dynamic_select.py:143
msgid "Error creating new instance"
msgstr ""
msgstr "Помилка створення нового екземпляра"
#: apps/common/fields/forms/grouped_select.py:24
#: apps/common/widgets/tom_select.py:92 apps/common/widgets/tom_select.py:95
msgid "Ungrouped"
msgstr ""
msgstr "Не згруповані"
#: apps/common/fields/month_year.py:30
msgid "Invalid date format. Use YYYY-MM or YYYY-MM-DD."
msgstr ""
msgstr "Неправильний формат дати. Використовуйте YYYY-MM або YYYY-MM-DD."
#: apps/common/fields/month_year.py:59
msgid "Invalid date format. Use YYYY-MM."
msgstr ""
msgstr "Неправильний формат дати. Використовуйте YYYY-MM."
#: apps/common/forms.py:25
msgid "Owner"
msgstr ""
msgstr "Власник"
#: apps/common/forms.py:28
msgid ""
"The owner of this object, if empty all users can see, edit and take "
"ownership."
msgstr ""
"Власник цього об'єкта, якщо він порожній, всі користувачі можуть бачити, "
"редагувати і набувати права власності."
#: apps/common/forms.py:35
msgid "Shared with users"
msgstr ""
msgstr "Поділитися з користувачами"
#: apps/common/forms.py:36
msgid "Select users to share this object with"
msgstr ""
msgstr "Виберіть користувачів, яким буде надано доступ до цього об'єкта"
#: apps/common/forms.py:41
msgid "Visibility"
msgstr ""
msgstr "Видимість"
#: apps/common/forms.py:43
msgid ""
"Private: Only shown for the owner and shared users. Only editable by the "
"owner.<br/>Public: Shown for all users. Only editable by the owner."
msgstr ""
"Private: Відображається лише для власника та користувачів із загальним "
"доступом. Редагувати може лише власник.<br/> Public: Відображається для всіх "
"користувачів. Редагувати може лише власник."
#: apps/common/forms.py:80 apps/users/forms.py:135
msgid "Save"
msgstr ""
msgstr "Зберегти"
#: apps/common/forms.py:95
msgid "You cannot share this item with its owner."
msgstr ""
msgstr "Ви не можете поділитися цією річчю з її власником."
#: apps/common/models.py:29
msgid "Private"
msgstr ""
msgstr "Private"
#: apps/common/models.py:30
msgid "Public"
msgstr ""
msgstr "Public"
#: apps/common/templatetags/natural.py:20
#: templates/monthly_overview/fragments/monthly_summary.html:9
msgid "today"
msgstr ""
msgstr "сьогодні"
#: apps/common/templatetags/natural.py:22
msgid "tomorrow"
msgstr ""
msgstr "завтра"
#: apps/common/templatetags/natural.py:24
msgid "yesterday"
msgstr ""
msgstr "вчора"
#: apps/common/templatetags/natural.py:26
msgid "last 7 days"
msgstr ""
msgstr "останні 7 днів"
#: apps/common/templatetags/natural.py:28
msgid "in the next 7 days"
msgstr ""
msgstr "у наступні 7 днів"
#: apps/common/templatetags/natural.py:31
#, python-format
msgid "%(years)s year ago"
msgid_plural "%(years)s years ago"
msgstr[0] ""
msgstr[1] ""
msgstr[0] "%(years)s рік тому"
msgstr[1] "%(years)s роки тому"
msgstr[2] "%(years)s років тому"
#: apps/common/templatetags/natural.py:37
#, python-format
msgid "%(months)s month ago"
msgid_plural "%(months)s months ago"
msgstr[0] ""
msgstr[1] ""
msgstr[0] "%(months)s місяць тому"
msgstr[1] "%(months)s місяці тому"
msgstr[2] "%(months)s місяців тому"
#: apps/common/templatetags/natural.py:41
#, python-format
msgid "%(weeks)s week ago"
msgid_plural "%(weeks)s weeks ago"
msgstr[0] ""
msgstr[1] ""
msgstr[0] "%(weeks)s тиждень тому"
msgstr[1] "%(weeks)s тижня тому"
msgstr[2] "%(weeks)s тижнів тому"
#: apps/common/templatetags/natural.py:46
#, python-format
@@ -385,6 +398,7 @@ msgid "in %(years)s year"
msgid_plural "in %(years)s years"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#: apps/common/templatetags/natural.py:51
#, python-format
@@ -402,55 +416,55 @@ msgstr[1] ""
#: apps/common/templatetags/toast_bg.py:34
msgid "Success"
msgstr ""
msgstr "Успіх"
#: apps/common/templatetags/toast_bg.py:36
msgid "Warning"
msgstr ""
msgstr "Попередження"
#: apps/common/templatetags/toast_bg.py:38
msgid "Error"
msgstr ""
msgstr "Помилка"
#: apps/common/templatetags/toast_bg.py:40
msgid "Info"
msgstr ""
msgstr "Info"
#: apps/common/views.py:111
msgid "Cache cleared successfully"
msgstr ""
msgstr "Кеш успішно очищено"
#: apps/common/widgets/datepicker.py:53 apps/common/widgets/datepicker.py:206
#: apps/common/widgets/datepicker.py:264
msgid "Today"
msgstr ""
msgstr "Сьогодні"
#: apps/common/widgets/datepicker.py:139
msgid "Now"
msgstr ""
msgstr "Зараз"
#: apps/common/widgets/tom_select.py:11
msgid "Remove"
msgstr ""
msgstr "Видалити"
#: apps/common/widgets/tom_select.py:15
#: templates/mini_tools/unit_price_calculator.html:174
#: templates/monthly_overview/pages/overview.html:172
#: templates/transactions/pages/transactions.html:17
msgid "Clear"
msgstr ""
msgstr "Чисто"
#: apps/common/widgets/tom_select.py:16
msgid "No results..."
msgstr ""
msgstr "Жодних результатів..."
#: apps/currencies/forms.py:17 apps/currencies/models.py:22
msgid "Prefix"
msgstr ""
msgstr "Префікс"
#: apps/currencies/forms.py:18 apps/currencies/models.py:23
msgid "Suffix"
msgstr ""
msgstr "Суфікс"
#: apps/currencies/forms.py:69 apps/dca/models.py:158 apps/rules/forms.py:169
#: apps/rules/forms.py:182 apps/rules/models.py:33 apps/rules/models.py:254
@@ -460,19 +474,19 @@ msgstr ""
#: templates/exchange_rates/fragments/table.html:10
#: templates/exchange_rates_services/fragments/table.html:10
msgid "Date"
msgstr ""
msgstr "Дата"
#: apps/currencies/models.py:14
msgid "Currency Code"
msgstr ""
msgstr "Код валюти"
#: apps/currencies/models.py:16
msgid "Currency Name"
msgstr ""
msgstr "Назва валюти"
#: apps/currencies/models.py:20
msgid "Decimal Places"
msgstr ""
msgstr "Десяткові знаки"
#: apps/currencies/models.py:40 apps/export_app/forms.py:26
#: apps/export_app/forms.py:133 apps/transactions/filters.py:60
@@ -483,58 +497,58 @@ msgstr ""
#: templates/transactions/fragments/summary.html:8
#: templates/transactions/pages/transactions.html:59
msgid "Currencies"
msgstr ""
msgstr "Валюти"
#: apps/currencies/models.py:49
msgid "Currency cannot have itself as exchange currency."
msgstr ""
msgstr "Валюта не може бути валютою обміну."
#: apps/currencies/models.py:60
msgid "From Currency"
msgstr ""
msgstr "З валюти"
#: apps/currencies/models.py:66
msgid "To Currency"
msgstr ""
msgstr "У валюту"
#: apps/currencies/models.py:69 apps/currencies/models.py:74
msgid "Exchange Rate"
msgstr ""
msgstr "Обмінний курс"
#: apps/currencies/models.py:71
msgid "Date and Time"
msgstr ""
msgstr "Дата і час"
#: apps/currencies/models.py:75 apps/export_app/forms.py:68
#: apps/export_app/forms.py:145 templates/exchange_rates/fragments/list.html:6
#: templates/exchange_rates/pages/index.html:4
#: templates/includes/navbar.html:126
msgid "Exchange Rates"
msgstr ""
msgstr "Обмінні курси"
#: apps/currencies/models.py:87
msgid "From and To currencies cannot be the same."
msgstr ""
msgstr "Валюти «Від» і «До» не можуть бути однаковими."
#: apps/currencies/models.py:102
msgid "On"
msgstr ""
msgstr "On"
#: apps/currencies/models.py:103
msgid "Every X hours"
msgstr ""
msgstr "Кожні Х годин"
#: apps/currencies/models.py:104
msgid "Not on"
msgstr ""
msgstr "Not on"
#: apps/currencies/models.py:106
msgid "Service Name"
msgstr ""
msgstr "Назва сервісу"
#: apps/currencies/models.py:108
msgid "Service Type"
msgstr ""
msgstr "Тип сервісу"
#: apps/currencies/models.py:110 apps/transactions/models.py:209
#: apps/transactions/models.py:233 apps/transactions/models.py:257
@@ -543,27 +557,27 @@ msgstr ""
#: templates/recurring_transactions/fragments/list.html:21
#: templates/tags/fragments/list.html:21 templates/users/fragments/list.html:28
msgid "Active"
msgstr ""
msgstr "Активний"
#: apps/currencies/models.py:115
msgid "API Key"
msgstr ""
msgstr "Ключ API"
#: apps/currencies/models.py:116
msgid "API key for the service (if required)"
msgstr ""
msgstr "API-ключ для сервісу (якщо потрібно)"
#: apps/currencies/models.py:121
msgid "Interval Type"
msgstr ""
msgstr "Тип інтервалу"
#: apps/currencies/models.py:125
msgid "Interval"
msgstr ""
msgstr "Інтервал"
#: apps/currencies/models.py:128
msgid "Last Successful Fetch"
msgstr ""
msgstr "Остання успішна вибірка"
#: apps/currencies/models.py:133
msgid "Target Currencies"

View File

@@ -46,7 +46,7 @@
color="grey"></c-amount.display>
</div>
{% endif %}
{% if currency.consolidated %}
{% if currency.consolidated and currency.consolidated.total_final != currency.total_final %}
<div class="d-flex align-items-baseline w-100">
<div class="account-name text-start font-monospace tw-text-gray-300">
<span class="hierarchy-line-icon"></span>{% trans 'Consolidated' %}</div>
@@ -57,7 +57,7 @@
:prefix="currency.consolidated.currency.prefix"
:suffix="currency.consolidated.currency.suffix"
:decimal_places="currency.consolidated.currency.decimal_places"
color="{% if currency.total_final > 0 %}green{% elif currency.total_final < 0 %}red{% endif %}"
color="{% if currency.consolidated.total_final > 0 %}green{% elif currency.consolidated.total_final < 0 %}red{% endif %}"
text-end></c-amount.display>
</div>
</div>