added ability to delete comparison sets, also made future bills recalc for Hydunday/D_quit dependent future bills, this is not effectively functional -v1.0 :)
This commit is contained in:
6
TODO
6
TODO
@@ -3,10 +3,8 @@ CALC:
|
||||
differently, but hard to calc - slides around with tax brackets in future
|
||||
|
||||
UI:
|
||||
* maybe a help/note/set of links somehow, to common things: e.g. macquarie int rate page, inflation source, etc.
|
||||
- also then could effectively take those "notes" out of db.py and incorporate, so that when I update stuff it gets done right
|
||||
- in fact, I *COULD* have a basic date check/workflow, e.g. its been more than X days since Y was updated, click here
|
||||
and it walks me through the manual update steps and when I finish its datestamped until next time
|
||||
* I *COULD* have a basic date check/workflow, e.g. it now>last_update+14 (and its more than a paydate <- need to think that bit through)
|
||||
then the question mark can be more prominent -> use an <alert> that suggests updating as per ?
|
||||
|
||||
For bills:
|
||||
* if we change certain items on the finance page, need to re-do bills, e.g.
|
||||
|
||||
102
bills.py
102
bills.py
@@ -1,4 +1,5 @@
|
||||
from db import set_bill_type_growth, new_bill
|
||||
from db import set_bill_type_growth, new_bill, deleteFutureEstimates, get_finance_data, get_bill_data, get_bill_types, get_bill_freqs
|
||||
from calc import calc_key_dates
|
||||
from defines import END_YEAR
|
||||
import datetime
|
||||
from datetime import date, timedelta
|
||||
@@ -310,7 +311,6 @@ def process_bill_data(bd, bt, bf, key_dates):
|
||||
bt_id_ann_growth_avg = {row["id"]: row["ann_growth_avg"] for row in bt}
|
||||
bt_id_name = {row["id"]: row["name"] for row in bt}
|
||||
|
||||
|
||||
# this maps freq to bills per annum (e.g. id=2 to 4 bills per annum)
|
||||
bf_id_num = {row["id"]: row["num_bills_per_annum"] for row in bf}
|
||||
# and allows me a way to see if the bill is quarterly but also fixed or seasonal
|
||||
@@ -386,7 +386,7 @@ def process_bill_data(bd, bt, bf, key_dates):
|
||||
if yr in bill_info[bill_type]['year'] and len(bill_info[bill_type]['year'][yr]) >= bill_info[bill_type]['num_ann_bills'] and bill_info[bill_type]['num_ann_bills'] !=4:
|
||||
continue
|
||||
add_missing_bills_for_yr( bill_type, bill_info, yr )
|
||||
derive_ann_growth( bill_type, bill_info, key_dates, future_car_bills, future_D_quit_bills )
|
||||
derive_ann_growth( bill_type, bill_info, key_dates )
|
||||
|
||||
deal_with_future_car_bills( key_dates, future_car_bills, bill_info )
|
||||
deal_with_future_D_quit_bills( key_dates, future_D_quit_bills, bill_info )
|
||||
@@ -409,7 +409,6 @@ def deal_with_future_car_bills( key_dates, future_car_bills, bill_info ):
|
||||
new_date=f"{yr}-{car_mmdd}"
|
||||
# if we dont already have an annual bill for this year (all car bills are annual)
|
||||
if yr not in bill_info[bt]['year']:
|
||||
print( f"amt of a car fb is: {amt}, car_yr={car_yr}, yr={yr}, nd={new_date}")
|
||||
new_estimated_bill( bill_info, yr, fb['bill_type'], amt, new_date )
|
||||
amt += amt * bill_info[bt]['growth']/100
|
||||
|
||||
@@ -433,23 +432,19 @@ def deal_with_future_D_quit_bills( key_dates, future_D_quit_bills, bill_info ):
|
||||
new_date=f"{yr}-{dq_mm}-{dq_dd}"
|
||||
# if we dont already have an annual bill for this year
|
||||
if not find_this_bill( bt, bill_info, new_date ):
|
||||
print( f"amt of a D_quit fb is: {amt}, dq_yr={D_quit_yr}, yr={yr}, nd={new_date}")
|
||||
new_estimated_bill( bill_info, yr, bt, amt, new_date )
|
||||
amt += amt * bill_info[bt]['growth']/100
|
||||
elif bill_info[bt]['num_ann_bills'] == 12:
|
||||
print( f"should be adding monthly future bill" )
|
||||
# do rest of this year, then next years
|
||||
for m in range( int(dq_mm), 13):
|
||||
new_date=f"{D_quit_yr}-{m:02d}-{dq_dd}"
|
||||
if not find_this_bill( bt, bill_info, new_date ):
|
||||
print( f"amt of a D_quit fb is: {amt}, dq_yr={D_quit_yr}, nd={new_date}")
|
||||
new_estimated_bill( bill_info, yr, bt, amt, new_date )
|
||||
for yr in range( int(D_quit_yr)+1, END_YEAR ):
|
||||
amt += amt * bill_info[bt]['growth']/100
|
||||
for m in range( 1, 13):
|
||||
new_date=f"{yr}-{m:02d}-{dq_dd}"
|
||||
if not find_this_bill( bt, bill_info, new_date ):
|
||||
print( f"amt of a D_quit fb is: {amt}, dq_yr={D_quit_yr}, yr={yr}, nd={new_date}")
|
||||
new_estimated_bill( bill_info, yr, bt, amt, new_date )
|
||||
|
||||
################################################################################
|
||||
@@ -492,7 +487,7 @@ def ProportionQtrlyData( bill_type, bill_info ):
|
||||
# terms of min/avg/max - uses qtr data for qtrly bills, or just normal totals
|
||||
# for other bill types
|
||||
################################################################################
|
||||
def derive_ann_growth( bill_type, bill_info, key_dates, future_car_bills, future_D_quit_bills ):
|
||||
def derive_ann_growth( bill_type, bill_info, key_dates ):
|
||||
# just do up to now so we stop earlier than looking at other estimated (just an optimisation)
|
||||
now_yr = datetime.date.today().year
|
||||
|
||||
@@ -586,3 +581,92 @@ 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
|
||||
|
||||
|
||||
################################################################################
|
||||
# When we change the day D_quits, or we buyout the car, then future bills need
|
||||
# to change/rebuild estimates, convenience routine used to find future bills -
|
||||
# rather than go through them as we render /bills
|
||||
################################################################################
|
||||
def getFutureBills(bd,bt,future_car_bills, future_D_quit_bills):
|
||||
# this maps a bill id to a name
|
||||
bt_id_name = {row["id"]: row["name"] for row in bt}
|
||||
|
||||
for bill in bd:
|
||||
bill_type = bill['bill_type']
|
||||
if bill['bill_date'] == 'future':
|
||||
# Future bills, deal with them at the end - they have dynamic start dates
|
||||
if 'Hyundai' in bt_id_name[bill_type]:
|
||||
future_car_bills.insert( 0, bill )
|
||||
else:
|
||||
future_D_quit_bills.insert( 0, bill )
|
||||
return
|
||||
|
||||
|
||||
################################################################################
|
||||
# When we change the day D_quits, or we buyout the car, then future bills need
|
||||
# to change/rebuild estimates, convenience routine used to handle this
|
||||
################################################################################
|
||||
def recalcFutureBills():
|
||||
future_car_bills=[]
|
||||
future_D_quit_bills=[]
|
||||
|
||||
print("Recalculating future bills as we changed a key date" )
|
||||
finance_data = get_finance_data()
|
||||
key_dates = calc_key_dates( finance_data )
|
||||
finance_data = get_finance_data()
|
||||
bill_data = get_bill_data("order_by_date_only")
|
||||
bill_types = get_bill_types()
|
||||
bill_freqs = get_bill_freqs()
|
||||
|
||||
bt_id_freq = {row["id"]: row["freq"] for row in bill_types}
|
||||
# this maps freq to bills per annum (e.g. id=2 to 4 bills per annum)
|
||||
bf_id_num = {row["id"]: row["num_bills_per_annum"] for row in bill_freqs}
|
||||
|
||||
|
||||
getFutureBills(bill_data, bill_types, future_car_bills, future_D_quit_bills)
|
||||
deleteFutureEstimates()
|
||||
# deal with future car bills
|
||||
car_yr=key_dates['D_hyundai_owned'][0:4]
|
||||
car_mmdd=key_dates['D_hyundai_owned'][5:]
|
||||
for fb in future_car_bills:
|
||||
amt=fb['amount']
|
||||
bt=fb['bill_type']
|
||||
# only can use simple growth as its a future bill
|
||||
growth=bill_types[bt]['ann_growth_simple']
|
||||
# factor in growth for next bills
|
||||
for yr in range( int(car_yr), END_YEAR ):
|
||||
new_date=f"{yr}-{car_mmdd}"
|
||||
new_bill( fb['bill_type'], amt, new_date, 1 )
|
||||
amt += amt * growth/100
|
||||
|
||||
# deal with future D_Quit bills
|
||||
D_quit_yr = key_dates['D_quit_date'][0:4]
|
||||
dq_mm=key_dates['D_quit_date'][5:7]
|
||||
dq_dd=key_dates['D_quit_date'][8:]
|
||||
# avoid feb 29+ :)
|
||||
if int(dq_dd) > 28: dq_dd=28
|
||||
for fb in future_D_quit_bills:
|
||||
# deal with future bills due to their starting dates being dynamic
|
||||
amt=fb['amount']
|
||||
bt=fb['bill_type']
|
||||
growth=bill_types[bt]['ann_growth_simple']
|
||||
num_ann_bills= bf_id_num[bt_id_freq[bt]]
|
||||
if num_ann_bills == 1:
|
||||
# factor in growth for next bill
|
||||
for yr in range( int(D_quit_yr), END_YEAR ):
|
||||
new_date=f"{yr}-{dq_mm}-{dq_dd}"
|
||||
# if we dont already have an annual bill for this year
|
||||
new_bill( fb['bill_type'], amt, new_date, 1 )
|
||||
amt += amt * growth/100
|
||||
elif num_ann_bills == 12:
|
||||
# do rest of this year, then next years
|
||||
for m in range( int(dq_mm), 13):
|
||||
new_date=f"{D_quit_yr}-{m:02d}-{dq_dd}"
|
||||
new_bill( fb['bill_type'], amt, new_date, 1 )
|
||||
for yr in range( int(D_quit_yr)+1, END_YEAR ):
|
||||
amt += amt * growth/100
|
||||
for m in range( 1, 13):
|
||||
new_date=f"{yr}-{m:02d}-{dq_dd}"
|
||||
new_bill( fb['bill_type'], amt, new_date, 1 )
|
||||
return
|
||||
|
||||
17
db.py
17
db.py
@@ -119,15 +119,6 @@ def init_db():
|
||||
# Check if table is empty, if so insert default values
|
||||
cur.execute('SELECT COUNT(*) FROM finance')
|
||||
if cur.fetchone()[0] == 0:
|
||||
###
|
||||
# For now manually update below on the fortnight of the original pay shcedule to compare saved version vs. our reality. Update:
|
||||
# Savings (Macq+me bank) -- noting ME bank is: $2001.19, nab is -5200
|
||||
# TLS/CBA prices
|
||||
# Interest rate
|
||||
# D_leave_owed_in_days
|
||||
# maybe quarterly update Inflation? (this is harder to appreciate, seems much lower officialy than Savings, but which inflation:
|
||||
# I've decided to use RBA Trimmed Mean CPI YoY -- https://tradingeconomics.com/australia/inflation-cpi
|
||||
###
|
||||
cur.execute('''INSERT INTO finance (D_Salary, D_Num_fortnights_pay, School_Fees, Car_loan_via_pay, Car_loan, Car_balloon, Car_buyout, Living_Expenses, Savings, Interest_Rate,
|
||||
Inflation, Mich_present, Overseas_trip, Mark_reno, D_leave_owed_in_days, D_TLS_shares, M_TLS_shares, D_CBA_shares, TLS_price, CBA_price, Overseas_trip_date, Mark_reno_date, Car_buyout_date, Sell_shares, compare_to, Ioniq6_future)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''',
|
||||
@@ -397,6 +388,14 @@ def save_ui(data):
|
||||
return
|
||||
|
||||
|
||||
def deleteFutureEstimates():
|
||||
conn = connect_db(False)
|
||||
cur = conn.cursor()
|
||||
cur.execute( "delete from bill_data where bill_date != 'future' and bill_type in ( select bill_type from bill_data where bill_date='future')" )
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return
|
||||
|
||||
def delete_estimated_bills():
|
||||
conn = connect_db(False)
|
||||
cur = conn.cursor()
|
||||
|
||||
10
main.py
10
main.py
@@ -5,7 +5,7 @@ from db import init_db, get_finance_data, update_finance, get_budget_data, inser
|
||||
from db import get_bill_data, new_bill, update_bill_data, delete_bill, delete_estimated_bills
|
||||
from db import get_bill_ui, save_ui, delete_cset
|
||||
from db import get_bill_types, insert_bill_type, update_bill_type, delete_bill_type, use_growth
|
||||
from bills import process_bill_data, calc_future_totals, set_bill_type_growth
|
||||
from bills import process_bill_data, calc_future_totals, set_bill_type_growth, recalcFutureBills
|
||||
from defines import END_YEAR
|
||||
from collections import defaultdict, Counter
|
||||
from datetime import datetime, date
|
||||
@@ -113,6 +113,8 @@ def save():
|
||||
@app.route('/update', methods=['POST'])
|
||||
def update():
|
||||
|
||||
old_finance_data = get_finance_data()
|
||||
|
||||
finance_data = (
|
||||
request.form['D_Salary'],
|
||||
request.form['D_Num_fortnights_pay'],
|
||||
@@ -142,7 +144,11 @@ def update():
|
||||
request.form['Ioniq6_future']
|
||||
)
|
||||
update_finance(finance_data)
|
||||
# FIXME: need code here to delete/rebuild future bills if we change "D # Pays to quit "
|
||||
new_finance_data = get_finance_data()
|
||||
# changed Ioniq6_future, Car_buyout_date or D_Num_fortnights_pay, so lets force recalc key_dates, and therefore estimated bills
|
||||
if old_finance_data['D_Num_fortnights_pay'] != new_finance_data['D_Num_fortnights_pay'] or old_finance_data['Ioniq6_future'] != new_finance_data['Ioniq6_future'] or old_finance_data['Car_buyout_date'] != new_finance_data['Car_buyout_date']:
|
||||
recalcFutureBills()
|
||||
|
||||
return redirect(url_for('index'))
|
||||
|
||||
@app.route('/bills')
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
|
||||
|
||||
|
||||
<title>Finance Form</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
@@ -19,7 +21,30 @@
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="d-flex align-items-center justify-content-center position-relative">
|
||||
<h3 align="center">Finance Tracker (go to <a href="bills">Bills</a> or <a href="cset">Comparison Sets</a>)</h3>
|
||||
<!-- Clickable Question Mark Icon -->
|
||||
<a href="#" tabindex="0"
|
||||
class="position-absolute end-0 me-3"
|
||||
data-bs-toggle="popover"
|
||||
data-bs-trigger="click"
|
||||
data-bs-placement="right"
|
||||
data-bs-content="For now manually update the itmes below on day aftter original pay shcedule to compare saved version vs. our reality:
|
||||
<ul>
|
||||
<li>Savings (<a href='https://online.macquarie.com.au/personal/#/login'>Macquarie</a>
|
||||
+<a href='https://ib.mebank.com.au/authR5/ib/login.jsp'>ME bank</a>
|
||||
+<a href='https://ib.nab.com.au/login'>NAB</a>) -- noting ME bank is: $2001.19</li>
|
||||
<li><a href='https://www.google.com/search?q=asx+tls'>TLS</a>/<a href='https://www.google.com/search?q=asx+cba'>CBA</a> prices</li>
|
||||
<li>Macq <a href='https://www.macquarie.com.au/everyday-banking/savings-account.html'>Interest rate</a></li>
|
||||
<li><a href='https://deakinpeople.deakin.edu.au/psc/HCMP/EMPLOYEE/HRMS/c/NUI_FRAMEWORK.PT_AGSTARTPAGE_NUI.GBL?CONTEXTIDPARAMS=TEMPLATE_ID%3aPTPPNAVCOL&scname=ADMN_LEAVE&PTPPB_GROUPLET_ID=DU_LEAVE&CRefName=ADMN_NAVCOLL_3'>D_leave_owed_in_days</a> by: {{key_dates['D_quit_date']}}</li>
|
||||
<li>update Inflation - using <a href='https://tradingeconomics.com/australia/core-inflation-rate'>RBA Trimmed Mean CPI YoY</a></li></li>
|
||||
</ul>"
|
||||
data-bs-html="true">
|
||||
<i class="bi bi-question-circle" style="font-size: 2.0rem;"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<form id="vals_form" class="ms-3 mt-3" action="/update" method="POST">
|
||||
{% for r in DISP %}
|
||||
@@ -198,6 +223,7 @@
|
||||
const savingsData = JSON.parse('{{ savings | tojson }}');
|
||||
const vars = JSON.parse('{{ finance | tojson }}');
|
||||
|
||||
$(function() { $('[data-bs-toggle="popover"]').popover(); });
|
||||
window.onload = function() {
|
||||
$('#Sell_shares').val( {{finance['Sell_shares']}} )
|
||||
$('#compare_to').val( {{finance['compare_to']}} )
|
||||
|
||||
Reference in New Issue
Block a user