Compare commits
5 Commits
5914f3fdd4
...
4b63b8bd44
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b63b8bd44 | |||
| a0d9ac45cd | |||
| d80cffa0dd | |||
| 2459dc6ea1 | |||
| 95d792e72f |
8
TODO
8
TODO
@@ -1,16 +1,16 @@
|
|||||||
UI:
|
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:
|
For bills:
|
||||||
* estimate future bills/growth:
|
* 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)
|
[DONE] - need to handle future only bills
|
||||||
- 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
|
|
||||||
- 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 processed on the day of the bill
|
||||||
- inflation can then be put to a more realistic quarterly figure
|
- 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)
|
* 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:
|
LONGER/HARDER:
|
||||||
* need to work out 'first bill' and 'last bill' to auto-fill missing bills based on
|
* need to work out 'first bill' and 'last bill' to auto-fill missing bills based on
|
||||||
|
|||||||
6
bills.py
6
bills.py
@@ -213,9 +213,7 @@ def actually_add_estimated_new_quarter_bill( bill_type, bill_info, yr, q ):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def actually_add_estimated_new_quarter_bill_forced( bill_type, bill_info, yr, q ):
|
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]
|
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)
|
amt=last_yrs_qtr_amount*(1+bill_info[bill_type]['growth']/100)
|
||||||
new_date = f'{yr}-{q*3:02d}-01'
|
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 )
|
||||||
@@ -309,6 +307,10 @@ def process_bill_data(bd, bt, bf):
|
|||||||
|
|
||||||
for bill in bd:
|
for bill in bd:
|
||||||
bill_type = bill['bill_type_id']
|
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])
|
yr= int(bill['bill_date'][:4])
|
||||||
# new bill type
|
# new bill type
|
||||||
if not bill_type in bill_info:
|
if not bill_type in bill_info:
|
||||||
|
|||||||
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
|
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_ui, save_ui
|
||||||
from db import get_bill_types, insert_bill_type, update_bill_type, delete_bill_type, use_growth
|
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 defines import END_YEAR
|
||||||
from collections import defaultdict, Counter
|
from collections import defaultdict, Counter
|
||||||
from datetime import datetime, date
|
from datetime import datetime, date
|
||||||
@@ -168,13 +168,17 @@ def UpdateBillType():
|
|||||||
def InsertBill():
|
def InsertBill():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
# last param is estimated - e.g. anything via GUI is not an estimate, but is a real bill
|
# 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"
|
return "200"
|
||||||
|
|
||||||
@app.route('/updatebill', methods=['POST'])
|
@app.route('/updatebill', methods=['POST'])
|
||||||
def UpdateBill():
|
def UpdateBill():
|
||||||
data = request.get_json()
|
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"
|
return "200"
|
||||||
|
|
||||||
@app.route('/delbilltype', methods=['POST'])
|
@app.route('/delbilltype', methods=['POST'])
|
||||||
|
|||||||
@@ -38,8 +38,12 @@
|
|||||||
#}
|
#}
|
||||||
|
|
||||||
<div class="mt-4 col-7">
|
<div class="mt-4 col-7">
|
||||||
<div class="row align-items-center">
|
<div class="row">
|
||||||
<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="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"> <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">
|
<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 %}
|
{% for bf in bill_freqs %}
|
||||||
@@ -129,19 +133,34 @@
|
|||||||
<!-- right-hand-side, bill types (e.g. gas, phone, etc.) -->
|
<!-- right-hand-side, bill types (e.g. gas, phone, etc.) -->
|
||||||
|
|
||||||
<div class="pt-4 col-5">
|
<div class="pt-4 col-5">
|
||||||
<div class="row align-items-center">
|
<div class="row">
|
||||||
<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="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">
|
<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 %}
|
{% for bt in bill_types %}
|
||||||
<option value={{bt.id}}>{{bt.name}}</option>
|
<option value={{bt.id}}>{{bt.name}}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</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-4 d-none">
|
||||||
<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="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()">
|
<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>
|
<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>
|
</div>
|
||||||
|
|
||||||
<!-- create tabbed view for each bill type -->
|
<!-- create tabbed view for each bill type -->
|
||||||
@@ -165,9 +184,9 @@
|
|||||||
{% for bd in bill_data %}
|
{% for bd in bill_data %}
|
||||||
{% if loop.first %}
|
{% if loop.first %}
|
||||||
<div class="row pt-2">
|
<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">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">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">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 class="px-0 col-4"><label class="form-control text-center border-0 fw-bold bg-body-tertiary rounded-0">Actions</ ></div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -179,8 +198,12 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
{% set classes="form-control text-center" %}
|
{% set classes="form-control text-center" %}
|
||||||
{% endif %}
|
{% 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="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>
|
{% 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>
|
<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 %}
|
{% 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>
|
<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()
|
function NewBill()
|
||||||
{
|
{
|
||||||
$.ajax( { type: 'POST', url: '/newbill',
|
if( $('#new-bill-data-growth').hasClass('d-none') )
|
||||||
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 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' } } )
|
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()
|
function CancelNewBill()
|
||||||
@@ -264,7 +306,7 @@
|
|||||||
$.ajax( { type: 'POST', url: '/updatebill',
|
$.ajax( { type: 'POST', url: '/updatebill',
|
||||||
contentType: 'application/json', data: JSON.stringify( {
|
contentType: 'application/json', data: JSON.stringify( {
|
||||||
'id': id,
|
'id': id,
|
||||||
'name': $('#bill-data-type-'+id).val(),
|
'bill_type': $('#bill-data-type-'+id).val(),
|
||||||
'bill_date' : $('#bill-data-date-'+id).val(),
|
'bill_date' : $('#bill-data-date-'+id).val(),
|
||||||
'amount': $('#bill-data-amount-'+id).val() } ),
|
'amount': $('#bill-data-amount-'+id).val() } ),
|
||||||
success: function() { window.location='bills' } } )
|
success: function() { window.location='bills' } } )
|
||||||
@@ -396,6 +438,35 @@
|
|||||||
// make the new bill drop-down default to the same as the current tab
|
// make the new bill drop-down default to the same as the current tab
|
||||||
$("#new-bill-data-type").val( {{bill_ui.last_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>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user