feat: improve calculations for net_worth

This commit is contained in:
Herculino Trotta
2024-11-17 11:36:32 -03:00
parent e5e002497f
commit 84bb247918
3 changed files with 56 additions and 169 deletions

View File

@@ -19,133 +19,6 @@ from apps.currencies.utils.convert import convert
from apps.transactions.models import Transaction
def calculate_account_net_worth():
ungrouped_id = None # Special ID for ungrouped accounts
# Subquery to calculate balance for each account
balance_subquery = (
Transaction.objects.filter(account=OuterRef("pk"), is_paid=True)
.values("account")
.annotate(
balance=Sum(
Case(
When(type=Transaction.Type.INCOME, then=F("amount")),
When(type=Transaction.Type.EXPENSE, then=-F("amount")),
default=0,
output_field=DecimalField(),
)
)
)
.values("balance")
)
# Main query to fetch all account data
accounts_data = (
Account.objects.filter(is_archived=False)
.annotate(balance=Coalesce(Subquery(balance_subquery), Decimal("0")))
.select_related("currency", "exchange_currency", "group")
)
account_net_worth = {ungrouped_id: {"name": _("Ungrouped"), "accounts": {}}}
for account in accounts_data:
account_data = {
"name": account.name,
"balance": account.balance,
"currency": {
"code": account.currency.code,
"name": account.currency.name,
"prefix": account.currency.prefix,
"suffix": account.currency.suffix,
"decimal_places": account.currency.decimal_places,
},
}
if account.exchange_currency:
converted_amount, prefix, suffix, decimal_places = convert(
amount=account.balance,
from_currency=account.currency,
to_currency=account.exchange_currency,
)
if converted_amount:
account_data["exchange"] = {
"amount": converted_amount,
"prefix": prefix,
"suffix": suffix,
"decimal_places": decimal_places,
}
group_id = account.group.id if account.group else ungrouped_id
group_name = account.group.name if account.group else _("Ungrouped")
if group_id not in account_net_worth:
account_net_worth[group_id] = {"name": group_name, "accounts": {}}
account_net_worth[group_id]["accounts"][account.id] = account_data
# Remove the "Ungrouped" category if it's empty
if not account_net_worth[ungrouped_id]["accounts"]:
del account_net_worth[ungrouped_id]
return account_net_worth
def calculate_currency_net_worth():
# Subquery to calculate balance for each currency
balance_subquery = (
Transaction.objects.filter(account__currency=OuterRef("pk"), is_paid=True)
.values("account__currency")
.annotate(
balance=Sum(
Case(
When(type=Transaction.Type.INCOME, then=F("amount")),
When(type=Transaction.Type.EXPENSE, then=-F("amount")),
default=0,
output_field=DecimalField(),
)
)
)
.values("balance")
)
# Fetch all currencies with their balances in a single query
currencies_data = Currency.objects.annotate(
balance=Coalesce(Subquery(balance_subquery), Decimal("0"))
).select_related(
"exchange_currency"
) # Optimize by pre-fetching exchange_currency
net_worth = {}
for currency in currencies_data:
# Skip conversion if no exchange currency is set
exchanged_value = None
if currency.exchange_currency:
exchanged_amount, ex_prefix, ex_suffix, ex_decimal_places = convert(
amount=currency.balance,
from_currency=currency,
to_currency=currency.exchange_currency,
)
exchanged_value = {
"amount": exchanged_amount,
"name": currency.exchange_currency.name,
"prefix": ex_prefix,
"suffix": ex_suffix,
"decimal_places": ex_decimal_places,
}
net_worth[currency.name] = {
"amount": currency.balance,
"code": currency.code,
"name": currency.name,
"prefix": currency.prefix,
"suffix": currency.suffix,
"decimal_places": currency.decimal_places,
"exchanged": exchanged_value,
}
return net_worth
def calculate_historical_currency_net_worth():
# Get all currencies and date range in a single query
aggregates = Transaction.objects.aggregate(

View File

@@ -9,11 +9,24 @@ from apps.net_worth.utils.calculate_net_worth import (
calculate_account_net_worth,
calculate_historical_account_balance,
)
from apps.transactions.models import Transaction
from apps.transactions.utils.calculations import (
calculate_currency_totals,
calculate_account_totals,
)
def net_worth_main(request):
currency_net_worth = calculate_currency_net_worth()
account_net_worth = calculate_account_net_worth()
transactions_queryset = Transaction.objects.filter(is_paid=True).order_by(
"account__group",
"account__name",
)
currency_net_worth = calculate_currency_totals(
transactions_queryset=transactions_queryset
)
account_net_worth = calculate_account_totals(
transactions_queryset=transactions_queryset
)
historical_currency_net_worth = calculate_historical_currency_net_worth()
@@ -83,7 +96,7 @@ def net_worth_main(request):
request,
"net_worth/net_worth.html",
{
"currency_net_worth": currency_net_worth.values(),
"currency_net_worth": currency_net_worth,
"account_net_worth": account_net_worth,
"chart_data_currency_json": chart_data_currency_json,
"currencies": currencies,