def setUp(self): self.date = date(2017, 6, 16) self.user = User.objects.create_user('test_staff_user', '*****@*****.**', '1234') self.user.is_staff = True self.user.save() self.a1 = Account(element='01', number='0101', name='Test Account 1') self.a1.save() self.a2 = Account(element='01', number='0102', name='Test Account 2') self.a2.save()
def setUp(self): self.user = User.objects.create_user('test_staff_user', '*****@*****.**', '1234') self.user.is_staff = True self.user.save() self.a1 = Account(element='01', number='0101', name='Test Account 1') self.a1.save() self.a2 = Account(element='01', number='0102', name='Test Account 2') self.a2.save() t1_value = Decimal(10) lines = (self.a1, self.a2, t1_value) self.t1 = Transaction(date=date(2017, 6, 16), value=0, user=self.user) self.t1.save(lines=lines)
def setUp(self): self.user = User.objects.create_user('test_staff_user', '*****@*****.**', '1234') self.user.is_staff = True self.user.save() self.a1 = Account(element='01', number='0101', name='Test Bank Account 1') self.a1.save() self.a2 = Account(element='15', number='0501', name='Test Expenses Account 1') self.a2.save() self.ba = BankAccount(account=self.a1, bank='CBA') self.ba.save() # banktransacion created, transaction matched t1_value = Decimal(10) lines = (self.a1, self.a2, t1_value) self.t1 = Transaction(date=date(2017, 6, 16), value=0, user=self.user, source="{}".format(BankAccount.__module__)) self.t1.save(lines=lines) self.b1 = BankLine(date=date(2017, 6, 16), value=t1_value, bank_account=self.ba, line_dump='Test Transaction 1', description='Test Transaction 1') self.b1.save() self.e1 = BankEntry.objects.create(transaction=self.t1, bank_line=self.b1) # banktransacion created only self.b2 = BankLine(date=date(2017, 6, 16), value=Decimal(20), bank_account=self.ba, line_dump='Test Transaction 2', description='Test Transaction 2') self.b2.save() self.b3 = BankLine(date=date(2017, 6, 16), value=Decimal(30), bank_account=self.ba, line_dump='Test Transaction 3', description='Test Transaction 3') self.b3.save()
class TestTransactionLineValidationSimpleFailures(TestCase): """ Test a bunch of random object types and formats for outcomes. """ def setUp(self): self.a1 = Account(element='01', number='0100', name='Test Account 1') self.a1.save() self.a2 = Account(element='01', number='0101', name='Test Account 2') self.a2.save() def test_transaction_line_validation_simple_none_fails(self): # Nothing test_input = None self.assertRaises(Exception, Transaction.line_validation, test_input) def test_transaction_line_validation_simple_empty_fails(self): # Pretty much nothing test_input = () self.assertRaises(Exception, Transaction.line_validation, test_input) def test_transaction_line_validation_simple_layout_fails(self): # Correct layout incorrect obj types test_input = ("", "", "") self.assertRaises(Exception, Transaction.line_validation, test_input) def test_transaction_line_validation_simple_layout_type_fails(self): # Correct layout incorrect obj types test_input = (Transaction, "", 0) self.assertRaises(Exception, Transaction.line_validation, test_input) def test_transaction_line_validation_simple_layout_obj_fails(self): # Correct layout incorrect obj types test_input = (Account, "", 0) self.assertRaises(Exception, Transaction.line_validation, test_input) def test_transaction_line_validation_simple_layout_incomplete_fails(self): # Correct layout incorrect obj types test_input = (self.a1, "", 0) self.assertRaises(Exception, Transaction.line_validation, test_input) def test_transaction_line_validation_simple_layout_nan_fails(self): # Correct layout incorrect obj types test_input = (self.a1, self.a2, "") self.assertRaises(Exception, Transaction.line_validation, test_input) def test_transaction_line_validation_simple_dup_fails(self): # Correct layout incorrect obj types test_input = (self.a1, self.a1, 5) self.assertRaises(Exception, Transaction.line_validation, test_input)
def get_tb_account(self): object_settings = self.get_object_settings() if object_settings.get('tb_account'): try: return Account.get_account(object_settings.get('tb_account')) except Exception: raise Exception("No trial balance account for {}".format(self)) else: return False
def setUp(self): self.a1 = Account(element='01', number='0101', name='Test Account 1') self.a1.save() self.a2 = Account(element='01', number='0102', name='Test Account 2') self.a2.save() self.a3 = Account(element='01', number='0103', name='Test Account 3') self.a3.save()
def process_kwargs(self, kwargs): """ Minimum keys assumed included: 'user', 'date', 'lines' 1. Keys added (if not already defined): 'cls', 'source' add `source` using ledgers.utils.get_source(`Model`) based upon `Model` provided in object settings 2. Keys checked: 'relation', dates, decimals, required fields IS_DATE using dateparser.parse IS_DECIMAL using ledgers.utils.make_decimal() IS_RELATION to normalise relation field name 3. Create dicts: (obj_kwargs, trans_kwargs) Check all required fields are represented (or explode) append `row_dict` set to `list_kwargs` """ # Generate list of codes to check against. # Cheaper than checking db for every account. ACCOUNT_CODE_LIST = Account.get_account_code_list() process_kwargs = {k.lower(): v for k, v in kwargs.items()} # If `cls` not manually described in kwargs. process_kwargs['source'] = utils.get_source(self) for key in kwargs: if key in settings.FIELD_IS_DATE: process_kwargs[key] = utils.make_date(kwargs[key]) if key in settings.FIELD_IS_DECIMAL: process_kwargs[key] = utils.make_decimal(kwargs[key]) if key in settings.FIELD_IS_RELATION: # Relation names are not always consistently used. # eg. Creditor, Relation if kwargs[key] is None: # Is likely to have emtpy relation column heading. # Remove empty relation, so doesn't blow up save. process_kwargs.pop(key) else: process_kwargs['relation'] = self.get_relation(kwargs[key]) if key in ACCOUNT_CODE_LIST: process_kwargs.setdefault('accounts', []).append( (key, kwargs[key])) self.check_required(process_kwargs) return process_kwargs
class TestTransactionCheckIsBalanced(TestCase): """ Test a bunch of random object types and formats for outcomes. """ def setUp(self): self.user = User.objects.create_user('test_staff_user', '*****@*****.**', '1234') self.user.is_staff = True self.user.save() self.a1 = Account(element='01', number='0101', name='Test Account 1') self.a1.save() self.a2 = Account(element='01', number='0102', name='Test Account 2') self.a2.save() t1_value = Decimal(10) lines = (self.a1, self.a2, t1_value) self.t1 = Transaction(date=date(2017, 6, 16), value=0, user=self.user) self.t1.save(lines=lines) def test_transaction_check_is_balanced_fails(self): broken_line = self.t1.lines.first() broken_line.value = 10000 broken_line.save() self.assertEqual(self.t1.is_balanced, False)
class TestTransactionLineValidationMultilineFailures(TestCase): """ Test a bunch of random object types and formats for outcomes. """ def setUp(self): self.a1 = Account(element='01', number='0101', name='Test Account 1') self.a1.save() self.a2 = Account(element='01', number='0102', name='Test Account 2') self.a2.save() self.a3 = Account(element='01', number='0103', name='Test Account 3') self.a3.save() def test_transaction_line_validation_multi_zero_list_fails(self): # Nothing test_input = [()] self.assertRaises(Exception, Transaction.line_validation, test_input) def test_transaction_line_validation_multi_zero_tuple_fails(self): # Nothing test_input = ((), ) self.assertRaises(Exception, Transaction.line_validation, test_input) def test_transaction_line_validation_multi_single_fails(self): # Nothing test_input = [("01-0101", 5)] self.assertRaises(Exception, Transaction.line_validation, test_input) def test_transaction_line_validation_multi_no_bal2_fails(self): test_input = [("01-0101", 5), ("01-0102", 5)] self.assertRaises(Exception, Transaction.line_validation, test_input) # self.assertEqual(Transaction.line_validation(test_input), None) def test_transaction_line_validation_multi_no_bal3_fails(self): test_input = [("01-0101", 5), ("01-0102", 5), ("01-0102", -5)] self.assertRaises(Exception, Transaction.line_validation, test_input) # self.assertEqual(Transaction.line_validation(test_input), None) def test_transaction_line_validation_multi_account_fails(self): test_input = [("01-0000", 5), ("01-0102", 5)] self.assertRaises(Exception, Transaction.line_validation, test_input)
def test_get_source_Obj_instance_passes(self): test_input = Account() test_result = "ledgers.models.Account" self.assertEqual(utils.get_source(test_input), test_result)
class TestBankLineQueryset(TestCase): def setUp(self): self.user = User.objects.create_user('test_staff_user', '*****@*****.**', '1234') self.user.is_staff = True self.user.save() self.a1 = Account(element='01', number='0101', name='Test Bank Account 1') self.a1.save() self.a2 = Account(element='15', number='0501', name='Test Expenses Account 1') self.a2.save() self.ba = BankAccount(account=self.a1, bank='CBA') self.ba.save() # banktransacion created, transaction matched t1_value = Decimal(10) lines = (self.a1, self.a2, t1_value) self.t1 = Transaction(date=date(2017, 6, 16), value=0, user=self.user, source="{}".format(BankAccount.__module__)) self.t1.save(lines=lines) self.b1 = BankLine(date=date(2017, 6, 16), value=t1_value, bank_account=self.ba, line_dump='Test Transaction 1', description='Test Transaction 1') self.b1.save() self.e1 = BankEntry.objects.create(transaction=self.t1, bank_line=self.b1) # banktransacion created only self.b2 = BankLine(date=date(2017, 6, 16), value=Decimal(20), bank_account=self.ba, line_dump='Test Transaction 2', description='Test Transaction 2') self.b2.save() self.b3 = BankLine(date=date(2017, 6, 16), value=Decimal(30), bank_account=self.ba, line_dump='Test Transaction 3', description='Test Transaction 3') self.b3.save() def test_banktransaction_queryset_reconcilied_passes(self): reconciled_obj_list = BankLine.objects.reconciled() self.assertEqual(reconciled_obj_list.count(), 1) self.assertIn(self.b1, reconciled_obj_list) self.assertNotIn(self.b2, reconciled_obj_list) self.assertNotIn(self.b3, reconciled_obj_list) def test_banktransaction_queryset_unreconcilied_passes(self): unreconciled_obj_list = BankLine.objects.unreconciled() self.assertEqual(unreconciled_obj_list.count(), 2) self.assertNotIn(self.b1, unreconciled_obj_list) self.assertIn(self.b2, unreconciled_obj_list) self.assertIn(self.b3, unreconciled_obj_list)
def process_tax(cls, kwargs, lines): """ Just a check that GST is defined if required. If subledger requires GST error will be raised if GST not defined. GST should be defined in one of two ways: - GST tb account being used - `GST total` column/ `gst_total` key in kwargs """ if not settings.OBJECT_SETTINGS.get(utils.get_source_name(cls)): # Tax not applicable to this subledgers return lines object_settings = settings.OBJECT_SETTINGS[utils.get_source_name(cls)] # @@ TODO facilitate other taxes and surcharges. if object_settings.get('is_GST'): # There are 2 methods of adding GST: # 1. defining GST_CR_ACCOUNT or GST_DR_ACCOUNT among lines # or # 2. defining `GST_total` column on import # First check if GST_CR_ACCOUNT or GST_DR_ACCOUNT lines exist gst_allocated = False for line in lines: if Account.get_account(line[0]) == \ Account.get_account(settings.GST_DR_ACCOUNT)\ or Account.get_account(line[0]) == \ Account.get_account(settings.GST_CR_ACCOUNT): gst_allocated = True # If not: # - set line value using `GST_total` column value # - set acccount using `is_tb_account_DR/CR` to get `GST_DR/CR_ACCOUNT` # Note: the value +/- is correct to subledger. May not be correct for # the tb. +/- corrected to tb set in different process. if not gst_allocated: # note: correct GST account, abs value # fix dr/cr +/- in next process, not here. if not kwargs['gst_total'] in ['', None, 0]: if object_settings.get('is_tb_account_DR'): lines.append((settings.GST_DR_ACCOUNT, utils.make_decimal(kwargs['gst_total']))) if object_settings.get('is_tb_account_CR'): lines.append((settings.GST_CR_ACCOUNT, utils.make_decimal(kwargs['gst_total']))) if kwargs.get('gst_total'): gst_allocated = True else: pass # @@ TODO Can add GSTEntity or GSTAccount here. # raise Exception('GST not represted.') return lines
class TestTransactionSave(TestCase): def setUp(self): self.date = date(2017, 6, 16) self.user = User.objects.create_user('test_staff_user', '*****@*****.**', '1234') self.user.is_staff = True self.user.save() self.a1 = Account(element='01', number='0101', name='Test Account 1') self.a1.save() self.a2 = Account(element='01', number='0102', name='Test Account 2') self.a2.save() # t1_value = Decimal(10) # lines = (self.a1, self.a2, t1_value) # self.t1 = Transaction(date=self.date, value=0, user=self.user) # self.t1.save(lines=lines) def test_transaction_new_object_save_no_lines_fails(self): new_transaction = Transaction(date=self.date, value=0, user=self.user) self.assertRaises(Exception, new_transaction.save) def test_transaction_new_object_simple_all_fields_correct(self): """ Testing custom save() behaves as expected.""" test_input = ("01-0101", "01-0102", 5) new_obj = Transaction(user=self.user, date=self.date, value=0) new_obj.save(test_input) self.assertEqual(new_obj.date, self.date) self.assertEqual(new_obj.value, Decimal(5)) self.assertEqual(new_obj.note, "") self.assertEqual(new_obj.source, "") self.assertEqual(new_obj.user, self.user) self.assertEqual(new_obj.is_balanced, True) self.assertEqual(new_obj.lines.count(), 2) self.assertEqual(new_obj.lines.first().account, self.a1) self.assertEqual(new_obj.lines.first().value, 5) self.assertEqual(new_obj.lines.first().note, "") self.assertEqual(new_obj.lines.last().account, self.a2) self.assertEqual(new_obj.lines.last().value, -5) self.assertEqual(new_obj.lines.last().note, "") def test_transaction_new_object_multi2_all_fields_correct(self): """ Testing custom save() behaves as expected.""" test_input = [("01-0101", 5), ("01-0102", -5)] new_obj = Transaction(user=self.user, date=self.date, value=0) new_obj.save(test_input) self.assertEqual(new_obj.date, self.date) self.assertEqual(new_obj.value, Decimal(5)) self.assertEqual(new_obj.note, "") self.assertEqual(new_obj.source, "") self.assertEqual(new_obj.user, self.user) self.assertEqual(new_obj.is_balanced, True) self.assertEqual(new_obj.lines.count(), 2) self.assertEqual(new_obj.lines.first().account, self.a1) self.assertEqual(new_obj.lines.first().value, 5) self.assertEqual(new_obj.lines.first().note, "") self.assertEqual(new_obj.lines.last().account, self.a2) self.assertEqual(new_obj.lines.last().value, -5) self.assertEqual(new_obj.lines.last().note, "") def test_transaction_new_object_multi3_all_fields_correct(self): """ Testing custom save() behaves as expected.""" test_input = [ ("01-0101", -5), ("01-0102", -5), ("01-0101", 10), ] new_obj = Transaction(user=self.user, date=self.date, value=0) new_obj.save(test_input) self.assertEqual(new_obj.date, self.date) self.assertEqual(new_obj.value, Decimal(10)) self.assertEqual(new_obj.note, "") self.assertEqual(new_obj.source, "") self.assertEqual(new_obj.user, self.user) self.assertEqual(new_obj.is_balanced, True) self.assertEqual(new_obj.lines.count(), 3) self.assertEqual(new_obj.lines.first().account, self.a1) self.assertEqual(new_obj.lines.first().value, -5) self.assertEqual(new_obj.lines.first().note, "") self.assertEqual(new_obj.lines.last().account, self.a1) self.assertEqual(new_obj.lines.last().value, 10) self.assertEqual(new_obj.lines.last().note, "")
class TestTransactionLineValidationSimplePasses(TestCase): """ All correct permuations: (s, s, x) -- test `Account` str/obj input perms (s, o, x) (o, s, x) (o, o, x) (x, x, s) -- test `value` str/obj/Decimal input perms (x, x, d) (x, x, i) """ def setUp(self): self.a1 = Account(element='01', number='0100', name='Test Account 1') self.a1.save() self.a2 = Account(element='01', number='0101', name='Test Account 2') self.a2.save() # check `Account`variations def test_transaction_line_validation_simple_ssx_input_passes(self): test_input = ("01-0100", "01-0101", 5) test_result = (5, [{ 'account': self.a1, 'value': 5 }, { 'account': self.a2, 'value': -5 }]) self.assertEqual(Transaction.line_validation(test_input), test_result) def test_transaction_line_validation_simple_sox_input_passes(self): test_input = (self.a1, "01-0101", 5) test_result = (5, [{ 'account': self.a1, 'value': 5 }, { 'account': self.a2, 'value': -5 }]) self.assertEqual(Transaction.line_validation(test_input), test_result) def test_transaction_line_validation_simple_osx_input_passes(self): test_input = ("01-0100", self.a2, 5) test_result = (5, [{ 'account': self.a1, 'value': 5 }, { 'account': self.a2, 'value': -5 }]) self.assertEqual(Transaction.line_validation(test_input), test_result) def test_transaction_line_validation_simple_oox_input_passes(self): test_input = (self.a1, self.a2, Decimal('5')) test_result = (5, [{ 'account': self.a1, 'value': 5 }, { 'account': self.a2, 'value': -5 }]) self.assertEqual(Transaction.line_validation(test_input), test_result) # Check `value` variations def test_transaction_line_validation_simple_xxs_input_passes(self): test_input = ("01-0100", "01-0101", '5') test_result = (5, [{ 'account': self.a1, 'value': 5 }, { 'account': self.a2, 'value': -5 }]) self.assertEqual(Transaction.line_validation(test_input), test_result) def test_transaction_line_validation_simple_xxd_input_passes(self): test_input = ("01-0100", "01-0101", Decimal(5)) test_result = (5, [{ 'account': self.a1, 'value': 5 }, { 'account': self.a2, 'value': -5 }]) self.assertEqual(Transaction.line_validation(test_input), test_result) def test_transaction_line_validation_simple_xxi_input_passes(self): test_input = ("01-0100", "01-0101", int(5)) test_result = (5, [{ 'account': self.a1, 'value': 5 }, { 'account': self.a2, 'value': -5 }]) self.assertEqual(Transaction.line_validation(test_input), test_result)
class TestTransactionLineValidationMultilinePasses(TestCase): """ Test successful cases.""" def setUp(self): self.a1 = Account(element='01', number='0101', name='Test Account 1') self.a1.save() self.a2 = Account(element='01', number='0102', name='Test Account 2') self.a2.save() self.a3 = Account(element='01', number='0103', name='Test Account 3') self.a3.save() def test_transaction_line_validation_multi_simple_passes(self): test_input = [("01-0101", 5), ("01-0102", -5)] test_result = [ Decimal(5), [{ 'account': self.a1, 'value': Decimal(5) }, { 'account': self.a2, 'value': Decimal(-5) }] ] self.assertEqual(Transaction.line_validation(test_input), test_result) def test_transaction_line_validation_multi_note1_passes(self): test_input = [("01-0101", 5, "A notation"), ("01-0102", -5)] test_result = [ Decimal(5), [{ 'account': self.a1, 'value': Decimal(5), 'note': 'A notation' }, { 'account': self.a2, 'value': Decimal(-5) }] ] self.assertEqual(Transaction.line_validation(test_input), test_result) def test_transaction_line_validation_multi_note2_passes(self): test_input = [("01-0101", 5), ("01-0102", -5, "A notation")] test_result = [ Decimal(5), [ { 'account': self.a1, 'value': Decimal(5) }, { 'account': self.a2, 'value': Decimal(-5), 'note': 'A notation' }, ] ] self.assertEqual(Transaction.line_validation(test_input), test_result) def test_transaction_line_validation_multi_note_both_passes(self): test_input = [("01-0101", 5, "A notation"), ("01-0102", -5, "Another notation")] test_result = [ Decimal(5), [ { 'account': self.a1, 'value': Decimal(5), 'note': 'A notation' }, { 'account': self.a2, 'value': Decimal(-5), 'note': 'Another notation' }, ] ] self.assertEqual(Transaction.line_validation(test_input), test_result) def test_transaction_line_validation_multi_same_passes(self): # have repeated same account is allowed in multi situation test_input = [("01-0101", -5), ("01-0101", -5), ("01-0101", 10)] test_result = [ Decimal(10), [{ 'account': self.a1, 'value': Decimal(-5) }, { 'account': self.a1, 'value': Decimal(-5) }, { 'account': self.a1, 'value': Decimal(10) }] ] self.assertEqual(Transaction.line_validation(test_input), test_result) def test_transaction_line_validation_multi_list_passes(self): test_input = [("01-0101", 5), ("01-0102", 5), ("01-0103", -10)] test_result = [ Decimal(10), [ { 'account': self.a1, 'value': Decimal(5) }, { 'account': self.a2, 'value': Decimal(5) }, { 'account': self.a3, 'value': Decimal(-10) }, ] ] self.assertEqual(Transaction.line_validation(test_input), test_result) def test_transaction_line_validation_multi_tuple_passes(self): test_input = (("01-0101", 5), ("01-0102", 5), ("01-0103", -10)) test_result = [ Decimal(10), [ { 'account': self.a1, 'value': Decimal(5) }, { 'account': self.a2, 'value': Decimal(5) }, { 'account': self.a3, 'value': Decimal(-10) }, ] ] self.assertEqual(Transaction.line_validation(test_input), test_result) def test_transaction_line_validation_multi_none_passes(self): test_input = [("01-0101", 5), ("01-0102", 5), ("01-0103", -10), ("01-0101", -5), ("01-0102", -5), ("01-0103", 10)] test_result = [ Decimal(20), [ { 'account': self.a1, 'value': Decimal(5) }, { 'account': self.a2, 'value': Decimal(5) }, { 'account': self.a3, 'value': Decimal(-10) }, { 'account': self.a1, 'value': Decimal(-5) }, { 'account': self.a2, 'value': Decimal(-5) }, { 'account': self.a3, 'value': Decimal(10) }, ] ] self.assertEqual(Transaction.line_validation(test_input), test_result)
def process_account(self, account, val): """ If only account is provided, retrieve tb account. """ return [(Account.get_account(account), utils.set_DR(utils.make_decimal(val)))]
def process_line(self, account_DR, account_CR, val): return [(Account.get_account(account_DR), utils.set_DR(utils.make_decimal(val))), (Account.get_account(account_CR), utils.set_CR(utils.make_decimal(val)))]
def test_unsaved(self): a = Account() with self.assertRaises(ValueError): a.set_balance() self.assertEqual(a.get_balance(), Decimal('0.00'))