diff --git a/pyluca/action.py b/pyluca/action.py index 10a3f5e..d6772a3 100644 --- a/pyluca/action.py +++ b/pyluca/action.py @@ -46,6 +46,8 @@ def _get_param( return _get_param(context[next_key], event, accountant, context) if key.startswith('balance.'): return Ledger(accountant.journal, accountant.config).get_account_balance(key.replace('balance.', '')) + if key.startswith('opening_balance.'): + return context['opening_balances'][key.replace('opening_balance.', '')] if hasattr(event, key): return event.__getattribute__(key) raise NotImplementedError(f'param {key} not implemented') @@ -104,6 +106,7 @@ def _apply_action( def apply(event: Event, accountant: Accountant, context: dict = None, external_actions: dict = None): context = context if context else {} + context['opening_balances'] = Ledger(accountant.journal, accountant.config).get_balances() event_config = accountant.config['actions_config']['on_event'][event.__class__.__name__] common_actions = accountant.config['actions_config'].get('common_actions', {}) external_actions = external_actions if external_actions else {} diff --git a/pyluca/tests/test_action.py b/pyluca/tests/test_action.py index 1208fa3..dbf7055 100644 --- a/pyluca/tests/test_action.py +++ b/pyluca/tests/test_action.py @@ -1,3 +1,4 @@ +from copy import deepcopy from datetime import datetime from unittest import TestCase from pyluca.accountant import Accountant @@ -26,13 +27,15 @@ 'SAVINGS_BANK': {'type': 'ASSET'}, 'MUTUAL_FUNDS': {'type': 'ASSET'}, 'LOANS': {'type': 'ASSET'}, - 'CAR_EMI': {'type': 'EXPENSE'}, + 'CAR_EMI': {'type': 'LIABILITY'}, + 'CAR_LOAN': {'type': 'LIABILITY'}, 'FREELANCING_INCOME': {'type': 'INCOME'}, 'LOANS_PAYBACK': {'type': 'ASSET'}, 'RISKY_LOANS': {'type': 'ASSET'}, 'MUTUAL_FUNDS_PNL': {'type': 'INCOME'}, 'CHARITY': {'type': 'EXPENSE'}, - 'FIXED_DEPOSIT': {'type': 'ASSET'} + 'FIXED_DEPOSIT': {'type': 'ASSET'}, + 'BORROW': {'type': 'LIABILITY'} }, 'rules': {}, 'actions_config': { @@ -160,6 +163,48 @@ } } ] + }, + 'CarEMIBillEvent': { + 'actions': [ + { + 'dr_account': 'CAR_LOAN', + 'cr_account': 'CAR_EMI', + 'amount': 'amount', + 'narration': 'Car EMI Bill' + } + ] + }, + 'PayCarEMIEvent': { + 'actions': [ + { + 'dr_account': 'CAR_EMI', + 'cr_account': 'SAVINGS_BANK', + 'amount': { + 'type': 'min', + 'a': 'amount', + 'b': 'opening_balance.SAVINGS_BANK' + }, + 'narration': 'Paying Car EMI' + }, + { + 'dr_account': 'CAR_EMI', + 'cr_account': 'BORROW', + 'amount': { + "type": "-", + "a": { + "type": "min", + "a": "opening_balance.CAR_EMI", + "b": "amount" + }, + "b": { + "type": "min", + "a": 'amount', + "b": "opening_balance.SAVINGS_BANK" + } + }, + 'narration': 'Paying Car EMI' + }, + ] } } } @@ -219,6 +264,14 @@ class FreelancingSalaryEvent(AmountEvent): pass +class CarEMIBillEvent(AmountEvent): + pass + + +class PayCarEMIEvent(AmountEvent): + pass + + class TestAction(TestCase): def test_config(self): config = { @@ -329,7 +382,7 @@ def test_sub_narration(self): self.assertEqual(je.narration, 'Put in fixed deposit for Freelancing salary') def test_externals(self): - config = {**personal_fin_config} + config = deepcopy(personal_fin_config) config['actions_config']['on_event']['SalaryEvent']['actions'] = [ *config['actions_config']['on_event']['SalaryEvent']['actions'], { @@ -358,3 +411,34 @@ def __check_balance(acct_name: str, balance: str, date: datetime): apply(e, accountant, external_actions={'check_balance': __check_balance}) self.assertTrue(local_state['checked']) + + def test_opening_balances(self): + accountant = Accountant(Journal(), personal_fin_config, '1') + events = [ + SalaryEvent('1', 2000, datetime(2022, 4, 1), datetime(2022, 4, 1)), + CarEMIBillEvent('2', 3000, datetime(2022, 4, 2), datetime(2022, 4, 2)) + ] + for e in events: + apply(e, accountant) + ledger = Ledger(accountant.journal, accountant.config) + self.assertEqual(ledger.get_account_balance('SALARY'), 2000) + self.assertEqual(ledger.get_account_balance('SAVINGS_BANK'), 2000) + self.assertEqual(ledger.get_account_balance('CAR_EMI'), 3000) + events = [ + PayCarEMIEvent('1', 2700, datetime(2022, 4, 3), datetime(2022, 4, 3)) + ] + for e in events: + apply(e, accountant) + ledger = Ledger(accountant.journal, accountant.config) + self.assertEqual(ledger.get_account_balance('SAVINGS_BANK'), 0) + self.assertEqual(ledger.get_account_balance('CAR_EMI'), 300) + self.assertEqual(ledger.get_account_balance('BORROW'), 700) + events = [ + PayCarEMIEvent('1', 300, datetime(2022, 4, 4), datetime(2022, 4, 4)) + ] + for e in events: + apply(e, accountant) + ledger = Ledger(accountant.journal, accountant.config) + self.assertEqual(ledger.get_account_balance('SAVINGS_BANK'), 0) + self.assertEqual(ledger.get_account_balance('CAR_EMI'), 0) + self.assertEqual(ledger.get_account_balance('BORROW'), 1000) diff --git a/setup.py b/setup.py index b430fb3..cd94405 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setuptools.setup( name='pyluca', - version='1.3.1', + version='1.3.2', author='datasignstech', author_email='tech+opensource@datasignstech.com', description='Double entry accounting system',