def test_transfer_neg_to_neg(self): src = self.account(type=Account.TYPES.asset) dst = self.account(type=Account.TYPES.asset) src.transfer_to(dst, Money(100, "EUR")) self.assertEqual(src.balance(), Balance(-100, "EUR")) self.assertEqual(dst.balance(), Balance(100, "EUR")) Account.validate_accounting_equation()
def test_balance_simple_as_of(self): account1 = self.account() account2 = self.account() with db_transaction.atomic(): transaction = Transaction.objects.create(date="2016-06-01") Leg.objects.create(transaction=transaction, account=account1, amount=Money(100, "EUR")) Leg.objects.create(transaction=transaction, account=account2, amount=Money(-100, "EUR")) transaction = Transaction.objects.create(date="2016-06-15") Leg.objects.create(transaction=transaction, account=account1, amount=Money(50, "EUR")) Leg.objects.create(transaction=transaction, account=account2, amount=Money(-50, "EUR")) self.assertEqual(account1.simple_balance(as_of="2016-01-01"), Balance(0, "EUR")) # before any transactions self.assertEqual(account1.simple_balance(as_of="2016-06-01"), Balance(100, "EUR")) # same date as first transaction self.assertEqual(account1.simple_balance(as_of="2016-06-10"), Balance(100, "EUR")) # between two transactions self.assertEqual(account1.simple_balance(as_of="2016-06-15"), Balance(150, "EUR")) # same date as second transaction self.assertEqual(account1.simple_balance(as_of="2020-01-01"), Balance(150, "EUR")) # after transactions
def test_transfer_to(self): account1 = self.account(type=Account.TYPES.income) account2 = self.account(type=Account.TYPES.income) transaction = account1.transfer_to(account2, Money(500, "EUR")) self.assertEqual(transaction.legs.count(), 2) self.assertEqual(account1.balance(), Balance(-500, "EUR")) self.assertEqual(account2.balance(), Balance(500, "EUR"))
def test_valid_data(self): form = SimpleTransactionForm( dict( from_account=self.from_account.uuid, to_account=self.to_account.uuid, description="A test simple transaction", amount_0="50.00", amount_1="EUR", date="2000-06-15", ) ) self.assertTrue(form.is_valid()) form.save() # Transaction exists with two legs transaction = Transaction.objects.get() self.assertEqual(transaction.description, "A test simple transaction") self.assertEqual(transaction.legs.count(), 2) # Account balances changed self.assertEqual(self.from_account.balance(), Balance(-50, "EUR")) self.assertEqual(self.to_account.balance(), Balance(50, "EUR")) # Check transaction legs have amounts set as expected from_leg = transaction.legs.get(account=self.from_account) to_leg = transaction.legs.get(account=self.to_account) self.assertEqual(from_leg.amount, Money(-50, "EUR")) self.assertEqual(to_leg.amount, Money(50, "EUR"))
def test_transfer_pos_to_pos(self): src = self.account(type=Account.TYPES.income) dst = self.account(type=Account.TYPES.income) src.transfer_to(dst, Money(100, 'EUR')) self.assertEqual(src.balance(), Balance(-100, 'EUR')) self.assertEqual(dst.balance(), Balance(100, 'EUR')) Account.validate_accounting_equation()
def test_transfer_expense_to_liability(self): # This should perform the reverse action to that in the above test_transfer_liability_to_expense() src = self.account(type=Account.TYPES.expense) dst = self.account(type=Account.TYPES.liability) src.transfer_to(dst, Money(100, "EUR")) self.assertEqual(src.balance(), Balance(100, "EUR")) self.assertEqual(dst.balance(), Balance(100, "EUR")) Account.validate_accounting_equation()
def test_currency_exchange(self): src = self.account(type=Account.TYPES.asset, currencies=['GBP']) trading = self.account(type=Account.TYPES.trading, currencies=['GBP', 'EUR']) dst = self.account(type=Account.TYPES.asset, currencies=['EUR']) src.transfer_to(trading, Money('100', 'GBP')) trading.transfer_to(dst, Money('110', 'EUR')) self.assertEqual(src.balance(), Balance('-100', 'GBP')) self.assertEqual(trading.balance(), Balance('-100', 'GBP', '110', 'EUR')) self.assertEqual(dst.balance(), Balance('110', 'EUR'))
def test_currency_exchange(self): src = self.account(type=Account.TYPES.asset, currencies=["GBP"]) trading = self.account(type=Account.TYPES.trading, currencies=["GBP", "EUR"]) dst = self.account(type=Account.TYPES.asset, currencies=["EUR"]) src.transfer_to(trading, Money("100", "GBP")) trading.transfer_to(dst, Money("110", "EUR")) self.assertEqual(src.balance(), Balance("-100", "GBP")) self.assertEqual(trading.balance(), Balance("-100", "GBP", "110", "EUR")) self.assertEqual(dst.balance(), Balance("110", "EUR"))
def test_transfer_liability_to_expense(self): # When doing this it is probably safe to assume we want to the # liability account to contribute to an expense, therefore both should decrease src = self.account(type=Account.TYPES.liability) dst = self.account(type=Account.TYPES.expense) src.transfer_to(dst, Money(100, "EUR")) self.assertEqual(src.balance(), Balance(-100, "EUR")) self.assertEqual(dst.balance(), Balance(-100, "EUR")) Account.validate_accounting_equation()
def test_neq(self): self.assertEqual(Balance() != Balance(), False) self.assertEqual(Balance([Money(0, 'USD')]) != Balance(), False) self.assertEqual(self.balance_1 != +self.balance_1, False) self.assertEqual(self.balance_1 != self.balance_2, True) self.assertEqual(Balance([Money(100, 'USD')]) != Balance([Money(100, 'USD')]), False) self.assertEqual(Balance([Money(100, 'USD'), Money(0, 'EUR')]) != Balance([Money(100, 'USD')]), False) self.assertEqual(Balance([Money(100, 'USD'), Money(10, 'EUR')]) != Balance([Money(100, 'USD')]), True)
def test_balance_simple(self): account1 = self.account() account2 = self.account() with db_transaction.atomic(): transaction = Transaction.objects.create() Leg.objects.create(transaction=transaction, account=account1, amount=Money(100, "EUR")) Leg.objects.create(transaction=transaction, account=account2, amount=Money(-100, "EUR")) self.assertEqual(account1.simple_balance(), Balance(100, "EUR")) self.assertEqual(account2.simple_balance(), Balance(-100, "EUR"))
def test_account_balance_after(self): src = self.account() dst = self.account() src.transfer_to(dst, Money(100, "EUR")) src.transfer_to(dst, Money(100, "EUR")) src.transfer_to(dst, Money(50, "EUR")) dst.transfer_to(src, Money(70, "EUR")) legs = Leg.objects.filter(account=dst).order_by("pk").all() self.assertEqual(legs[0].account_balance_after(), Balance("100", "EUR")) self.assertEqual(legs[1].account_balance_after(), Balance("200", "EUR")) self.assertEqual(legs[2].account_balance_after(), Balance("250", "EUR")) self.assertEqual(legs[3].account_balance_after(), Balance("180", "EUR"))
def test_manager(self): account1 = self.account(currencies=['USD']) account2 = self.account(currencies=['USD']) with db_transaction.atomic(): transaction = Transaction.objects.create() Leg.objects.create(transaction=transaction, account=account1, amount=Money(100.12, 'USD')) Leg.objects.create(transaction=transaction, account=account2, amount=Money(-80.06, 'USD')) Leg.objects.create(transaction=transaction, account=account2, amount=Money(-20.06, 'USD')) self.assertEqual(Leg.objects.sum_to_balance(), Balance()) self.assertEqual(account1.legs.sum_to_balance(), Balance([Money('100.12', 'USD')])) self.assertEqual(account2.legs.sum_to_balance(), Balance([Money('-100.12', 'USD')]))
def test_account_balance_after(self): src = self.account() dst = self.account() src.transfer_to(dst, Money(100, 'EUR')) src.transfer_to(dst, Money(100, 'EUR')) src.transfer_to(dst, Money(50, 'EUR')) dst.transfer_to(src, Money(70, 'EUR')) legs = Leg.objects.filter(account=dst).order_by('pk').all() self.assertEqual(legs[0].account_balance_after(), Balance('100', 'EUR')) self.assertEqual(legs[1].account_balance_after(), Balance('200', 'EUR')) self.assertEqual(legs[2].account_balance_after(), Balance('250', 'EUR')) self.assertEqual(legs[3].account_balance_after(), Balance('180', 'EUR'))
def test_utility_bill(self): # Month 1 self.gas_expense.transfer_to(self.gas_payable, Money(100, 'EUR')) self.assertEqual(self.cash.balance(), 0) self.assertEqual(self.gas_expense.balance(), Balance(100, 'EUR')) self.assertEqual(self.gas_payable.balance(), Balance(100, 'EUR')) # Month 2 self.gas_expense.transfer_to(self.gas_payable, Money(100, 'EUR')) self.assertEqual(self.cash.balance(), 0) self.assertEqual(self.gas_expense.balance(), Balance(200, 'EUR')) self.assertEqual(self.gas_payable.balance(), Balance(200, 'EUR')) # Month 3 self.gas_expense.transfer_to(self.gas_payable, Money(100, 'EUR')) self.assertEqual(self.cash.balance(), 0) self.assertEqual(self.gas_expense.balance(), Balance(300, 'EUR')) self.assertEqual(self.gas_payable.balance(), Balance(300, 'EUR')) # We receive the actual bill (we are moving a negative amount of money, # as this is an outgoing) self.cash.transfer_to(self.gas_payable, Money(-300, 'EUR')) # We are now 300 overdrawn, but the payable account has been cleared self.assertEqual(self.cash.balance(), Balance(-300, 'EUR')) self.assertEqual(self.gas_expense.balance(), Balance(300, 'EUR')) self.assertEqual(self.gas_payable.balance(), 0) Account.validate_accounting_equation()
def test_gte(self): self.assertEqual(Balance() >= Balance(), True) self.assertEqual(self.balance_1 >= self.balance_1, True) self.assertEqual(Balance() >= Balance([Money(1, "USD")]), False) self.assertEqual(Balance([Money(1, "USD")]) >= Balance(), True) self.assertEqual( Balance([Money(1, "USD")]) >= Balance([Money(1, "EUR")]), False)
def test_gt(self): self.assertEqual(Balance() > Balance(), False) self.assertEqual(self.balance_1 > self.balance_1, False) self.assertEqual(Balance() > Balance([Money(1, 'USD')]), False) self.assertEqual(Balance([Money(1, 'USD')]) > Balance(), True) self.assertEqual( Balance([Money(1, 'USD')]) > Balance([Money(1, 'EUR')]), False)
def test_transfer_from_bank_to_income(self): """If we move money out of the bank and into an income account, we expect both values to go up""" form = SimpleTransactionForm(dict( from_account=self.bank.uuid, to_account=self.income.uuid, description='A test simple transaction', amount_0='50.00', amount_1='EUR', )) self.assertTrue(form.is_valid()) form.save() self.assertEqual(self.bank.balance(), Balance(50, 'EUR')) self.assertEqual(self.income.balance(), Balance(50, 'EUR'))
def test_lte(self): self.assertEqual(Balance() <= Balance(), True) self.assertEqual(self.balance_1 <= self.balance_1, True) self.assertEqual(Balance() <= Balance([Money(1, 'USD')]), True) self.assertEqual(Balance([Money(1, 'USD')]) <= Balance(), False) self.assertEqual( Balance([Money(1, 'USD')]) <= Balance([Money(1, 'EUR')]), True)
def test_submit(self): # more checks done in form unit tests response = self.client.post(self.view_url, data=dict( from_account=self.bank_account.uuid, to_account=self.income_account.uuid, amount_0='123.45', amount_1='EUR', )) self.assertEqual(response.status_code, 302) self.assertEqual(response['Location'], '/') self.assertEqual(self.bank_account.balance(), Balance('123.45', 'EUR')) self.assertEqual(self.income_account.balance(), Balance('123.45', 'EUR'))
def test_eq(self): self.assertEqual(Balance() == Balance(), True) self.assertEqual(Balance([Money(0, 'USD')]) == Balance(), True) self.assertEqual(self.balance_1 == +self.balance_1, True) self.assertEqual(self.balance_1 == self.balance_2, False) self.assertEqual( Balance([Money(100, 'USD')]) == Balance([Money(100, 'USD')]), True) self.assertEqual( Balance([Money(100, 'USD'), Money(0, 'EUR')]) == Balance([Money(100, 'USD')]), True) self.assertEqual( Balance([Money(100, 'USD'), Money(10, 'EUR')]) == Balance([Money(100, 'USD')]), False)
def test_create_transaction_money_out(self): """Call StatementLine.create_transaction() for an expense""" line = StatementLine.objects.create( date="2016-01-01", statement_import=self.statement_import, amount=-100 ) line.refresh_from_db() transaction = line.create_transaction(self.expenses) self.assertEqual(transaction.legs.count(), 2) self.assertEqual(transaction.date, date(2016, 1, 1)) self.assertEqual(self.bank.balance(), Balance(-100, "EUR")) self.assertEqual(self.expenses.balance(), Balance(100, "EUR")) line.refresh_from_db() self.assertEqual(line.transaction, transaction) Account.validate_accounting_equation()
def test_valid(self): form = CurrencyTradeForm(dict( source_account=self.account_gbp.uuid, source_amount_0='100', source_amount_1='GBP', trading_account=self.trading_gbp_eur.uuid, destination_account=self.account_eur.uuid, destination_amount_0='110', destination_amount_1='EUR', )) self.assertTrue(form.is_valid(), form.errors) form.save() self.assertEqual(self.account_gbp.balance(), Balance('-100', 'GBP')) self.assertEqual(self.trading_gbp_eur.balance(), Balance('-100', 'GBP', '110', 'EUR')) self.assertEqual(self.account_eur.balance(), Balance('110', 'EUR'))
def test_account_balance_after_out_of_order_ids(self): src = self.account() dst = self.account() # Take test_account_balance_after() as a reference test, # here we reverse the order of creation (to make the IDs go # backwards), and set the dates to force the order we want dst.transfer_to(src, Money(70, "EUR"), date="2000-01-15") src.transfer_to(dst, Money(50, "EUR"), date="2000-01-10") src.transfer_to(dst, Money(100, "EUR"), date="2000-01-05") src.transfer_to(dst, Money(100, "EUR"), date="2000-01-01") legs = Leg.objects.filter(account=dst).order_by("transaction__date").all() self.assertEqual(legs[0].account_balance_after(), Balance("100", "EUR")) self.assertEqual(legs[1].account_balance_after(), Balance("200", "EUR")) self.assertEqual(legs[2].account_balance_after(), Balance("250", "EUR")) self.assertEqual(legs[3].account_balance_after(), Balance("180", "EUR"))
def sum_to_balance(self): """Sum the Legs of the QuerySet to get a `Balance`_ object """ result = self.values("amount_currency").annotate( total=models.Sum("amount")) return Balance( [Money(r["total"], r["amount_currency"]) for r in result])
def validate_accounting_equation(cls): """Check that all accounts sum to 0""" balances = [account.balance(raw=True) for account in Account.objects.root_nodes()] if sum(balances, Balance()) != 0: raise exceptions.AccountingEquationViolationError( 'Account balances do not sum to zero. They sum to {}'.format(sum(balances)) )
def sum_to_balance(self): """Sum the Legs of the QuerySet to get a `Balance`_ object """ result = self.values('amount_currency').annotate( total=models.Sum('amount')) return Balance( [Money(r['total'], r['amount_currency']) for r in result])
def test_balance(self): account1 = self.account(type=Account.TYPES.income) account1_child = self.account(type=Account.TYPES.income, parent=account1) account2 = self.account(type=Account.TYPES.income) with db_transaction.atomic(): transaction = Transaction.objects.create() Leg.objects.create( transaction=transaction, account=account1, amount=50, amount_currency="EUR", ) Leg.objects.create( transaction=transaction, account=account1_child, amount=50, amount_currency="EUR", ) Leg.objects.create( transaction=transaction, account=account2, amount=-100, amount_currency="EUR", ) self.assertEqual(account1.balance(), Balance(100, "EUR"))
def test_balance_kwargs(self): account1 = self.account() account2 = self.account() with db_transaction.atomic(): transaction = Transaction.objects.create(date="2016-06-01") Leg.objects.create( transaction=transaction, account=account1, amount=100, amount_currency="EUR", ) Leg.objects.create( transaction=transaction, account=account2, amount=-100, amount_currency="EUR", ) transaction = Transaction.objects.create(date="2016-06-15") Leg.objects.create( transaction=transaction, account=account1, amount=50, amount_currency="EUR", ) Leg.objects.create( transaction=transaction, account=account2, amount=-50, amount_currency="EUR", ) self.assertEqual(account1.balance(transaction__date__gte="2016-06-15"), Balance(50, "EUR"))
def test_create_transaction_money_in(self): """Call StatementLine.create_transaction() for a sale""" line = StatementLine.objects.create( date='2016-01-01', statement_import=self.statement_import, amount=100, ) line.refresh_from_db() transaction = line.create_transaction(self.sales) self.assertEqual(transaction.legs.count(), 2) self.assertEqual(transaction.date, date(2016, 1, 1)) self.assertEqual(self.bank.balance(), Balance(100, 'EUR')) self.assertEqual(self.sales.balance(), Balance(100, 'EUR')) line.refresh_from_db() self.assertEqual(line.transaction, transaction) Account.validate_accounting_equation()