61 lines
2.5 KiB
Python
61 lines
2.5 KiB
Python
from __future__ import annotations
|
|
|
|
from decimal import Decimal
|
|
|
|
from app.models import MonthlyAllocation, MonthlyEntryValue, MonthlyIncome, to_decimal
|
|
|
|
|
|
class ComparisonService:
|
|
def month_delta(self, current_month, previous_month) -> dict:
|
|
if previous_month is None:
|
|
zero = Decimal("0.00")
|
|
return {
|
|
"income_delta": zero,
|
|
"cost_delta": zero,
|
|
"remainder_delta": zero,
|
|
"allocation_delta": zero,
|
|
}
|
|
return {
|
|
"income_delta": self._total_income(current_month) - self._total_income(previous_month),
|
|
"cost_delta": self._total_costs(current_month) - self._total_costs(previous_month),
|
|
"remainder_delta": self._remainder(current_month) - self._remainder(previous_month),
|
|
"allocation_delta": self._total_allocations(current_month)
|
|
- self._total_allocations(previous_month),
|
|
}
|
|
|
|
def top_entry_changes(self, current_month, previous_month, limit: int = 6) -> list[dict]:
|
|
if previous_month is None:
|
|
return []
|
|
previous_values = {
|
|
item.entry_id: to_decimal(item.planned_amount) for item in previous_month.entry_values
|
|
}
|
|
changes = []
|
|
for item in current_month.entry_values:
|
|
previous_amount = previous_values.get(item.entry_id, Decimal("0.00"))
|
|
current_amount = to_decimal(item.planned_amount)
|
|
delta = current_amount - previous_amount
|
|
if delta:
|
|
changes.append(
|
|
{
|
|
"entry_name": item.entry.name,
|
|
"category_name": item.entry.category.name,
|
|
"delta": delta,
|
|
"current_amount": current_amount,
|
|
"previous_amount": previous_amount,
|
|
}
|
|
)
|
|
changes.sort(key=lambda item: abs(item["delta"]), reverse=True)
|
|
return changes[:limit]
|
|
|
|
def _total_income(self, month) -> Decimal:
|
|
return sum((to_decimal(item.amount) for item in month.incomes), Decimal("0.00"))
|
|
|
|
def _total_costs(self, month) -> Decimal:
|
|
return sum((to_decimal(item.planned_amount) for item in month.entry_values), Decimal("0.00"))
|
|
|
|
def _total_allocations(self, month) -> Decimal:
|
|
return sum((to_decimal(item.amount) for item in month.allocations), Decimal("0.00"))
|
|
|
|
def _remainder(self, month) -> Decimal:
|
|
return self._total_income(month) - self._total_costs(month)
|