From 2f94dc299c4bf4516eb97d9b8e859e56a08d3918 Mon Sep 17 00:00:00 2001 From: Herculino Trotta Date: Sun, 17 Nov 2024 01:30:07 -0300 Subject: [PATCH] refactor: improve monthly_summary calculations --- app/apps/monthly_overview/views.py | 169 ++----------- .../fragments/monthly_summary.html | 229 ++++++++++++------ 2 files changed, 170 insertions(+), 228 deletions(-) diff --git a/app/apps/monthly_overview/views.py b/app/apps/monthly_overview/views.py index 45f5b89..489ff6d 100644 --- a/app/apps/monthly_overview/views.py +++ b/app/apps/monthly_overview/views.py @@ -1,8 +1,5 @@ -from decimal import Decimal - from django.contrib.auth.decorators import login_required from django.db.models import ( - Sum, Q, ) from django.shortcuts import render, redirect @@ -10,9 +7,13 @@ from django.utils import timezone from django.views.decorators.http import require_http_methods from apps.common.decorators.htmx import only_htmx -from apps.common.functions.dates import remaining_days_in_month +from apps.common.utils.dicts import remove_falsey_entries +from apps.monthly_overview.utils.daily_spending_allowance import ( + calculate_daily_allowance_currency, +) from apps.transactions.filters import TransactionsFilter from apps.transactions.models import Transaction +from apps.transactions.utils.calculations import calculate_currency_totals from apps.transactions.utils.default_ordering import default_order @@ -91,163 +92,27 @@ def transactions_list(request, month: int, year: int): @login_required @require_http_methods(["GET"]) def monthly_summary(request, month: int, year: int): - # Helper function to calculate sums for different transaction types - def calculate_sum(transaction_type, is_paid): - return ( - base_queryset.filter(type=transaction_type, is_paid=is_paid) - .values( - "account__currency__name", - "account__currency__suffix", - "account__currency__prefix", - "account__currency__decimal_places", - ) - .annotate(total=Sum("amount")) - .order_by("account__currency__name") - ) - - # Helper function to format currency sums - def format_currency_sum(queryset): - return [ - { - "currency": item["account__currency__name"], - "suffix": item["account__currency__suffix"], - "prefix": item["account__currency__prefix"], - "decimal_places": item["account__currency__decimal_places"], - "amount": item["total"], - } - for item in queryset - ] - - # Calculate totals - def calculate_total(income, expenses): - totals = {} - - # Process income - for item in income: - currency = item["account__currency__name"] - totals[currency] = totals.get(currency, Decimal("0")) + item["total"] - - # Subtract expenses - for item in expenses: - currency = item["account__currency__name"] - totals[currency] = totals.get(currency, Decimal("0")) - item["total"] - - return [ - { - "currency": currency, - "suffix": next( - ( - item["account__currency__suffix"] - for item in list(income) + list(expenses) - if item["account__currency__name"] == currency - ), - "", - ), - "prefix": next( - ( - item["account__currency__prefix"] - for item in list(income) + list(expenses) - if item["account__currency__name"] == currency - ), - "", - ), - "decimal_places": next( - ( - item["account__currency__decimal_places"] - for item in list(income) + list(expenses) - if item["account__currency__name"] == currency - ), - 2, - ), - "amount": amount, - } - for currency, amount in totals.items() - ] - - # Calculate total final - def sum_totals(total1, total2): - totals = {} - for item in total1 + total2: - currency = item["currency"] - totals[currency] = totals.get(currency, Decimal("0")) + item["amount"] - return [ - { - "currency": currency, - "suffix": next( - item["suffix"] - for item in total1 + total2 - if item["currency"] == currency - ), - "prefix": next( - item["prefix"] - for item in total1 + total2 - if item["currency"] == currency - ), - "decimal_places": next( - item["decimal_places"] - for item in total1 + total2 - if item["currency"] == currency - ), - "amount": amount, - } - for currency, amount in totals.items() - ] - # Base queryset with all required filters base_queryset = Transaction.objects.filter( reference_date__year=year, reference_date__month=month, account__is_asset=False ).exclude(Q(category__mute=True) & ~Q(category=None)) - # Calculate sums for different transaction types - paid_income = calculate_sum(Transaction.Type.INCOME, True) - projected_income = calculate_sum(Transaction.Type.INCOME, False) - paid_expenses = calculate_sum(Transaction.Type.EXPENSE, True) - projected_expenses = calculate_sum(Transaction.Type.EXPENSE, False) + data = calculate_currency_totals(base_queryset, ignore_empty=True) - total_current = calculate_total(paid_income, paid_expenses) - total_projected = calculate_total(projected_income, projected_expenses) - - total_final = sum_totals(total_current, total_projected) - - # Calculate daily spending allowance - remaining_days = remaining_days_in_month( - month=month, year=year, current_date=timezone.localdate(timezone.now()) - ) - if ( - timezone.localdate(timezone.now()).month == month - and timezone.localdate(timezone.now()).year == year - ): - daily_spending_allowance = [ - { - "currency": item["currency"], - "suffix": item["suffix"], - "prefix": item["prefix"], - "decimal_places": item["decimal_places"], - "amount": ( - amount - if (amount := item["amount"] / remaining_days) > 0 - else Decimal("0") - ), - } - for item in total_final - ] - else: - daily_spending_allowance = [] - - # Construct the response dictionary - data = { - "paid_income": format_currency_sum(paid_income), - "projected_income": format_currency_sum(projected_income), - "paid_expenses": format_currency_sum(paid_expenses), - "projected_expenses": format_currency_sum(projected_expenses), - "total_current": total_current, - "total_projected": total_projected, - "total_final": total_final, - "daily_spending_allowance": daily_spending_allowance, + context = { + "income_current": remove_falsey_entries(data, "income_current"), + "income_projected": remove_falsey_entries(data, "income_projected"), + "expense_current": remove_falsey_entries(data, "expense_current"), + "total_current": remove_falsey_entries(data, "total_current"), + "total_final": remove_falsey_entries(data, "total_final"), + "total_projected": remove_falsey_entries(data, "total_projected"), + "daily_spending_allowance": calculate_daily_allowance_currency( + currency_totals=data, month=month, year=year + ), } return render( request, "monthly_overview/fragments/monthly_summary.html", - context={"totals": data}, + context=context, ) diff --git a/app/templates/monthly_overview/fragments/monthly_summary.html b/app/templates/monthly_overview/fragments/monthly_summary.html index 8684e42..72df630 100644 --- a/app/templates/monthly_overview/fragments/monthly_summary.html +++ b/app/templates/monthly_overview/fragments/monthly_summary.html @@ -15,13 +15,24 @@
{% translate 'today' %}
- {% for entry in totals.daily_spending_allowance %} - + {% for currency in daily_spending_allowance.values %} +
+ +
+ {% if currency.exchanged %} +
+ +
+ {% endif %} {% empty %}
-
{% endfor %} @@ -44,13 +55,25 @@
{% translate 'current' %}
- {% for entry in totals.paid_income %} - + {% for currency in income_current.values %} +
+ +
+ {% if currency.exchanged %} +
+ +
+ {% endif %} {% empty %}
-
{% endfor %} @@ -62,13 +85,25 @@
{% translate 'projected' %}
- {% for entry in totals.projected_income %} - + {% for currency in income_projected.values %} +
+ +
+ {% if currency.exchanged %} +
+ +
+ {% endif %} {% empty %}
-
{% endfor %} @@ -85,19 +120,31 @@
-
{% translate 'Expenses' %}
+
{% translate 'Expenses' %}
{% translate 'current' %}
- {% for entry in totals.paid_expenses %} - + {% for currency in expense_current.values %} +
+ +
+ {% if currency.exchanged %} +
+ +
+ {% endif %} {% empty %}
-
{% endfor %} @@ -109,13 +156,25 @@
{% translate 'projected' %}
- {% for entry in totals.projected_expenses %} - + {% for currency in expense_projected.values %} +
+ +
+ {% if currency.exchanged %} +
+ +
+ {% endif %} {% empty %}
-
{% endfor %} @@ -132,19 +191,31 @@
-
{% translate 'Total' %}
+
{% translate 'Total' %}
{% translate 'current' %}
- {% for entry in totals.total_current %} - + {% for currency in total_current.values %} +
+ +
+ {% if currency.exchanged %} +
+ +
+ {% endif %} {% empty %}
-
{% endfor %} @@ -155,13 +226,25 @@
{% translate 'projected' %}
- {% for entry in totals.total_projected %} - + {% for currency in total_projected.values %} +
+ +
+ {% if currency.exchanged %} +
+ +
+ {% endif %} {% empty %}
-
{% endfor %} @@ -170,13 +253,25 @@
- {% for entry in totals.total_final %} - + {% for currency in total_final.values %} +
+ +
+ {% if currency.exchanged %} +
+ +
+ {% endif %} {% empty %}
-
{% endfor %} @@ -186,21 +281,3 @@
-{#
#} -{#

#} -{# {% translate "Account Overview" %}

#} -{#
#} -{#
#} -{# {% for account in account_summary %}#} -{#
#} -{#
#} -{#
{{ account.name }}
#} -{#
#} -{#
#} -{#
#} -{#
#} -{#
#} -{#
#} -{# {% endfor %}#} -{#
#} -{#
#}