Compare commits

...

3 Commits

5 changed files with 50 additions and 41 deletions

34
TODO
View File

@@ -1,33 +1,11 @@
UI:
* use key_dates to highlight when I quit, when we own car
For bills:
Future bills:
* longer-term: make the moment we quit / own car trigger new bill creation, and remove hard-coded logic on costs - just use bill_data created
- this will then cover handling different bill_types or growth models
[DONE] * calc quit date based on finance data
[DONE] * calc date of car lease end or buyout
* use this to populate bill estimates, so this allows totals / year and simplifies calc.py
* might need to be able to mark a specific bill as an outlier - so we ignore the data somehow (think Gas is messing with my bills)
* might need to be able to mark a specific bill as an outlier:
- so we ignore the data somehow (think Gas is messing with my bills)
- and even electricity, water, etc. for when we were away in Europe but mostly gas/elec
LONGER/HARDER:
* need to work out 'first bill' and 'last bill' to auto-fill missing bills based on
-- all missing bills follow varying growth models & its by choice -- therefore I need this in DB
- ANN: flat, min, avg, max, manual
- QTR: flat, qtrly seasonal: min/avg/max/manual, qtrly simple: min/avg/max/manual, annual: min/avg/max/manual
- MON: flat, monthly: min/avg/max/manual, annual: min/avg/max/manual
-- use this logic to add missing bills (date):
-- ANN: missing annual bill, find date based on MM-DD and add new year - given we start with first_bill anyway, will only be used for future bill predictions
-- QTR: missing quarterly bill, find date based on MM-DD and ??? - can have missing bilsl in first year
-- MON: missing monthly bills, find date based on DD and put in each missing month
-- use this logic to add missing bills (amount):
-- ANN: future only, so add ann_growth (based on drop-down) for each future year
-- QTR: add growth (based on drop-down) for each future year
-- MON: add growth (based on drop-down) for each future year
MUCH LONGER/HARDER:
potentially for each bill_type, there are unique extras - e.g. THIS feels too hard:
water has 2 fixed charges (water & sewerage) and then a consumption charge (per ML)
elec has 1 fixe charge (daily) and then consumption (per kwh) BUT, also daily solar rate
gas has fixed charge and consumption
internet, kayo is monthly fixed (but can go up sometimes)
eweka is annual fixed
phone is messier again.

View File

@@ -291,7 +291,7 @@ def get_growth_value( bt, bill_type ):
# and I didn't want to input 12 of them at the same price), and it always
# occurs for future bills
################################################################################
def process_bill_data(bd, bt, bf):
def process_bill_data(bd, bt, bf, key_dates):
# this maps a bill id to a freq id (e.g. bill #34 - has a frequency of #2 (which might be quarterly)
bt_id_freq = {row["id"]: row["freq"] for row in bt}
bt_id_ann_growth_avg = {row["id"]: row["ann_growth_avg"] for row in bt}
@@ -495,3 +495,4 @@ def calc_future_totals(bill_info, bill_types):
# had to round to 2 decimal here to get sensible totals
total[bt['id']][yr] = round( total[bt['id']][yr], 2 )
return total

40
calc.py
View File

