add statistics for irregular expenses, interpret negative transactions as outgoing and positive as incoming
This commit is contained in:
@@ -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]
|
||||
|
||||
@@ -44,36 +44,38 @@
|
||||
|
||||
<div class="main-container">
|
||||
<h2>Analysis</h2>
|
||||
<p>current average amounts per month:</p>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>current average monthly income</td>
|
||||
<td>income</td>
|
||||
<td>{{ avg_monthly_income|euro }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>current average monthly expenses</td>
|
||||
<td>expenses</td>
|
||||
<td>{{ avg_monthly_expenses|euro }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>current average monthly result</td>
|
||||
<td>result</td>
|
||||
<td>{{ avg_monthly_result|euro }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p>average irregular expenses based on the last half-a-year:</p>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>irregular expenses / month</td>
|
||||
<td>0</td>
|
||||
<td>monthly</td>
|
||||
<td>{{ avg_monthly_irregular_expenses|euro }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>irregular expenses / week</td>
|
||||
<td>0</td>
|
||||
<td>weekly</td>
|
||||
<td>{{ avg_weekly_irregular_expenses|euro }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>irregular expenses / day</td>
|
||||
<td>0</td>
|
||||
<td>daily</td>
|
||||
<td>{{ avg_daily_irregular_expenses|euro }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user