Compare commits

..

5 Commits

4 changed files with 101 additions and 24 deletions

8
TODO
View File

@@ -1,16 +1,16 @@
UI:
future bill (like for health) when I click change should either get fancy and allow toggle, or at least just disable the text field with future in it
For bills:
* estimate future bills/growth:
[DONE] - calculate pragmatic min/avg/max/simple
- need to handle future only bills (like health insurance which only kicks in when I quit)
- could work by guessing start dates of bills for now (hack); OR
- we put some smarts in so they future bills have a base and kick in the date I quit
[DONE] - need to handle future only bills
- 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 processed on the day of the bill
- inflation can then be put to a more realistic quarterly figure
* 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

View File

@@ -213,9 +213,7 @@ def actually_add_estimated_new_quarter_bill( bill_type, bill_info, yr, q ):
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 )
@@ -309,6 +307,10 @@ def process_bill_data(bd, bt, bf):
for bill in bd:
bill_type = bill['bill_type_id']
if bill['bill_date'] == 'future':
print("Having a future data - skip this one")
continue
yr= int(bill['bill_date'][:4])
# new bill type
if not bill_type in bill_info:

10
main.py
View File

@@ -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
from db import get_bill_ui, save_ui
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
from bills import process_bill_data, calc_future_totals, set_bill_type_growth
from defines import END_YEAR
from collections import defaultdict, Counter
from datetime import datetime, date
@@ -168,13 +168,17 @@ def UpdateBillType():
def InsertBill():
data = request.get_json()
# last param is estimated - e.g. anything via GUI is not an estimate, but is a real bill
new_bill( data['name'], data['amount'], data['bill_date'], 0 )
if 'bill_date' in data:
new_bill( data['bill_type'], data['amount'], data['bill_date'], 0 )
else:
new_bill( data['bill_type'], data['amount'], 'future', 0 )
set_bill_type_growth( data['bill_type'], 0, 0, 0, data['growth'] )
return "200"
@app.route('/updatebill', methods=['POST'])
def UpdateBill():
data = request.get_json()
update_bill_data( data['id'], data['name'], data['amount'], data['bill_date'] )
update_bill_data( data['id'], data['bill_type'], data['amount'], data['bill_date'] )
return "200"
@app.route('/delbilltype', methods=['POST'])

View File

