diff --git a/app/apps/net_worth/urls.py b/app/apps/net_worth/urls.py index 424012c..10ab2c4 100644 --- a/app/apps/net_worth/urls.py +++ b/app/apps/net_worth/urls.py @@ -3,5 +3,6 @@ from django.urls import path from . import views urlpatterns = [ - path("net-worth/", views.net_worth_main, name="net_worth"), + path("net-worth/current/", views.net_worth_current, name="net_worth_current"), + path("net-worth/projected/", views.net_worth_projected, name="net_worth_projected"), ] diff --git a/app/apps/net_worth/utils/calculate_net_worth.py b/app/apps/net_worth/utils/calculate_net_worth.py index 6ab9bbf..a0ba24b 100644 --- a/app/apps/net_worth/utils/calculate_net_worth.py +++ b/app/apps/net_worth/utils/calculate_net_worth.py @@ -19,7 +19,9 @@ from apps.currencies.utils.convert import convert from apps.transactions.models import Transaction -def calculate_historical_currency_net_worth(): +def calculate_historical_currency_net_worth(is_paid=True): + transactions_params = {**{k: v for k, v in [("is_paid", True)] if is_paid}} + # Get all currencies and date range in a single query aggregates = Transaction.objects.aggregate( min_date=Min("reference_date"), @@ -39,7 +41,7 @@ def calculate_historical_currency_net_worth(): # Calculate cumulative balances for each account, currency, and month cumulative_balances = ( - Transaction.objects.filter(is_paid=True) + Transaction.objects.filter(**transactions_params) .annotate(month=TruncMonth("reference_date")) .values("account__currency__name", "month") .annotate( @@ -99,12 +101,13 @@ def calculate_historical_currency_net_worth(): return historical_net_worth -def calculate_historical_account_balance(): +def calculate_historical_account_balance(is_paid=True): + transactions_params = {**{k: v for k, v in [("is_paid", True)] if is_paid}} # Get all accounts accounts = Account.objects.filter(is_archived=False) # Get the date range - date_range = Transaction.objects.filter(is_paid=True).aggregate( + date_range = Transaction.objects.filter(**transactions_params).aggregate( min_date=Min("reference_date"), max_date=Max("reference_date") ) @@ -120,7 +123,7 @@ def calculate_historical_account_balance(): # Calculate balances for each account and month balances = ( - Transaction.objects.filter(is_paid=True) + Transaction.objects.filter(**transactions_params) .annotate(month=TruncMonth("reference_date")) .values("account", "month") .annotate( diff --git a/app/apps/net_worth/views.py b/app/apps/net_worth/views.py index 308a022..0ac725c 100644 --- a/app/apps/net_worth/views.py +++ b/app/apps/net_worth/views.py @@ -14,7 +14,7 @@ from apps.transactions.utils.calculations import ( ) -def net_worth_main(request): +def net_worth_current(request): transactions_currency_queryset = Transaction.objects.filter( is_paid=True, account__is_archived=False ).order_by( @@ -110,3 +110,103 @@ def net_worth_main(request): "accounts": accounts, }, ) + + +def net_worth_projected(request): + transactions_currency_queryset = Transaction.objects.filter( + account__is_archived=False + ).order_by( + "account__currency__name", + ) + transactions_account_queryset = Transaction.objects.filter( + account__is_archived=False + ).order_by( + "account__group__name", + "account__name", + ) + + currency_net_worth = calculate_currency_totals( + transactions_queryset=transactions_currency_queryset + ) + account_net_worth = calculate_account_totals( + transactions_queryset=transactions_account_queryset + ) + + historical_currency_net_worth = calculate_historical_currency_net_worth( + is_paid=False + ) + + labels = ( + list(historical_currency_net_worth.keys()) + if historical_currency_net_worth + else [] + ) + currencies = ( + list(historical_currency_net_worth[labels[0]].keys()) + if historical_currency_net_worth + else [] + ) + + datasets = [] + for i, currency in enumerate(currencies): + data = [ + float(month_data[currency]) + for month_data in historical_currency_net_worth.values() + ] + datasets.append( + { + "label": currency, + "data": data, + "yAxisID": f"y{i}", + "fill": False, + "tension": 0.1, + } + ) + + chart_data_currency = {"labels": labels, "datasets": datasets} + + chart_data_currency_json = json.dumps(chart_data_currency, cls=DjangoJSONEncoder) + + historical_account_balance = calculate_historical_account_balance(is_paid=False) + + labels = ( + list(historical_account_balance.keys()) if historical_account_balance else [] + ) + accounts = ( + list(historical_account_balance[labels[0]].keys()) + if historical_account_balance + else [] + ) + + datasets = [] + for i, account in enumerate(accounts): + data = [ + float(month_data[account]) + for month_data in historical_account_balance.values() + ] + datasets.append( + { + "label": account, + "data": data, + "fill": False, + "tension": 0.1, + "yAxisID": f"y-axis-{i}", # Assign each dataset to its own Y-axis + } + ) + + chart_data_accounts = {"labels": labels, "datasets": datasets} + + chart_data_accounts_json = json.dumps(chart_data_accounts, cls=DjangoJSONEncoder) + + return render( + request, + "net_worth/net_worth.html", + { + "currency_net_worth": currency_net_worth, + "account_net_worth": account_net_worth, + "chart_data_currency_json": chart_data_currency_json, + "currencies": currencies, + "chart_data_accounts_json": chart_data_accounts_json, + "accounts": accounts, + }, + ) diff --git a/app/templates/includes/navbar.html b/app/templates/includes/navbar.html index 79620d8..229ab5f 100644 --- a/app/templates/includes/navbar.html +++ b/app/templates/includes/navbar.html @@ -31,11 +31,19 @@ href="{% url 'calendar_index' %}">{% translate 'Calendar' %} -