@@ -5,6 +5,15 @@ from defines import END_YEAR
# GLOBAL CONSTANTS
LEASE = 0
# Dates that don't change
car_balloon_date = datetime(2026, 11, 15)
new_fin_year_25 = datetime(2025, 7, 1)
new_fin_year_26 = datetime(2026, 7, 1)
end_date = datetime(END_YEAR, 4, 15)
school_fees_date = datetime(2025, 12, 5)
mich_present_date = datetime(2026,10,15)
first_pay_date = datetime(2025,1,8)
def bill_amount_today(finance, day, bill_data, bt_id_name, total ):
amt=0
day_str = day.strftime("%Y-%m-%d")
@@ -100,8 +109,6 @@ def calculate_savings_depletion(finance, bill_data, bill_type):
D_has_quit = False
D_quit_year = 0
claim_tax_on_leave = False
new_fin_year_25 = datetime(2025, 7, 1)
new_fin_year_26 = datetime(2026, 7, 1)
# Constants for interest calculations
annual_interest_rate = Interest_Rate / 100.0
@@ -109,7 +116,6 @@ def calculate_savings_depletion(finance, bill_data, bill_type):
# main loop range -- start from now, and simulate till D is 60 (April 2031)
current_date = datetime.today()
end_date = datetime(END_YEAR, 4, 15)
# work out which bill_types relate to future bills
for bt in bill_type:
@@ -151,11 +157,6 @@ def calculate_savings_depletion(finance, bill_data, bill_type):
depletion_date = None
savings_per_fortnight = []
# significant dates that are non-changeable
school_fees_date = datetime(2025, 12, 5)
car_balloon_date = datetime(2026, 11, 15)
mich_present_date = datetime(2026,10,15)
# significant dates - but who knows when? :)
overseas_trip_date = datetime.strptime( finance['Overseas_trip_date'], "%Y-%m-%d")
mark_reno_date = datetime.strptime( finance['Mark_reno_date'], "%Y-%m-%d")
@@ -360,3 +361,26 @@ def calculate_savings_depletion(finance, bill_data, bill_type):
return depletion_date, savings_per_fortnight, current_savings
################################################################################
# work out the date D quits and when we own the car, so we can then use it to
# handle future bills
################################################################################
def calc_key_dates( finance ):
key_dates={}
now = datetime.today()
# this will be 0 to 13 days - how far into this fortnights pay cycle are we now
days_in_pay_fortnight= ( now - first_pay_date ).days % 14
# add 1 less fortnight than we continue to work, then add rest of pay cycle (14-days_in_pay_fortnight)
key_dates['D_quit_date'] = (now+timedelta(weeks=2*(finance['D_Num_fortnights_pay']-1))+timedelta(days=(14-days_in_pay_fortnight))).strftime('%Y-%m-%d')
# use lease date
if finance['Ioniq6_future'] == LEASE:
key_dates['D_hyundai_owned'] = car_balloon_date.strftime('%Y-%m-%d')
# use buyout date
else:
key_dates['D_hyundai_owned'] = finance['Car_buyout_date']
print( f"kd={key_dates}" )
return key_dates

12
main.py
View File

@@ -1,6 +1,6 @@
# main.py
from flask import Flask, render_template, request, redirect, url_for, Response, jsonify
from calc import calculate_savings_depletion
from calc import calculate_savings_depletion, calc_key_dates
from db import init_db, get_finance_data, update_finance, get_budget_data, insert_cset, get_comp_set_data, get_comp_set_options, get_bill_freqs
from db import get_bill_data, new_bill, update_bill_data, delete_bill
from db import get_bill_ui, save_ui
@@ -145,14 +145,20 @@ def update():
@app.route('/bills')
def DisplayBillData():
finance_data = get_finance_data()
# work out when D quits, when car is owned
key_dates = calc_key_dates( finance_data )
bill_data = get_bill_data("order_by_bill_type_then_date")
bill_types = get_bill_types()
bill_freqs = get_bill_freqs()
bill_ui = get_bill_ui()
bill_info=process_bill_data(bill_data, bill_types, bill_freqs)
# take bill data, AND work out estimated future bills - process this into the bill_info array,
bill_info=process_bill_data(bill_data, bill_types, bill_freqs, key_dates)
# get an array of the total costs of bills each year - purely cosmetic (using bill_info)
total=calc_future_totals(bill_info, bill_types)
# update/re-get bill_data now that new estimated bills have been added
bill_data = get_bill_data("order_by_bill_type_then_date")
return render_template('bills.html', bill_data=bill_data, bill_types=bill_types, bill_freqs=bill_freqs, bill_ui=bill_ui, this_year=datetime.today().year, END_YEAR=END_YEAR, total=total )
return render_template('bills.html', bill_data=bill_data, bill_types=bill_types, bill_freqs=bill_freqs, bill_ui=bill_ui, this_year=datetime.today().year, END_YEAR=END_YEAR, total=total, key_dates=key_dates )
@app.route('/newbilltype', methods=['POST'])
def InsertBillType():

View File

@@ -152,7 +152,7 @@
</div>
</div>
</form>
<div class="row mt-4 highcharts-dark" id="container" style="width:100%; height:400px;"></div>
<div class="row mt-4 highcharts-dark" id="container" style="width:100%; height:800px;"></div>
<script type="text/javascript">
// make these global so we can also use them in the /save route (via modal)
const savingsData = JSON.parse('{{ savings | tojson }}');