Files
WYGIWYH/app/apps/yearly_overview/views.py
2024-10-31 23:13:29 -03:00

349 lines
11 KiB
Python

from datetime import date
from decimal import Decimal
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
from django.utils import timezone
from django.db.models import Sum, F, Q, Value, CharField, DecimalField
from django.db.models.functions import TruncMonth, Coalesce
from django.db.models.expressions import Case, When
from django.db.models.functions import Concat
from apps.transactions.models import Transaction
@login_required
def index_by_currency(request):
now = timezone.localdate(timezone.now())
return redirect(to="yearly_overview_currency", year=now.year)
@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, account__is_archived=False
)
monthly_data = (
transactions.annotate(month=TruncMonth("reference_date"))
.values(
"month",
"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__currency__code")
)
# Create a list of all months in the year
all_months = [date(year, month, 1) for month in range(1, 13)]
# Create a dictionary to store the final result
result = {
month: {
"income_paid": [],
"expense_paid": [],
"income_unpaid": [],
"expense_unpaid": [],
"balance_unpaid": [],
"balance_paid": [],
"balance_total": [],
}
for month in all_months
}
# Fill in the data
for entry in monthly_data:
month = entry["month"]
currency_code = entry["account__currency__code"]
prefix = entry["account__currency__prefix"]
suffix = entry["account__currency__suffix"]
decimal_places = entry["account__currency__decimal_places"]
for field in [
"income_paid",
"expense_paid",
"income_unpaid",
"expense_unpaid",
"balance_unpaid",
"balance_paid",
"balance_total",
]:
if entry[field] != 0:
result[month][field].append(
{
"code": currency_code,
"prefix": prefix,
"suffix": suffix,
"decimal_places": decimal_places,
"amount": entry[field],
}
)
# Fill in missing months with empty lists
for month in all_months:
if not any(result[month].values()):
result[month] = {
"income_paid": [],
"expense_paid": [],
"income_unpaid": [],
"expense_unpaid": [],
"balance_unpaid": [],
"balance_paid": [],
"balance_total": [],
}
return render(
request,
"yearly_overview/pages/overview_by_currency.html",
context={
"year": year,
"next_year": next_year,
"previous_year": previous_year,
"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,
},
)