@@ -38,8 +38,12 @@
#}
<div class="mt-4 col-7">
<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>
<div class="row">
<div class="col-2 form-control-inline d-none new-bill-type-class">Bill Type</div>
<div class="col-2 form-control-inline d-none new-bill-type-class">Frequency</div>
</div>
<div class="row align-items-center mb-3">
<button id="new-bill-type-button" class="mt-4 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>
<div class="new-bill-type-class px-0 col-2 d-none"> <input type="text" class="form-control text-end float-end border border-primary" id="new-bill-type-name"></div>
<div class="new-bill-type-class px-0 col-2 d-none"><select id="new-bill-type-freq" class="form-select text-center">
{% for bf in bill_freqs %}
@@ -129,19 +133,34 @@
<!-- right-hand-side, bill types (e.g. gas, phone, etc.) -->
<div class="pt-4 col-5">
<div class="row align-items-center">
<button id="new-bill-data-button" class="mb-3 px-0 offset-6 col-2 btn btn-success bg-success-subtle text-success" onCLick="StartNewBillData()"><span class="bi bi-plus-lg"> New Bill</span></button>
<div class="row">
<div class="col-2 form-control-inline d-none new-bill-data-class">Bill Type</div>
<div id="new-bill-data-date-label" class="col-4 form-control-inline d-none new-bill-data-class">Date</div>
<div id="new-bill-data-growth-label" class="col-4 form-control-inline d-none">Est. Annual Growth</div>
<div class="col-2 form-control-inline d-none new-bill-data-class">Amount</div>
</div>
<div class="row align-items-center mb-3">
<button id="new-bill-data-button" class="mt-4 px-0 offset-8 col-2 btn btn-success bg-success-subtle text-success" onCLick="StartNewBillData()"><span class="bi bi-plus-lg"> New Bill</span></button>
<div class="new-bill-data-class px-0 col-2 d-none"> <select id="new-bill-data-type" class="form-select text-end float-end border border-primary">
{% for bt in bill_types %}
<option value={{bt.id}}>{{bt.name}}</option>
{% endfor %}
</select>
</div>
<div class="new-bill-data-class px-0 col-2 d-none"> <input type="date" class="form-control text-end float-end border border-primary" id="new-bill-data-date"> </div>
<div class="new-bill-data-class px-0 col-2 d-none"> <input type="number" class="form-control text-end float-end border border-primary" id="new-bill-data-amount"> </div>
<div class="new-bill-data-class px-0 col-4 d-none">
<div class="input-group" style="max-width: 300px;">
<input type="date" class="form-control" id="new-bill-data-date">
<input type="text" class="form-control d-none" id="new-bill-data-growth">
<button class="btn btn-outline-danger" type="button" id="toggleDateBtn">When quit</button>
</div>
</div>
<div class="new-bill-data-class px-0 col-2 d-none">
<input type="number" class="form-control text-end float-end border border-primary" id="new-bill-data-amount">
</div>
<button id="save-bill" class="new-bill-data-class px-0 col-1 btn btn-success bg-success-subtle text-success d-none" onClick="NewBill()">
<span class="bi bi-floppy"></span> Save </button>
<button class="new-bill-data-class px-0 col-1 btn btn-danger bg-danger-subtle text-danger d-none" onClick="CancelNewBill()" ><span class="bi bi-x"> Cancel</span> </button>
<button class="new-bill-data-class px-0 col-1 btn btn-danger bg-danger-subtle text-danger d-none" onClick="CancelNewBill()" >
<span class="bi bi-x"> Cancel</span> </button>
</div>
<!-- create tabbed view for each bill type -->
@@ -165,9 +184,9 @@
{% for bd in bill_data %}
{% if loop.first %}
<div class="row pt-2">
<div class="p-0 col-2"> <label class="form-control text-center border-0 fw-bold bg-body-tertiary rounded-0">Name</ > </div>
<div class="p-0 col-2"> <label class="form-control text-center border-0 fw-bold bg-body-tertiary rounded-0">Date</ > </div>
<div class="p-0 col-2"> <label class="form-control text-center border-0 fw-bold bg-body-tertiary rounded-0">Amount</ > </div>
<div class="p-0 col-2"><label class="form-control text-center border-0 fw-bold bg-body-tertiary rounded-0">Name</ ></div>
<div class="p-0 col-2"><label class="form-control text-center border-0 fw-bold bg-body-tertiary rounded-0">Date</ ></div>
<div class="p-0 col-2"><label class="form-control text-center border-0 fw-bold bg-body-tertiary rounded-0">Amount</ ></div>
<div class="px-0 col-4"><label class="form-control text-center border-0 fw-bold bg-body-tertiary rounded-0">Actions</ ></div>
</div>
{% endif %}
@@ -179,8 +198,12 @@
<div class="row">
{% set classes="form-control text-center" %}
{% endif %}
<div class="px-0 col-2"> <input type="text" class="{{classes}}" id="bill-data-type-{{bd.id}}" value="{{ bd.name }}" disabled> </div>
<div class="px-0 col-2"> <input type="date" class="{{classes}}" id="bill-data-date-{{bd.id}}" value="{{ bd.bill_date }}" disabled> </div>
<div class="px-0 col-2"> <input type="text" class="{{classes}}" id="bill-data-type-{{bd.id}}" value="{{ bd.name }}" disabled> </div>
{% if bd.bill_date == 'future' %}
<div class="px-0 col-2"> <input type="text" class="{{classes}}" id="bill-data-date-{{bd.id}}" value="{{ bd.bill_date }}" disabled> </div>
{% else %}
<div class="px-0 col-2"> <input type="date" class="{{classes}}" id="bill-data-date-{{bd.id}}" value="{{ bd.bill_date }}" disabled> </div>
{% endif %}
<div class="px-0 col-2"> <input type="number" class="{{classes}}" id="bill-data-amount-{{bd.id}}" value="{{ bd.amount }}" disabled> </div>
{% if bd.estimated == 0 %}
<button id="bill-data-chg-{{bd.id}}" class="px-0 col-2 btn btn-success bg-success-subtle text-success" onClick="StartUpdateBill( {{bd.id}} )"><span class="bi bi-pencil-square"> Change</button>
@@ -222,9 +245,28 @@
function NewBill()
{
$.ajax( { type: 'POST', url: '/newbill',
contentType: 'application/json', data: JSON.stringify( { 'name': $('#new-bill-data-type').val(), 'amount': $('#new-bill-data-amount').val(), 'bill_date': $('#new-bill-data-date').val() } ),
if( $('#new-bill-data-growth').hasClass('d-none') )
{
// if growth is hidden, then we have normal bill
$.ajax( { type: 'POST', url: '/newbill',
contentType: 'application/json',
data: JSON.stringify( {
'bill_type': $('#new-bill-data-type').val(),
'amount': $('#new-bill-data-amount').val(),
'bill_date': $('#new-bill-data-date').val() } ),
success: function() { window.location='bills' } } )
}
else
{
// if growth is visible, then we have future bill/growth & no date
$.ajax( { type: 'POST', url: '/newbill',
contentType: 'application/json',
data: JSON.stringify( {
'bill_type': $('#new-bill-data-type').val(),
'amount': $('#new-bill-data-amount').val(),
'growth': $('#new-bill-data-growth').val() } ),
success: function() { window.location='bills' } } )
}
}
function CancelNewBill()
@@ -264,7 +306,7 @@
$.ajax( { type: 'POST', url: '/updatebill',
contentType: 'application/json', data: JSON.stringify( {
'id': id,
'name': $('#bill-data-type-'+id).val(),
'bill_type': $('#bill-data-type-'+id).val(),
'bill_date' : $('#bill-data-date-'+id).val(),
'amount': $('#bill-data-amount-'+id).val() } ),
success: function() { window.location='bills' } } )
@@ -396,6 +438,35 @@
// make the new bill drop-down default to the same as the current tab
$("#new-bill-data-type").val( {{bill_ui.last_tab}} )
} )
$(function () {
let disabled = false;
$('#toggleDateBtn').on('click', function () {
disabled = !disabled;
if (disabled) {
$('#new-bill-data-date').addClass('d-none')
$('#new-bill-data-growth').removeClass('d-none')
$(this)
.removeClass('btn-outline-danger')
.addClass('btn-outline-success')
.html('Normal date');
$('#new-bill-data-date-label').addClass('d-none')
$('#new-bill-data-growth-label').removeClass('d-none')
} else {
$('#new-bill-data-date').removeClass('d-none')
$('#new-bill-data-growth').addClass('d-none')
$(this)
.removeClass('btn-outline-success')
.addClass('btn-outline-danger')
.html('When quit');
$('#new-bill-data-date-label').removeClass('d-none')
$('#new-bill-data-growth-label').addClass('d-none')
}
});
});
</script>
</body>
</html>