Compare commits
8 Commits
392daa1deb
...
e373dd0009
| Author | SHA1 | Date | |
|---|---|---|---|
| e373dd0009 | |||
| 3a5b77f12d | |||
| c74383f89e | |||
| 4a7080787b | |||
| 07f2a321ec | |||
| f67ca61cc7 | |||
| 9ad5089ac5 | |||
| 17f2534056 |
12
README
12
README
@@ -1,18 +1,8 @@
|
|||||||
TODO:
|
|
||||||
* fix BUGs
|
|
||||||
* convert code over to use bill_type instead of name in bills.html
|
|
||||||
|
|
||||||
|
|
||||||
CONSIDER in code:
|
|
||||||
* when we time the payment of GMHBA / HCF (and at what cadence) and include it in calcs better
|
|
||||||
- it kicks in after pay stops, and could be paid monthly say, but it is higher than if we pay yearly (I think)
|
|
||||||
* could make bills be paid quarterly rather than as 'daily' living expenses
|
|
||||||
- also could be more painful with bill increases, they seem to go up more than CPI
|
|
||||||
|
|
||||||
CONSIDER in real-world:
|
CONSIDER in real-world:
|
||||||
* moving > $250k into say ING, then rabo-bank -- 4 months interest higher in each -- maybe to another provider after that
|
* moving > $250k into say ING, then rabo-bank -- 4 months interest higher in each -- maybe to another provider after that
|
||||||
while the balance is > $250k it offsets individual bank risk
|
while the balance is > $250k it offsets individual bank risk
|
||||||
* maybe buying shares in something like berkshire-hathaway, or vanguard ETFs?
|
* maybe buying shares in something like berkshire-hathaway, or vanguard ETFs?
|
||||||
|
* pay out car if the diff is negligible to reduce the exposure to > $250k in bank
|
||||||
|
|
||||||
To run the code:
|
To run the code:
|
||||||
|
|
||||||
|
|||||||
7
TODO
7
TODO
@@ -1,7 +1,14 @@
|
|||||||
UI:
|
UI:
|
||||||
|
|
||||||
For bills:
|
For 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)
|
||||||
|
|
||||||
|
* estimate future bills/growth:
|
||||||
[DONE] - calculate pragmatic min/avg/max/simple
|
[DONE] - calculate pragmatic min/avg/max/simple
|
||||||
|
- need to handle future only bills (like health insurance which only kicks in when I quit)
|
||||||
|
- might all work - validate why future seems to jump in 2026, but not 27+
|
||||||
|
- seems specifically 27->28 is way too small compare to growth in
|
||||||
|
other years, does not seem likely, but maybe qtr timing? (by then they should all be est. though)
|
||||||
- remove bills from Living_Expenses (carefully - but by hand)
|
- remove bills from Living_Expenses (carefully - but by hand)
|
||||||
- fold future bills into calc so they are taken out in a more time and growth appropriate way
|
- fold future bills into calc so they are taken out in a more time and growth appropriate way
|
||||||
- inflation can then be put to a more realistic quarterly figure
|
- inflation can then be put to a more realistic quarterly figure
|
||||||
|
|||||||
48
bills.py
48
bills.py
@@ -140,7 +140,7 @@ def new_estimated_bill( bill_info, yr, bill_type, amt, new_date ):
|
|||||||
bill['bill_date']=new_date
|
bill['bill_date']=new_date
|
||||||
bill['amount']=amt
|
bill['amount']=amt
|
||||||
bill['estimated']=1
|
bill['estimated']=1
|
||||||
# need this for find_previous_bill to work but only need the above 3 fields
|
# need to insert(0,) to add this "newest" bill to start of the data for {yr} so that find_previous_bill can work - only need the above 3 fields
|
||||||
bill_info[bill_type]['year'][yr].insert(0,bill)
|
bill_info[bill_type]['year'][yr].insert(0,bill)
|
||||||
|
|
||||||
if bill_info[bill_type]['num_ann_bills'] == 4:
|
if bill_info[bill_type]['num_ann_bills'] == 4:
|
||||||
@@ -190,15 +190,34 @@ def add_missing_quarter_bills_in_yr( bill_type, bill_info, yr ):
|
|||||||
else:
|
else:
|
||||||
r=range(1,5)
|
r=range(1,5)
|
||||||
for q in r:
|
for q in r:
|
||||||
# amt is total of last year's qtr bill proportion
|
if 'forced' in bill_info[bill_type]['freq']:
|
||||||
amt = bill_info[bill_type]['qtr'][yr-1][q]*(1+bill_info[bill_type]['growth']/100)
|
actually_add_estimated_new_quarter_bill_forced(bill_type, bill_info, yr, q)
|
||||||
# just make new bills first of last month of a qtr (good as any date for GAS, they move anyway)
|
else:
|
||||||
new_date = f'{yr}-{q*3:02d}-01'
|
actually_add_estimated_new_quarter_bill(bill_type, bill_info, yr, q)
|
||||||
# SANITY CHECK: we might be adding a bill estimate we already have (due to stupid gas bills /qtrly code)
|
|
||||||
if yr in bill_info[bill_type]['year']:
|
|
||||||
for b in bill_info[bill_type]['year'][yr]:
|
|
||||||
if b['bill_date'] == new_date:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# func take a qtr in a year, finds equiv from previous year, calcs new based on
|
||||||
|
# it (same 'day' with amt * growth)
|
||||||
|
################################################################################
|
||||||
|
def actually_add_estimated_new_quarter_bill( bill_type, bill_info, yr, q ):
|
||||||
|
|
||||||
|
# amt is total of last year's qtr bill (NOTE: use 4-q, bills are in desc order)
|
||||||
|
last_yrs_bill_in_this_q = bill_info[bill_type]['year'][yr-1][4-q]
|
||||||
|
amt = last_yrs_bill_in_this_q['amount']*(1+bill_info[bill_type]['growth']/100)
|
||||||
|
|
||||||
|
# make new qtr bill same 'day' (mm-dd) as last year, just chg (yr)
|
||||||
|
mmdd=last_yrs_bill_in_this_q['bill_date'][5:]
|
||||||
|
new_date = f'{yr}-{mmdd}'
|
||||||
|
new_estimated_bill( bill_info, yr, bill_type, amt, new_date )
|
||||||
|
return
|
||||||
|
|
||||||
|
def actually_add_estimated_new_quarter_bill_forced( bill_type, bill_info, yr, q ):
|
||||||
|
print( f"Being asked to add a FORCED qtr bill for {yr}-q{q} - likely a GAS bill, as its the only one, *sigh*" )
|
||||||
|
last_yrs_qtr_amount = bill_info[bill_type]['qtr'][yr-1][q]
|
||||||
|
print( f"amt is based on forced qtr amount for last year: {last_yrs_qtr_amount}" )
|
||||||
|
amt=last_yrs_qtr_amount*(1+bill_info[bill_type]['growth']/100)
|
||||||
|
new_date = f'{yr}-{q*3:02d}-01'
|
||||||
new_estimated_bill( bill_info, yr, bill_type, amt, new_date )
|
new_estimated_bill( bill_info, yr, bill_type, amt, new_date )
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -281,6 +300,8 @@ def process_bill_data(bd, bt, bf):
|
|||||||
|
|
||||||
# this maps freq to bills per annum (e.g. id=2 to 4 bills per annum)
|
# 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}
|
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
|
||||||
|
bf_id_name = {row["id"]: row["name"] for row in bf}
|
||||||
|
|
||||||
# want to proces all bill data into easier to maniuplate structure, so make
|
# want to proces all bill data into easier to maniuplate structure, so make
|
||||||
# a bill_info[bill_id] with first_bill, last_bill, [yr] with matching bills to process
|
# a bill_info[bill_id] with first_bill, last_bill, [yr] with matching bills to process
|
||||||
@@ -292,6 +313,7 @@ def process_bill_data(bd, bt, bf):
|
|||||||
# new bill type
|
# new bill type
|
||||||
if not bill_type in bill_info:
|
if not bill_type in bill_info:
|
||||||
bill_info[bill_type]={}
|
bill_info[bill_type]={}
|
||||||
|
bill_info[bill_type]['freq'] = bf_id_name[bt_id_freq[bill_type]]
|
||||||
bill_info[bill_type]['growth'] = get_growth_value( bt, bill_type )
|
bill_info[bill_type]['growth'] = get_growth_value( bt, bill_type )
|
||||||
bill_info[bill_type]['num_ann_bills'] = bf_id_num[bt_id_freq[bill_type]]
|
bill_info[bill_type]['num_ann_bills'] = bf_id_num[bt_id_freq[bill_type]]
|
||||||
bill_info[bill_type]['first_bill']={}
|
bill_info[bill_type]['first_bill']={}
|
||||||
@@ -310,7 +332,7 @@ def process_bill_data(bd, bt, bf):
|
|||||||
bill_info[bill_type]['first_bill_year']=int(bill['bill_date'][:4])
|
bill_info[bill_type]['first_bill_year']=int(bill['bill_date'][:4])
|
||||||
if not 'last_real_bill_year' in bill_info[bill_type] and not bill['estimated']:
|
if not 'last_real_bill_year' in bill_info[bill_type] and not bill['estimated']:
|
||||||
bill_info[bill_type]['last_real_bill_year']=int(bill['bill_date'][:4])
|
bill_info[bill_type]['last_real_bill_year']=int(bill['bill_date'][:4])
|
||||||
# add this bill to list for this year
|
# append this bill to list for this year
|
||||||
bill_info[bill_type]['year'][yr].append(bill)
|
bill_info[bill_type]['year'][yr].append(bill)
|
||||||
|
|
||||||
# now process the bill_info from yr of first bill to yr of last bill
|
# now process the bill_info from yr of first bill to yr of last bill
|
||||||
@@ -447,7 +469,7 @@ def derive_ann_growth( bill_type, bill_info ):
|
|||||||
else:
|
else:
|
||||||
# okay use last - first / years to get a simple_growth, just need bills from different years
|
# okay use last - first / years to get a simple_growth, just need bills from different years
|
||||||
# if there are totals for them (may not be set with monthly and < 12 bills in 1st year)
|
# if there are totals for them (may not be set with monthly and < 12 bills in 1st year)
|
||||||
if bill_info[bill_type]['first_bill_year'] != bill_info[bill_type]['last_real_bill_year'] and bill_info[bill_type]['first_bill_year'] in total and bill_info[bill_type]['last_real_bill_year'] in total:
|
if 'last_real_bill_year' in bill_info[bill_type] and bill_info[bill_type]['first_bill_year'] != bill_info[bill_type]['last_real_bill_year'] and bill_info[bill_type]['first_bill_year'] in total and bill_info[bill_type]['last_real_bill_year'] in total:
|
||||||
simple_growth=( ((total[bill_info[bill_type]['last_real_bill_year']]-total[bill_info[bill_type]['first_bill_year']])/(bill_info[bill_type]['last_real_bill_year']-bill_info[bill_type]['first_bill_year'])) / total[bill_info[bill_type]['first_bill_year']] )*100.0
|
simple_growth=( ((total[bill_info[bill_type]['last_real_bill_year']]-total[bill_info[bill_type]['first_bill_year']])/(bill_info[bill_type]['last_real_bill_year']-bill_info[bill_type]['first_bill_year'])) / total[bill_info[bill_type]['first_bill_year']] )*100.0
|
||||||
set_bill_type_growth( bill_type, 0, 0, 0, simple_growth )
|
set_bill_type_growth( bill_type, 0, 0, 0, simple_growth )
|
||||||
else:
|
else:
|
||||||
@@ -464,8 +486,10 @@ def calc_future_totals(bill_info, bill_types):
|
|||||||
for bt in bill_types:
|
for bt in bill_types:
|
||||||
total[bt['id']]={}
|
total[bt['id']]={}
|
||||||
for yr in range( now_yr, END_YEAR+1):
|
for yr in range( now_yr, END_YEAR+1):
|
||||||
total[bt['id']][yr]=0
|
total[bt['id']][yr]=0.0
|
||||||
if bt['id'] in bill_info and yr in bill_info[bt['id']]['year']:
|
if bt['id'] in bill_info and yr in bill_info[bt['id']]['year']:
|
||||||
for b in bill_info[bt['id']]['year'][yr]:
|
for b in bill_info[bt['id']]['year'][yr]:
|
||||||
total[bt['id']][yr] += b['amount']
|
total[bt['id']][yr] += b['amount']
|
||||||
|
# had to round to 2 decimal here to get sensible totals
|
||||||
|
total[bt['id']][yr] = round( total[bt['id']][yr], 2 )
|
||||||
return total
|
return total
|
||||||
|
|||||||
3
db.py
3
db.py
@@ -134,7 +134,8 @@ def init_db():
|
|||||||
(4762.29, 10, 24000, 620, 2412, 45824.68, 83738.74, 80000, 424875.26, 4.75, 2.4, 10000, 50000, 10000, 76.85, 1000, 750, 1111, 4.52, 163.32, '2025-06-01', '2025-09-01', '2025-02-20', 4, 0, 0))
|
(4762.29, 10, 24000, 620, 2412, 45824.68, 83738.74, 80000, 424875.26, 4.75, 2.4, 10000, 50000, 10000, 76.85, 1000, 750, 1111, 4.52, 163.32, '2025-06-01', '2025-09-01', '2025-02-20', 4, 0, 0))
|
||||||
cur.execute( "INSERT INTO bill_freq values ( 1, 'Annual', 1 )" )
|
cur.execute( "INSERT INTO bill_freq values ( 1, 'Annual', 1 )" )
|
||||||
cur.execute( "INSERT INTO bill_freq values ( 2, 'Quarterly', 4 )" )
|
cur.execute( "INSERT INTO bill_freq values ( 2, 'Quarterly', 4 )" )
|
||||||
cur.execute( "INSERT INTO bill_freq values ( 3, 'Monthly', 12 )" )
|
cur.execute( "INSERT INTO bill_freq values ( 3, 'Quarterly (forced)', 4 )" )
|
||||||
|
cur.execute( "INSERT INTO bill_freq values ( 4, 'Monthly', 12 )" )
|
||||||
# start with no specific Tab/bill_type to show, and dont show_estimated
|
# start with no specific Tab/bill_type to show, and dont show_estimated
|
||||||
cur.execute( "INSERT INTO bill_ui values ( 1, null, 0 )" )
|
cur.execute( "INSERT INTO bill_ui values ( 1, null, 0 )" )
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|||||||
@@ -21,6 +21,22 @@
|
|||||||
<div class="pt-2 containerfluid row">
|
<div class="pt-2 containerfluid row">
|
||||||
<h3 align="center">Bill Details (go to <a href="/">Finance Tracker</a>)</h3>
|
<h3 align="center">Bill Details (go to <a href="/">Finance Tracker</a>)</h3>
|
||||||
|
|
||||||
|
{# DEBUG totals if needed
|
||||||
|
<table>
|
||||||
|
{% for bt in total %}
|
||||||
|
<tr><td></td><td> {{bt}}:</td>
|
||||||
|
{% for yr in range( 2025, 2032 ) %}
|
||||||
|
{% if yr in total[bt] %}
|
||||||
|
<td>
|
||||||
|
{{total[bt][yr]}}
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
#}
|
||||||
|
|
||||||
<div class="mt-4 col-7">
|
<div class="mt-4 col-7">
|
||||||
<div class="row align-items-center">
|
<div class="row align-items-center">
|
||||||
<button id="new-bill-type-button" class="mb-3 px-0 offset-4 col-2 btn btn-success bg-success-subtle text-success" onCLick="StartNewBillType()"><span class="bi bi-plus-lg"> New Bill Type</span></button>
|
<button id="new-bill-type-button" class="mb-3 px-0 offset-4 col-2 btn btn-success bg-success-subtle text-success" onCLick="StartNewBillType()"><span class="bi bi-plus-lg"> New Bill Type</span></button>
|
||||||
|
|||||||
Reference in New Issue
Block a user