Skip to content

Commit

Permalink
issue #178 - update BiweeklyPayPeriod to handle split-budget Transact…
Browse files Browse the repository at this point in the history
…ions
  • Loading branch information
jantman committed Mar 11, 2018
1 parent 80b7635 commit 517b41c
Show file tree
Hide file tree
Showing 3 changed files with 247 additions and 115 deletions.
111 changes: 67 additions & 44 deletions biweeklybudget/biweeklypayperiod.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,36 +428,54 @@ def _make_budget_sums(self):
'is_income': b.is_income
}
for t in self.transactions_list:
if t['budget_id'] not in res:
# Issue #161 - inactive budget, but transaction for it in this
# period. Add it if it's a periodic budget.
b = self._db.query(Budget).get(t['budget_id'])
if not b.is_periodic:
continue
res[b.id] = {
'budget_amount': b.starting_balance,
'allocated': Decimal('0.0'),
'spent': Decimal('0.0'),
'trans_total': Decimal('0.0'),
'is_income': b.is_income
}
# if a ScheduledTransaction, update some values and then continue
if t['type'] == 'ScheduledTransaction':
res[t['budget_id']]['allocated'] += t['amount']
res[t['budget_id']]['trans_total'] += t['amount']
for budg_id, budg_data in t['budgets'].items():
if budg_id not in res:
# Issue #161 - inactive budget, but transaction for it
# in this period. Add it if it's a periodic budget.
b = self._db.query(Budget).get(budg_id)
if not b.is_periodic:
continue
res[b.id] = {
'budget_amount': b.starting_balance,
'allocated': Decimal('0.0'),
'spent': Decimal('0.0'),
'trans_total': Decimal('0.0'),
'is_income': b.is_income
}
res[budg_id]['allocated'] += budg_data['amount']
res[budg_id]['trans_total'] += budg_data['amount']
continue
# t['type'] == 'Transaction'
res[t['budget_id']]['trans_total'] += t['amount']
if t['budgeted_amount'] is None:
res[t['budget_id']]['allocated'] += t['amount']
res[t['budget_id']]['spent'] += t['amount']
else:
# NOT a ScheduledTransaction; must be an actual Transaction
for budg_id, budg_data in t['budgets'].items():
if budg_id not in res:
# Issue #161 - inactive budget, but transaction for it
# in this period. Add it if it's a periodic budget.
b = self._db.query(Budget).get(budg_id)
if not b.is_periodic:
continue
res[b.id] = {
'budget_amount': b.starting_balance,
'allocated': Decimal('0.0'),
'spent': Decimal('0.0'),
'trans_total': Decimal('0.0'),
'is_income': b.is_income
}
# update the budget's transactions total and spent amount
res[budg_id]['trans_total'] += budg_data['amount']
res[budg_id]['spent'] += budg_data['amount']
if t['budgeted_amount'] is None:
res[budg_id]['allocated'] += budg_data['amount']
if t['budgeted_amount'] is not None:
# has a budgeted amount. It _shouldn't_ be possible to have a
# budgeted_amount without a planned_budget_id, but if that
# happens, allocate to the first budget.
if t.get('planned_budget_id', None) is None:
res[t['budget_id']]['allocated'] += t[
'budgeted_amount']
bid = list(t['budgets'].keys())[0]
else:
res[t['planned_budget_id']]['allocated'] += t[
'budgeted_amount']
res[t['budget_id']]['spent'] += t['amount']
bid = t['planned_budget_id']
res[bid]['allocated'] += t['budgeted_amount']
for b in res.keys():
if res[b]['trans_total'] > res[b]['allocated']:
res[b]['remaining'] = res[
Expand Down Expand Up @@ -549,10 +567,9 @@ def _trans_dict(self, t):
against.
* ``account_name`` (**str**) the name of the Account the transaction is
against.
* ``budget_id`` (**int**) the id of the Budget the transaction is
against.
* ``budget_name`` (**str**) the name of the Budget the transaction is
against.
* ``budgets`` (**dict**) dict of information on the Budgets this
Transaction is against. Keys are budget IDs (**int**), values are
dicts with keys "amount" (**Decimal**) and "name" (**string**).
* ``reconcile_id`` (**int**) the ID of the TxnReconcile, or None
* ``planned_budget_id`` (**int**) the id of the Budget the transaction
was planned against, if any. May be None.
Expand Down Expand Up @@ -591,15 +608,14 @@ def _dict_for_trans(self, t):
against.
* ``account_name`` (**str**) the name of the Account the transaction is
against.
* ``budget_id`` (**int**) the id of the Budget the transaction is
against.
* ``budget_name`` (**str**) the name of the Budget the transaction is
against.
* ``reconcile_id`` (**int**) the ID of the TxnReconcile, or None
* ``planned_budget_id`` (**int**) the id of the Budget the transaction
was planned against, if any. May be None.
* ``planned_budget_name`` (**str**) the name of the Budget the
transaction was planned against, if any. May be None.
* ``budgets`` (**dict**) dict of information on the Budgets this
Transaction is against. Keys are budget IDs (**int**), values are
dicts with keys "amount" (**Decimal**) and "name" (**string**).
:param t: transaction to describe
:type t: Transaction
Expand All @@ -616,10 +632,14 @@ def _dict_for_trans(self, t):
'amount': t.actual_amount,
'account_id': t.account_id,
'account_name': t.account.name,
'budget_id': t.budget_transactions[0].budget_id,
'budget_name': t.budget_transactions[0].budget.name,
'planned_budget_id': t.planned_budget_id,
'planned_budget_name': None
'planned_budget_name': None,
'budgets': {
bt.budget_id: {
'amount': bt.amount,
'name': bt.budget.name
} for bt in t.budget_transactions
}
}
if t.reconcile is None:
res['reconcile_id'] = None
Expand Down Expand Up @@ -654,11 +674,10 @@ def _dict_for_sched_trans(self, t):
against.
* ``account_name`` (**str**) the name of the Account the transaction is
against.
* ``budget_id`` (**int**) the id of the Budget the transaction is
against.
* ``budget_name`` (**str**) the name of the Budget the transaction is
against.
* ``reconcile_id`` (**int**) the ID of the TxnReconcile, or None
* ``budgets`` (**dict**) dict of information on the Budgets this
Transaction is against. Keys are budget IDs (**int**), values are
dicts with keys "amount" (**Decimal**) and "name" (**string**).
:param t: ScheduledTransaction to describe
:type t: ScheduledTransaction
Expand All @@ -675,9 +694,13 @@ def _dict_for_sched_trans(self, t):
'budgeted_amount': None,
'account_id': t.account_id,
'account_name': t.account.name,
'budget_id': t.budget_id,
'budget_name': t.budget.name,
'reconcile_id': None
'reconcile_id': None,
'budgets': {
t.budget_id: {
'name': t.budget.name,
'amount': t.amount
}
}
}
if t.schedule_type == 'date':
res['date'] = t.date
Expand Down
74 changes: 45 additions & 29 deletions biweeklybudget/tests/acceptance/test_biweeklypayperiod.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ def test_1_confirm_pay_period_start(self, testdb):
def test_2_add_data(self, testdb):
acct = testdb.query(Account).get(1)
budg = testdb.query(Budget).get(1)
budg2 = testdb.query(Budget).get(2)
st_daynum = ScheduledTransaction(
amount=Decimal('111.11'),
description='ST_day_9',
Expand Down Expand Up @@ -323,7 +324,10 @@ def test_2_add_data(self, testdb):
)
testdb.add(t_foo)
t_bar = Transaction(
budget_amounts={budg: Decimal('666.66')},
budget_amounts={
budg: Decimal('666.66'),
budg2: Decimal('100.00')
},
date=date(2017, 4, 16),
description='Trans_bar',
account=acct
Expand All @@ -344,8 +348,9 @@ def test_3_ignore_scheduled_converted_to_real_trans(self, testdb):
'account_id': 1,
'account_name': 'BankOne',
'amount': Decimal('222.22'),
'budget_id': 1,
'budget_name': 'Periodic1',
'budgets': {
1: {'name': 'Periodic1', 'amount': Decimal('222.22')}
},
'budgeted_amount': None,
'date': None,
'description': 'ST_pp_1',
Expand All @@ -359,8 +364,9 @@ def test_3_ignore_scheduled_converted_to_real_trans(self, testdb):
'account_id': 1,
'account_name': 'BankOne',
'amount': Decimal('333.33'),
'budget_id': 1,
'budget_name': 'Periodic1',
'budgets': {
1: {'name': 'Periodic1', 'amount': Decimal('333.33')}
},
'budgeted_amount': None,
'date': None,
'description': 'ST_pp_3',
Expand All @@ -374,8 +380,9 @@ def test_3_ignore_scheduled_converted_to_real_trans(self, testdb):
'account_id': 1,
'account_name': 'BankOne',
'amount': Decimal('555.55'),
'budget_id': 1,
'budget_name': 'Periodic1',
'budgets': {
1: {'name': 'Periodic1', 'amount': Decimal('555.55')}
},
'budgeted_amount': None,
'date': date(2017, 4, 8),
'description': 'Trans_foo',
Expand All @@ -392,8 +399,9 @@ def test_3_ignore_scheduled_converted_to_real_trans(self, testdb):
'account_id': 1,
'account_name': 'BankOne',
'amount': Decimal('111.33'),
'budget_id': 1,
'budget_name': 'Periodic1',
'budgets': {
1: {'name': 'Periodic1', 'amount': Decimal('111.33')}
},
'budgeted_amount': Decimal('111.11'),
'date': date(2017, 4, 9),
'description': 'Trans_ST_day_9',
Expand All @@ -410,8 +418,9 @@ def test_3_ignore_scheduled_converted_to_real_trans(self, testdb):
'account_id': 1,
'account_name': 'BankOne',
'amount': Decimal('444.44'),
'budget_id': 1,
'budget_name': 'Periodic1',
'budgets': {
1: {'name': 'Periodic1', 'amount': Decimal('444.44')}
},
'budgeted_amount': Decimal('444.44'),
'date': date(2017, 4, 12),
'description': 'Trans_ST_date',
Expand All @@ -427,8 +436,9 @@ def test_3_ignore_scheduled_converted_to_real_trans(self, testdb):
'account_id': 1,
'account_name': 'BankOne',
'amount': Decimal('333.33'),
'budget_id': 1,
'budget_name': 'Periodic1',
'budgets': {
1: {'name': 'Periodic1', 'amount': Decimal('333.33')}
},
'budgeted_amount': Decimal('333.33'),
'date': date(2017, 4, 14),
'description': 'Trans_ST_pp_3_A',
Expand All @@ -444,8 +454,9 @@ def test_3_ignore_scheduled_converted_to_real_trans(self, testdb):
'account_id': 1,
'account_name': 'BankOne',
'amount': Decimal('333.33'),
'budget_id': 1,
'budget_name': 'Periodic1',
'budgets': {
1: {'name': 'Periodic1', 'amount': Decimal('333.33')}
},
'budgeted_amount': Decimal('333.33'),
'date': date(2017, 4, 15),
'description': 'Trans_ST_pp_3_B',
Expand All @@ -460,9 +471,11 @@ def test_3_ignore_scheduled_converted_to_real_trans(self, testdb):
{
'account_id': 1,
'account_name': 'BankOne',
'amount': Decimal('666.66'),
'budget_id': 1,
'budget_name': 'Periodic1',
'amount': Decimal('766.66'),
'budgets': {
1: {'name': 'Periodic1', 'amount': Decimal('666.66')},
2: {'name': 'Periodic2', 'amount': Decimal('100.00')}
},
'budgeted_amount': None,
'date': date(2017, 4, 16),
'description': 'Trans_bar',
Expand Down Expand Up @@ -547,7 +560,10 @@ def test_3_add_transactions(self, testdb):
# Budget 3 Income Transaction
t1 = Transaction(
date=date(2017, 4, 7),
budget_amounts={budgets[3]: Decimal('100.00')},
budget_amounts={
budgets[3]: Decimal('100.00'),
budgets[4]: Decimal('50.00')
},
budgeted_amount=Decimal('100.00'),
description='B3 Income',
account=acct
Expand Down Expand Up @@ -630,10 +646,10 @@ def test_4_budget_sums(self, testdb):
4: {
'budget_amount': Decimal('500.00'),
'allocated': Decimal('1000.0'),
'spent': Decimal('850.0'),
'trans_total': Decimal('1100.0'),
'spent': Decimal('900.0'),
'trans_total': Decimal('1150.0'),
'is_income': False,
'remaining': Decimal('-600.0')
'remaining': Decimal('-650.0')
},
5: {
'budget_amount': Decimal('100.0'),
Expand All @@ -652,9 +668,9 @@ def test_5_overall_sums(self, testdb):
)
assert pp._data['overall_sums'] == {
'allocated': Decimal('1100.0'),
'spent': Decimal('853.0'),
'spent': Decimal('903.0'),
'income': Decimal('322.45'),
'remaining': Decimal('-530.55')
'remaining': Decimal('-580.55')
}

@patch('%s.settings.PAY_PERIOD_START_DATE' % pbm, date(2017, 4, 7))
Expand Down Expand Up @@ -711,10 +727,10 @@ def test_7_spent_greater_than_allocated(self, testdb):
4: {
'budget_amount': Decimal('500.00'),
'allocated': Decimal('1000.0'),
'spent': Decimal('850.0'),
'trans_total': Decimal('1100.0'),
'spent': Decimal('900.0'),
'trans_total': Decimal('1150.0'),
'is_income': False,
'remaining': Decimal('-600.0')
'remaining': Decimal('-650.0')
},
5: {
'budget_amount': Decimal('100.0'),
Expand All @@ -727,9 +743,9 @@ def test_7_spent_greater_than_allocated(self, testdb):
}
assert pp._data['overall_sums'] == {
'allocated': Decimal('1100.0'),
'spent': Decimal('2885.0'),
'spent': Decimal('2935.0'),
'income': Decimal('322.45'),
'remaining': Decimal('-2562.55')
'remaining': Decimal('-2612.55')
}

@patch('%s.settings.PAY_PERIOD_START_DATE' % pbm, date(2017, 4, 7))
Expand Down
Loading

0 comments on commit 517b41c

Please sign in to comment.