From 9dce5e9efe9b092f75260ecd4724f256d1c3c236 Mon Sep 17 00:00:00 2001 From: Herculino Trotta Date: Sat, 20 Sep 2025 11:02:23 -0300 Subject: [PATCH] feat(networth): add a chart with the currency difference between each month --- .../net_worth/utils/calculate_net_worth.py | 26 + app/apps/net_worth/views.py | 34 + app/templates/cotton/ui/info_card.html | 2 +- app/templates/net_worth/net_worth.html | 700 ++++++++++-------- 4 files changed, 453 insertions(+), 309 deletions(-) diff --git a/app/apps/net_worth/utils/calculate_net_worth.py b/app/apps/net_worth/utils/calculate_net_worth.py index 7dce4a4..dbaf161 100644 --- a/app/apps/net_worth/utils/calculate_net_worth.py +++ b/app/apps/net_worth/utils/calculate_net_worth.py @@ -182,3 +182,29 @@ def calculate_historical_account_balance(queryset): historical_account_balance[date_filter(end_date, "b Y")] = month_data return historical_account_balance + + +def calculate_monthly_net_worth_difference(historical_net_worth): + diff_dict = OrderedDict() + if not historical_net_worth: + return diff_dict + + # Get all currencies + currencies = set() + for data in historical_net_worth.values(): + currencies.update(data.keys()) + + # Initialize prev_values for all currencies + prev_values = {currency: Decimal("0.00") for currency in currencies} + + for month, values in historical_net_worth.items(): + diff_values = {} + for currency in sorted(list(currencies)): + current_val = values.get(currency, Decimal("0.00")) + prev_val = prev_values.get(currency, Decimal("0.00")) + diff_values[currency] = current_val - prev_val + + diff_dict[month] = diff_values + prev_values = values.copy() + + return diff_dict diff --git a/app/apps/net_worth/views.py b/app/apps/net_worth/views.py index a2e6d43..57687d7 100644 --- a/app/apps/net_worth/views.py +++ b/app/apps/net_worth/views.py @@ -8,6 +8,7 @@ from django.views.decorators.http import require_http_methods from apps.net_worth.utils.calculate_net_worth import ( calculate_historical_currency_net_worth, calculate_historical_account_balance, + calculate_monthly_net_worth_difference, ) from apps.transactions.models import Transaction from apps.transactions.utils.calculations import ( @@ -96,6 +97,38 @@ def net_worth(request): chart_data_currency_json = json.dumps(chart_data_currency, cls=DjangoJSONEncoder) + monthly_difference_data = calculate_monthly_net_worth_difference( + historical_net_worth=historical_currency_net_worth + ) + + diff_labels = ( + list(monthly_difference_data.keys()) if monthly_difference_data else [] + ) + diff_currencies = ( + list(monthly_difference_data[diff_labels[0]].keys()) + if monthly_difference_data and diff_labels + else [] + ) + + diff_datasets = [] + for i, currency in enumerate(diff_currencies): + data = [ + float(month_data.get(currency, 0)) + for month_data in monthly_difference_data.values() + ] + diff_datasets.append( + { + "label": currency, + "data": data, + "borderWidth": 3, + } + ) + + chart_data_monthly_difference = {"labels": diff_labels, "datasets": diff_datasets} + chart_data_monthly_difference_json = json.dumps( + chart_data_monthly_difference, cls=DjangoJSONEncoder + ) + historical_account_balance = calculate_historical_account_balance( queryset=transactions_account_queryset ) @@ -140,6 +173,7 @@ def net_worth(request): "chart_data_accounts_json": chart_data_accounts_json, "accounts": accounts, "type": view_type, + "chart_data_monthly_difference_json": chart_data_monthly_difference_json, }, ) diff --git a/app/templates/cotton/ui/info_card.html b/app/templates/cotton/ui/info_card.html index d6f55ff..0831fb7 100644 --- a/app/templates/cotton/ui/info_card.html +++ b/app/templates/cotton/ui/info_card.html @@ -3,7 +3,7 @@ {% if icon %}{% else %}{{ title.0 }}{% endif %}
-
{{ title }}{% if help_text %}{% endif %}
+
{{ title }}{% if help_text %}{% endif %}
{{ slot }}
diff --git a/app/templates/net_worth/net_worth.html b/app/templates/net_worth/net_worth.html index 5353188..fde3280 100644 --- a/app/templates/net_worth/net_worth.html +++ b/app/templates/net_worth/net_worth.html @@ -9,338 +9,422 @@ {% block title %}{% if type == "current" %}{% translate 'Current Net Worth' %}{% else %}{% translate 'Projected Net Worth' %}{% endif %}{% endblock %} {% block content %} -
-
-
- - +
+
+
+ + - - + + +
-
-
-
-
-
-
- - {% for currency in currency_net_worth.values %} -
-
-
- {{ currency.currency.name }} +
+
+
+
+
+ + {% for currency in currency_net_worth.values %} +
+
+
+ {{ currency.currency.name }} +
+
+
+ +
-
+
+ {% if currency.exchanged and currency.exchanged.total_final %}
+ :amount="currency.exchanged.total_final" + :prefix="currency.exchanged.currency.prefix" + :suffix="currency.exchanged.currency.suffix" + :decimal_places="currency.exchanged.currency.decimal_places" + text-end + color="grey">
-
-
- {% if currency.exchanged and currency.exchanged.total_final %} -
- -
- {% endif %} - {% if currency.consolidated and currency.consolidated.total_final != currency.total_final %} -
- -
-
- + +
+
+ -
- {% endif %} - {% endfor %} - +
+ {% endif %} + {% endfor %} + +
+
+
+
+ +
+
+
+ +
+
+
+
+ +
+
-
-
- -
-
-
-
-
-
-
-
- - {% regroup account_net_worth.values by account.group as account_data %} - {% for data in account_data %} - {% if data.grouper %} -
-
-
+
+
+
+
+
+ + {% regroup account_net_worth.values by account.group as account_data %} + {% for data in account_data %} + {% if data.grouper %} +
+
+
{{ data.grouper }}
-
-
- {% for account in data.list %} -
-
- -
-
- -
- {% if account.exchanged and account.exchanged.total_final %} - - {% endif %} - {% endfor %} - {% else %} - {% for account in data.list %} -
-
- -
-
- + {% for account in data.list %} +
+
+ +
+
+ +
-
- {% if account.exchanged and account.exchanged.total_final %} - - {% endif %} - {% endfor %} - {% endif %} - {% endfor %} - + {% if account.exchanged and account.exchanged.total_final %} + + {% endif %} + {% endfor %} + {% else %} + {% for account in data.list %} +
+
+ +
+
+ +
+
+
+ {% if account.exchanged and account.exchanged.total_final %} + + {% endif %} + {% endfor %} + {% endif %} + {% endfor %} + +
+
+
+
+
+
-
-
- -
-
+ + + + + + + +
- - - - - - -
- + {% endblock %}