Skip to content

Commit

Permalink
issue #183 - Add UI support for specifying Interest Charge, Interest …
Browse files Browse the repository at this point in the history
…Paid, Payment, Late Fee, and Other Fee regexes on each account
  • Loading branch information
jantman committed Mar 8, 2018
1 parent 0da7e94 commit 775c448
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Unreleased Changes
* Add UI link to ignore reconciling an OFXTransaction if there will not be a matching Transaction.
* Remove default values for the ``Account`` model's ``re_`` fields in preparation for actually using them.
* Replace the ``Account`` model's ``re_fee`` field with separate ``re_late_fee`` and ``re_other_fee`` fields.
* Add UI support for specifying Interest Charge, Interest Paid, Payment, Late Fee, and Other Fee regexes on each account.

* Upgrade chromedriver in TravisCI builds from 2.33 to 2.36, to fix failing acceptance tests caused by Ubuntu upgrade from Chrome 64 to 65.

Expand Down
10 changes: 10 additions & 0 deletions biweeklybudget/flaskapp/static/js/accounts_modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ function accountModalDivForm() {
)
.addCheckbox('account_frm_negate_ofx', 'negate_ofx_amounts', 'Negate OFX Amounts', false)
.addCheckbox('account_frm_reconcile_trans', 'reconcile_trans', 'Reconcile Transactions?', true)
.addText('account_frm_re_interest_charge', 're_interest_charge', 'Interest Charge Regex', { helpBlock: 'If specified, OFX Transactions with name/memo matching this regex will be marked as interest charges (and not reconciled).'})
.addText('account_frm_re_interest_paid', 're_interest_paid', 'Interest Paid Regex', { helpBlock: 'If specified, OFX Transactions with name/memo matching this regex will be marked as interest payments (and not reconciled).'})
.addText('account_frm_re_payment', 're_payment', 'Payment Regex', { helpBlock: 'If specified, OFX Transactions with name/memo matching this regex will be marked as payments (and not reconciled).'})
.addText('account_frm_re_late_fee', 're_late_fee', 'Late Fee Regex', { helpBlock: 'If specified, OFX Transactions with name/memo matching this regex will be marked as late fees (and not reconciled).'})
.addText('account_frm_re_other_fee', 're_other_fee', 'Other Fee Regex', { helpBlock: 'If specified, OFX Transactions with name/memo matching this regex will be marked as other fees (and not reconciled).'})
.addCurrency('account_frm_credit_limit', 'credit_limit', 'Credit Limit')
.addText('account_frm_apr', 'apr', 'APR', { helpBlock: 'If you know the margin added to the Prime Rate for this card, use the Margin field instead.'})
.addText('account_frm_margin', 'prime_rate_margin', 'Margin', { helpBlock: 'If known, the margin added to the US Prime Rate to determine the APR.'})
Expand Down Expand Up @@ -153,6 +158,11 @@ function accountModalDivFillAndShow(msg) {
$('#account_frm_reconcile_trans').prop('checked', false);
}
$('#account_frm_vault_creds_path').val(msg['vault_creds_path']);
if(msg['re_interest_charge'] != null) { $('#account_frm_re_interest_charge').val(msg['re_interest_charge']); }
if(msg['re_interest_paid'] != null) { $('#account_frm_re_interest_paid').val(msg['re_interest_paid']); }
if(msg['re_payment'] != null) { $('#account_frm_re_payment').val(msg['re_payment']); }
if(msg['re_late_fee'] != null) { $('#account_frm_re_late_fee').val(msg['re_late_fee']); }
if(msg['re_other_fee'] != null) { $('#account_frm_re_other_fee').val(msg['re_other_fee']); }
$("#modalDiv").modal('show');
}

Expand Down
21 changes: 21 additions & 0 deletions biweeklybudget/flaskapp/views/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
from flask import render_template, jsonify
from decimal import Decimal
import json
import re

from biweeklybudget.flaskapp.app import app
from biweeklybudget.flaskapp.views.formhandlerview import FormHandlerView
Expand All @@ -51,6 +52,14 @@

logger = logging.getLogger(__name__)

RE_FIELD_NAMES = [
're_interest_charge',
're_interest_paid',
're_payment',
're_late_fee',
're_other_fee'
]


