diff --git a/app/apps/users/models.py b/app/apps/users/models.py index 83adb17..784a95d 100644 --- a/app/apps/users/models.py +++ b/app/apps/users/models.py @@ -23,8 +23,9 @@ class User(AbstractUser): class UserSettings(models.Model): class StartPage(models.TextChoices): - MONTHLY = "MONTHLY_OVERVIEW", _("Monthly Overview") - YEARLY = "YEARLY_OVERVIEW", _("Yearly Overview") + MONTHLY = "MONTHLY_OVERVIEW", _("Monthly") + YEARLY_CURRENCY = "YEARLY_OVERVIEW_CURRENCY", _("Yearly by currency") + YEARLY_ACCOUNT = "YEARLY_OVERVIEW_ACCOUNT", _("Yearly by account") NETWORTH = "NETWORTH", _("Net Worth") ALL_TRANSACTIONS = "ALL_TRANSACTIONS", _("All Transactions") CALENDAR = "CALENDAR", _("Calendar") diff --git a/app/apps/yearly_overview/urls.py b/app/apps/yearly_overview/urls.py index 64ad627..70b13c4 100644 --- a/app/apps/yearly_overview/urls.py +++ b/app/apps/yearly_overview/urls.py @@ -3,10 +3,16 @@ from django.urls import path from . import views urlpatterns = [ - path("yearly/", views.index, name="yearly_index"), + path("yearly/currency/", views.index_by_currency, name="yearly_index_currency"), + path("yearly/account/", views.index_by_account, name="yearly_index_account"), path( - "yearly//", - views.yearly_overview, - name="yearly_overview", + "yearly/currency//", + views.yearly_overview_by_currency, + name="yearly_overview_currency", + ), + path( + "yearly/account//", + views.yearly_overview_by_account, + name="yearly_overview_account", ), ] diff --git a/app/apps/yearly_overview/views.py b/app/apps/yearly_overview/views.py index 8fade8d..bc39d25 100644 --- a/app/apps/yearly_overview/views.py +++ b/app/apps/yearly_overview/views.py @@ -13,17 +13,27 @@ from apps.transactions.models import Transaction @login_required -def index(request): +def index_by_currency(request): now = timezone.localdate(timezone.now()) - return redirect(to="yearly_overview", year=now.year) + return redirect(to="yearly_overview_currency", year=now.year) -def yearly_overview(request, year: int): +@login_required +def index_by_account(request): + now = timezone.localdate(timezone.now()) + + return redirect(to="yearly_overview_account", year=now.year) + + +@login_required +def yearly_overview_by_currency(request, year: int): next_year = year + 1 previous_year = year - 1 - transactions = Transaction.objects.filter(reference_date__year=year) + transactions = Transaction.objects.filter( + reference_date__year=year, account__is_archived=False + ) monthly_data = ( transactions.annotate(month=TruncMonth("reference_date")) @@ -165,7 +175,7 @@ def yearly_overview(request, year: int): return render( request, - "yearly_overview/pages/overview.html", + "yearly_overview/pages/overview_by_currency.html", context={ "year": year, "next_year": next_year, @@ -173,3 +183,166 @@ def yearly_overview(request, year: int): "totals": result, }, ) + + +@login_required +def yearly_overview_by_account(request, year: int): + next_year = year + 1 + previous_year = year - 1 + + transactions = Transaction.objects.filter( + reference_date__year=year, account__is_archived=False + ) + + monthly_data = ( + transactions.annotate(month=TruncMonth("reference_date")) + .values( + "month", + "account__id", + "account__name", + "account__currency__code", + "account__currency__prefix", + "account__currency__suffix", + "account__currency__decimal_places", + ) + .annotate( + income_paid=Coalesce( + Sum( + Case( + When( + type=Transaction.Type.INCOME, is_paid=True, then=F("amount") + ), + default=Value(Decimal("0")), + output_field=DecimalField(), + ) + ), + Value(Decimal("0")), + output_field=DecimalField(), + ), + expense_paid=Coalesce( + Sum( + Case( + When( + type=Transaction.Type.EXPENSE, + is_paid=True, + then=F("amount"), + ), + default=Value(Decimal("0")), + output_field=DecimalField(), + ) + ), + Value(Decimal("0")), + output_field=DecimalField(), + ), + income_unpaid=Coalesce( + Sum( + Case( + When( + type=Transaction.Type.INCOME, + is_paid=False, + then=F("amount"), + ), + default=Value(Decimal("0")), + output_field=DecimalField(), + ) + ), + Value(Decimal("0")), + output_field=DecimalField(), + ), + expense_unpaid=Coalesce( + Sum( + Case( + When( + type=Transaction.Type.EXPENSE, + is_paid=False, + then=F("amount"), + ), + default=Value(Decimal("0")), + output_field=DecimalField(), + ) + ), + Value(Decimal("0")), + output_field=DecimalField(), + ), + ) + .annotate( + balance_unpaid=F("income_unpaid") - F("expense_unpaid"), + balance_paid=F("income_paid") - F("expense_paid"), + balance_total=F("income_paid") + + F("income_unpaid") + - F("expense_paid") + - F("expense_unpaid"), + ) + .order_by("month", "account__name") + ) + + # Create a list of all months in the year + all_months = [date(year, month, 1) for month in range(1, 13)] + + # Get all accounts that had transactions in this year + accounts = ( + transactions.values( + "account__id", + "account__name", + "account__group__name", + "account__currency__code", + "account__currency__prefix", + "account__currency__suffix", + "account__currency__decimal_places", + ) + .distinct() + .order_by("account__name") + ) + + # Create a dictionary to store the final result + result = { + month: { + account["account__id"]: { + "name": account["account__name"], + "group": account["account__group__name"], + "currency": { + "code": account["account__currency__code"], + "prefix": account["account__currency__prefix"], + "suffix": account["account__currency__suffix"], + "decimal_places": account["account__currency__decimal_places"], + }, + "income_paid": Decimal("0"), + "expense_paid": Decimal("0"), + "income_unpaid": Decimal("0"), + "expense_unpaid": Decimal("0"), + "balance_unpaid": Decimal("0"), + "balance_paid": Decimal("0"), + "balance_total": Decimal("0"), + } + for account in accounts + } + for month in all_months + } + + # Fill in the data + for entry in monthly_data: + month = entry["month"] + account_id = entry["account__id"] + + for field in [ + "income_paid", + "expense_paid", + "income_unpaid", + "expense_unpaid", + "balance_unpaid", + "balance_paid", + "balance_total", + ]: + result[month][account_id][field] = entry[field] + + return render( + request, + "yearly_overview/pages/overview_by_account.html", + context={ + "year": year, + "next_year": next_year, + "previous_year": previous_year, + "totals": result, + "accounts": accounts, + }, + ) diff --git a/app/templates/accounts/fragments/account_reconciliation.html b/app/templates/accounts/fragments/account_reconciliation.html index 2361ec4..102696a 100644 --- a/app/templates/accounts/fragments/account_reconciliation.html +++ b/app/templates/accounts/fragments/account_reconciliation.html @@ -16,7 +16,7 @@
diff --git a/app/templates/cotton/msg/empty.html b/app/templates/cotton/msg/empty.html index d1e3057..3073a1f 100644 --- a/app/templates/cotton/msg/empty.html +++ b/app/templates/cotton/msg/empty.html @@ -1,5 +1,5 @@ -
-
+
+

{{ title }}

diff --git a/app/templates/includes/navbar.html b/app/templates/includes/navbar.html index b7bb617..435584a 100644 --- a/app/templates/includes/navbar.html +++ b/app/templates/includes/navbar.html @@ -13,7 +13,7 @@
-{#
#} -{#
#} -{#
#} -{#
#} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# {% for date, x in totals.items %}#} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# {% endfor %}#} -{# #} -{#
{% translate 'Month' %}{% translate 'Projected Income' %}{% translate 'Projected Expenses' %}{% translate 'Projected Total' %}{% translate 'Current Income' %}{% translate 'Current Expenses' %}{% translate 'Current Total' %}{% translate 'Final Total' %}
{{ date.month|month_name }}#} -{# {% for data in x.income_unpaid %}#} -{#
#} -{# {% empty %}#} -{#
-
#} -{# {% endfor %}#} -{#
#} -{# {% for data in x.expense_unpaid %}#} -{#
#} -{# {% empty %}#} -{#
-
#} -{# {% endfor %}#} -{#
#} -{# {% for data in x.balance_unpaid %}#} -{#
#} -{# {% empty %}#} -{#
-
#} -{# {% endfor %}#} -{#
#} -{# {% for data in x.income_paid %}#} -{#
#} -{# {% empty %}#} -{#
-
#} -{# {% endfor %}#} -{#
#} -{# {% for data in x.expense_paid %}#} -{#
#} -{# {% empty %}#} -{#
-
#} -{# {% endfor %}#} -{#
#} -{# {% for data in x.balance_paid %}#} -{#
#} -{# {% empty %}#} -{#
-
#} -{# {% endfor %}#} -{#
#} -{# {% for data in x.balance_total %}#} -{#
#} -{# {% empty %}#} -{#
-
#} -{# {% endfor %}#} -{#
#} -{#
#} -{#
#} -{#
#} -{#
#}
{% for date, x in totals.items %}