diff --git a/financeplanner/views.py b/financeplanner/views.py index 6e5e225..b8d6443 100644 --- a/financeplanner/views.py +++ b/financeplanner/views.py @@ -10,29 +10,47 @@ from django.shortcuts import render from financeplanner.utils import format_date -def _get_relevant_balances(user, start, end): - balances = user.balances.filter(date__range=(start, end)).order_by("date") - return list(balances) +def _get_relevant_balances(user, range_start, range_end): + balance_list = [] + balances_until_start = user.balances.filter(date__lte=range_start) + if balances_until_start.exists(): + balance_list = [balances_until_start.latest("date")] + other_balances = user.balances.filter(date__gt=range_start, date__lte=range_end).order_by("date") + return balance_list + list(other_balances) -def _get_relevant_transactions(user, start, end): +def _get_relevant_transactions(user, calculation_start, range_end): + one_time_in_range = Q( + booking_date__gte=calculation_start, + booking_date__lte=range_end, + recurring_months__isnull=True, + ) + endlessly_recurring = Q( + booking_date__lte=range_end, + recurring_months__isnull=False, + not_recurring_after__isnull=True, + ) + recurring_and_ending_in_range = Q( + booking_date__lte=range_end, + recurring_months__isnull=False, + not_recurring_after__gte=calculation_start, + ) transactions = user.transactions.filter( - Q(booking_date__range=(start, end)) | - Q(booking_date__lte=end, recurring_months__isnull=False, not_recurring_after__gte=start) + one_time_in_range | endlessly_recurring | recurring_and_ending_in_range ).order_by("booking_date") return list(transactions) -def _calculate_actual_transactions(start, end, transaction_list): +def _calculate_actual_transactions(calculation_start, range_end, transaction_list): result = [] for trans in transaction_list: current_calc_trans = {} if trans.recurring_months: current_date = trans.booking_date step = 1 - limit = min(trans.not_recurring_after, end) if trans.not_recurring_after else end + limit = min(trans.not_recurring_after, range_end) if trans.not_recurring_after else range_end while current_date < limit: - if current_date >= start: + if current_date >= calculation_start: result.append(( current_date, f"{trans.subject} #{step}", @@ -50,12 +68,12 @@ def _calculate_actual_transactions(start, end, transaction_list): return result -def _calculate_daily_stats(start, end, balance_list, actual_transactions): +def _calculate_daily_stats(calculation_start, range_end, balance_list, actual_transactions): stats = {} - current_date = start + current_date = calculation_start current_amount = None - while current_date < end: + while current_date < range_end: current_stats = { "balance": None, "transactions": [], @@ -87,6 +105,10 @@ def _calculate_daily_stats(start, end, balance_list, actual_transactions): return stats +def _trim_stats(stats, range_start): + return {date: stat for date, stat in stats.items() if date >= range_start} + + def _floor_to_first_two_places(dec): if dec < 100: return 100 @@ -103,7 +125,7 @@ def _calculate_scale(stats): return _floor_to_first_two_places(max_amount * Decimal("1.3")) -def _build_graph_data(start, end, balance_list, actual_transactions): +def _build_graph_data(range_start, range_end, calculation_start, balance_list, actual_transactions): """ result has the format: { @@ -125,7 +147,8 @@ def _build_graph_data(start, end, balance_list, actual_transactions): } """ today = datetime.now().date() - stats = _calculate_daily_stats(start, end, balance_list, actual_transactions) + stats = _calculate_daily_stats(calculation_start, range_end, balance_list, actual_transactions) + stats = _trim_stats(stats, range_start) amount_limit = _calculate_scale(stats) for date in stats.keys(): amount = stats[date]["amount"] @@ -162,9 +185,10 @@ def index(request): range_start = today - relativedelta(days=30) range_end = today + relativedelta(days=50) balance_list = _get_relevant_balances(user, range_start, range_end) - transaction_list = _get_relevant_transactions(user, range_start, range_end) - actual_transactions = _calculate_actual_transactions(range_start, range_end, transaction_list) - graph_data = _build_graph_data(range_start, range_end, balance_list, actual_transactions) + calculation_start = balance_list[0].date if balance_list else range_start + transaction_list = _get_relevant_transactions(user, calculation_start, range_end) + actual_transactions = _calculate_actual_transactions(calculation_start, range_end, transaction_list) + graph_data = _build_graph_data(range_start, range_end, calculation_start, balance_list, actual_transactions) context = { "range_start": format_date(range_start), "range_end": format_date(range_end),