diff --git a/financeplanner/calc.py b/financeplanner/calc.py index 9af722b..9f0ab01 100644 --- a/financeplanner/calc.py +++ b/financeplanner/calc.py @@ -6,6 +6,10 @@ from django.db.models import Q from financeplanner.utils import current_date +days_per_year = Decimal("365.25") +days_per_month = days_per_year / Decimal("12") +days_per_week = Decimal("7") + def _floor_to_first_two_places(dec): if dec < 100: @@ -21,6 +25,9 @@ class ActualTransaction: self.subject = subject self.amount = amount + def __str__(self): + return f"ActualTransaction({self.date}, {self.subject}, {self.amount})" + class DailyStat: def __init__(self, date, balance_amount, actual_transactions, resulting_amount): @@ -32,6 +39,10 @@ class DailyStat: self.color_class = "" self.opacity_class = "" + def __str__(self): + return f"DailyStat({self.date}, {self.balance_amount}, " \ + f"{len(self.actual_transactions)} a. transactions, {self.resulting_amount})" + def generate_graph_bar_attributes(self, amount_scale, today): if self.resulting_amount is not None: self.percentage = int((self.resulting_amount / amount_scale) * 100) @@ -68,6 +79,9 @@ class Statistics: self.avg_monthly_income = 0 self.avg_monthly_expenses = 0 self.avg_monthly_result = 0 + self.avg_daily_irregular_expenses = 0 + self.avg_weekly_irregular_expenses = 0 + self.avg_monthly_irregular_expenses = 0 self._fetch_relevant_balances() self._fetch_relevant_transactions() @@ -150,7 +164,7 @@ class Statistics: if transaction.date == iter_date: actual_transactions.append(transaction) if iter_amount is not None: - iter_amount -= transaction.amount + iter_amount += transaction.amount if iter_amount is not None: self.daily_stats.append(DailyStat( @@ -174,20 +188,38 @@ class Statistics: def _calculate_analysis(self): booked = Q(booking_date__lte=self.today) - incoming = Q(amount__lt=0) - outgoing = Q(amount__gt=0) + incoming = Q(amount__gt=0) + outgoing = Q(amount__lt=0) recurring = Q(recurring_months__isnull=False) & ( Q(not_recurring_after__isnull=True) | Q(not_recurring_after__gte=self.today)) in_trans = self.user.transactions.filter(booked & incoming & recurring) in_trans_per_month = [t.amount / t.recurring_months for t in in_trans] - self.avg_monthly_income = -sum(in_trans_per_month) + self.avg_monthly_income = sum(in_trans_per_month) out_trans = self.user.transactions.filter(booked & outgoing & recurring) out_trans_per_month = [t.amount / t.recurring_months for t in out_trans] self.avg_monthly_expenses = sum(out_trans_per_month) - self.avg_monthly_result = self.avg_monthly_income - self.avg_monthly_expenses + self.avg_monthly_result = self.avg_monthly_income + self.avg_monthly_expenses + + daily_irregular_expenses = [] + if len(self.balances) > 1: + for i in range(1, len(self.balances)): + previous_balance = self.balances[i - 1] + current_balance = self.balances[i] + day_before = current_balance.date - relativedelta(days=1) + relevant_stats = [s for s in self.daily_stats if s.date == day_before] + if relevant_stats and day_before > previous_balance.date: + assert len(relevant_stats) == 1, \ + f"daily stats should be unique per date, but here are {relevant_stats}" + amount_diff = current_balance.amount - relevant_stats[0].resulting_amount + time_diff = current_balance.date - previous_balance.date + daily_irregular_expenses.append(amount_diff / time_diff.days) + if daily_irregular_expenses: + self.avg_daily_irregular_expenses = sum(daily_irregular_expenses) / len(daily_irregular_expenses) + self.avg_weekly_irregular_expenses = self.avg_daily_irregular_expenses * days_per_week + self.avg_monthly_irregular_expenses = self.avg_daily_irregular_expenses * days_per_month def get_daily_stats_in_range(self): return [s for s in self.daily_stats if self.start <= s.date <= self.end] diff --git a/financeplanner/templates/financeplanner/index.html b/financeplanner/templates/financeplanner/index.html index 474d68e..f8c1044 100644 --- a/financeplanner/templates/financeplanner/index.html +++ b/financeplanner/templates/financeplanner/index.html @@ -44,36 +44,38 @@

Analysis

+

current average amounts per month:

- + - + - +
current average monthly incomeincome {{ avg_monthly_income|euro }}
current average monthly expensesexpenses {{ avg_monthly_expenses|euro }}
current average monthly resultresult {{ avg_monthly_result|euro }}
+

average irregular expenses based on the last half-a-year:

- - + + - - + + - - + +
irregular expenses / month0monthly{{ avg_monthly_irregular_expenses|euro }}
irregular expenses / week0weekly{{ avg_weekly_irregular_expenses|euro }}
irregular expenses / day0daily{{ avg_daily_irregular_expenses|euro }}
diff --git a/financeplanner/views.py b/financeplanner/views.py index 17a31c7..8461fe3 100644 --- a/financeplanner/views.py +++ b/financeplanner/views.py @@ -18,5 +18,8 @@ def index(request): "avg_monthly_income": statistics.avg_monthly_income, "avg_monthly_expenses": statistics.avg_monthly_expenses, "avg_monthly_result": statistics.avg_monthly_result, + "avg_daily_irregular_expenses": statistics.avg_daily_irregular_expenses, + "avg_weekly_irregular_expenses": statistics.avg_weekly_irregular_expenses, + "avg_monthly_irregular_expenses": statistics.avg_monthly_irregular_expenses, } return render(request, "financeplanner/index.html", context)