class AccountsView(MethodView):
"""
Expand Down Expand Up @@ -151,6 +160,13 @@ def validate(self, data):
errors['min_payment_class_name'].append(
'Invalid minimum payment class name'
)
for f in RE_FIELD_NAMES:
if data[f].strip() == '':
continue
try:
re.compile(data[f])
except Exception:
errors[f].append('Invalid regular expression.')
if have_errors:
return errors
return None
Expand Down Expand Up @@ -219,6 +235,11 @@ def submit(self, data):
account.is_active = True
else:
account.is_active = False
for f in RE_FIELD_NAMES:
data[f] = data[f].strip()
if data[f] == '':
data[f] = None
setattr(account, f, data[f])
logger.info('%s: %s', action, account.as_dict)
db_session.add(account)
db_session.commit()
Expand Down
108 changes: 108 additions & 0 deletions biweeklybudget/tests/acceptance/flaskapp/views/test_accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ def test_10_verify_db(self, testdb):
assert acct.interest_class_name is None
assert acct.min_payment_class_name is None
assert acct.is_active is True
assert acct.re_interest_charge == '^interest-charge'
assert acct.re_interest_paid == '^interest-paid'
assert acct.re_payment == '^(payment|thank you)'
assert acct.re_late_fee == '^re-late-fee'
assert acct.re_other_fee == '^re-other-fee'

def test_11_get_acct1_url(self, base_url, selenium):
self.get(selenium, base_url + '/accounts/1')
Expand Down Expand Up @@ -218,6 +223,23 @@ def test_11_get_acct1_url(self, base_url, selenium):
assert selenium.find_element_by_id(
'account_frm_min_pay_class_name').is_displayed() is False
# END CREDIT
# BEGIN REs
assert selenium.find_element_by_id(
'account_frm_re_interest_charge'
).get_attribute('value') == '^interest-charge'
assert selenium.find_element_by_id(
'account_frm_re_interest_paid'
).get_attribute('value') == '^interest-paid'
assert selenium.find_element_by_id(
'account_frm_re_payment'
).get_attribute('value') == '^(payment|thank you)'
assert selenium.find_element_by_id(
'account_frm_re_late_fee'
).get_attribute('value') == '^re-late-fee'
assert selenium.find_element_by_id(
'account_frm_re_other_fee'
).get_attribute('value') == '^re-other-fee'
# END REs
assert selenium.find_element_by_id('account_frm_active').is_selected()

def test_12_edit_acct1(self, base_url, selenium):
Expand Down Expand Up @@ -272,6 +294,11 @@ def test_20_verify_db(self, testdb):
assert acct.interest_class_name is None
assert acct.min_payment_class_name is None
assert acct.is_active is True
assert acct.re_interest_charge is None
assert acct.re_interest_paid is None
assert acct.re_payment is None
assert acct.re_late_fee is None
assert acct.re_other_fee is None

def test_21_get_acct2_click(self, base_url, selenium):
self.get(selenium, base_url + '/accounts')
Expand Down Expand Up @@ -317,6 +344,23 @@ def test_21_get_acct2_click(self, base_url, selenium):
assert selenium.find_element_by_id(
'account_frm_min_pay_class_name').is_displayed() is False
# END CREDIT
# BEGIN REs
assert selenium.find_element_by_id(
'account_frm_re_interest_charge'
).get_attribute('value') == ''
assert selenium.find_element_by_id(
'account_frm_re_interest_paid'
).get_attribute('value') == ''
assert selenium.find_element_by_id(
'account_frm_re_payment'
).get_attribute('value') == ''
assert selenium.find_element_by_id(
'account_frm_re_late_fee'
).get_attribute('value') == ''
assert selenium.find_element_by_id(
'account_frm_re_other_fee'
).get_attribute('value') == ''
# END REs
assert selenium.find_element_by_id('account_frm_active').is_selected()

def test_22_edit_acct2(self, base_url, selenium):
Expand Down Expand Up @@ -344,6 +388,20 @@ def test_22_edit_acct2(self, base_url, selenium):
selenium.find_element_by_id('account_frm_negate_ofx').click()
selenium.find_element_by_id('account_frm_reconcile_trans').click()
selenium.find_element_by_id('account_frm_active').click()
# BEGIN REs
selenium.find_element_by_id(
'account_frm_re_interest_charge'
).send_keys('my-re-ic$')
selenium.find_element_by_id(
'account_frm_re_interest_paid'
).send_keys('my-re-ip$')
selenium.find_element_by_id(
'account_frm_re_payment'
).send_keys('my-re-p$')
selenium.find_element_by_id(
'account_frm_re_late_fee'
).send_keys('my-re-lf$')
# END REs
# submit the form
selenium.find_element_by_id('modalSaveButton').click()
self.wait_for_jquery_done(selenium)
Expand Down Expand Up @@ -372,6 +430,11 @@ def test_23_verify_db(self, testdb):
assert acct.interest_class_name is None
assert acct.min_payment_class_name is None
assert acct.is_active is False
assert acct.re_interest_charge == 'my-re-ic$'
assert acct.re_interest_paid == 'my-re-ip$'
assert acct.re_payment == 'my-re-p$'
assert acct.re_late_fee == 'my-re-lf$'
assert acct.re_other_fee is None

def test_30_verify_db(self, testdb):
acct = testdb.query(Account).get(3)
Expand All @@ -390,6 +453,11 @@ def test_30_verify_db(self, testdb):
assert acct.interest_class_name == 'AdbCompoundedDaily'
assert acct.min_payment_class_name == 'MinPaymentAmEx'
assert acct.is_active is True
assert acct.re_interest_charge is None
assert acct.re_interest_paid is None
assert acct.re_payment is None
assert acct.re_late_fee is None
assert acct.re_other_fee is None

def test_31_get_acct3_click(self, base_url, selenium):
self.get(selenium, base_url + '/accounts')
Expand Down Expand Up @@ -487,6 +555,11 @@ def test_33_verify_db(self, testdb):
assert acct.interest_class_name == 'AdbCompoundedDaily'
assert acct.min_payment_class_name == 'MinPaymentAmEx'
assert acct.is_active is True
assert acct.re_interest_charge is None
assert acct.re_interest_paid is None
assert acct.re_payment is None
assert acct.re_late_fee is None
assert acct.re_other_fee is None

def test_40_verify_db(self, testdb):
acct = testdb.query(Account).get(4)
Expand All @@ -505,6 +578,11 @@ def test_40_verify_db(self, testdb):
assert acct.interest_class_name == 'AdbCompoundedDaily'
assert acct.min_payment_class_name == 'MinPaymentDiscover'
assert acct.is_active is True
assert acct.re_interest_charge is None
assert acct.re_interest_paid is None
assert acct.re_payment is None
assert acct.re_late_fee is None
assert acct.re_other_fee is None

def test_41_get_acct4_url(self, base_url, selenium):
self.get(selenium, base_url + '/accounts/4')
Expand Down Expand Up @@ -628,6 +706,11 @@ def test_43_verify_db(self, testdb):
assert acct.interest_class_name == 'AdbCompoundedDaily'
assert acct.min_payment_class_name == 'MinPaymentCiti'
assert acct.is_active is False
assert acct.re_interest_charge is None
assert acct.re_interest_paid is None
assert acct.re_payment is None
assert acct.re_late_fee is None
assert acct.re_other_fee is None

def test_50_verify_db(self, testdb):
acct = testdb.query(Account).get(5)
Expand All @@ -646,6 +729,11 @@ def test_50_verify_db(self, testdb):
assert acct.interest_class_name is None
assert acct.min_payment_class_name is None
assert acct.is_active is True
assert acct.re_interest_charge is None
assert acct.re_interest_paid is None
assert acct.re_payment is None
assert acct.re_late_fee is None
assert acct.re_other_fee is None

def test_51_get_acct5_click(self, base_url, selenium):
self.get(selenium, base_url + '/accounts')
Expand Down Expand Up @@ -729,6 +817,11 @@ def test_53_verify_db(self, testdb):
assert acct.interest_class_name is None
assert acct.min_payment_class_name is None
assert acct.is_active is True
assert acct.re_interest_charge is None
assert acct.re_interest_paid is None
assert acct.re_payment is None
assert acct.re_late_fee is None
assert acct.re_other_fee is None

def test_60_verify_db(self, testdb):
max_id = max([
Expand Down Expand Up @@ -815,6 +908,11 @@ def test_62_verify_db(self, testdb):
assert acct.interest_class_name is None
assert acct.min_payment_class_name is None
assert acct.is_active is True
assert acct.re_interest_charge is None
assert acct.re_interest_paid is None
assert acct.re_payment is None
assert acct.re_late_fee is None
assert acct.re_other_fee is None

def test_70_verify_db(self, testdb):
max_id = max([
Expand Down Expand Up @@ -927,6 +1025,11 @@ def test_72_verify_db(self, testdb):
assert acct.interest_class_name == 'AdbCompoundedDaily'
assert acct.min_payment_class_name == 'MinPaymentCiti'
assert acct.is_active is False
assert acct.re_interest_charge is None
assert acct.re_interest_paid is None
assert acct.re_payment is None
assert acct.re_late_fee is None
assert acct.re_other_fee is None

def test_80_verify_db(self, testdb):
max_id = max([
Expand Down Expand Up @@ -1014,3 +1117,8 @@ def test_82_verify_db(self, testdb):
assert acct.interest_class_name is None
assert acct.min_payment_class_name is None
assert acct.is_active is True
assert acct.re_interest_charge is None
assert acct.re_interest_paid is None
assert acct.re_payment is None
assert acct.re_late_fee is None
assert acct.re_other_fee is None
7 changes: 6 additions & 1 deletion biweeklybudget/tests/fixtures/sampledata.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,12 @@ def _bank_one(self):
ofx_cat_memo_to_name=True,
ofxgetter_config_json='{"foo": "bar"}',
vault_creds_path='secret/foo/bar/BankOne',
acct_type=AcctType.Bank
acct_type=AcctType.Bank,
re_interest_charge='^interest-charge',
re_interest_paid='^interest-paid',
re_payment='^(payment|thank you)',
re_late_fee='^re-late-fee',
re_other_fee='^re-other-fee'
)
statements = [
OFXStatement(
Expand Down

0 comments on commit 775c448

Please sign in to comment.