Exemple #1
0
 def test_add(self):
     t1 = Total(100, 'USD', 100, 'EUR')
     t2 = Total(80, 'USD', 150, 'GBP')
     t = t1 + t2
     assert t['USD'].amount == 180
     assert t['EUR'].amount == 100
     assert t['GBP'].amount == 150
Exemple #2
0
 def test_sub(self):
     t1 = Total(100, 'USD', 100, 'EUR')
     t2 = Total(80, 'USD', 150, 'GBP')
     t = t1 - t2
     assert t['USD'].amount == 20
     assert t['EUR'].amount == 100
     assert t['GBP'].amount == -150
    def test_it_pays_invoices_in_different_currencies(self):
        invoice2 = Invoice.objects.create(account_id=self.account.id, due_date=date.today())
        Charge.objects.create(account=self.account, invoice=invoice2, amount=Money(5, 'EUR'),
                              product_code='BCHARGE')
        Transaction.objects.create(account=self.account, amount=Money(5, 'EUR'), success=True)
        Transaction.objects.create(account=self.account, amount=Money(10, 'CHF'), success=True)
        paid_invoice_ids = accounts.assign_funds_to_account_pending_invoices(account_id=self.account.id)
        assert paid_invoice_ids == [self.invoice1.pk, invoice2.pk]

        self.invoice1.refresh_from_db()
        assert_attrs(self.invoice1,
                     {'status': Invoice.PAID,
                      'items': [
                          {'amount': Money(40, 'CHF'), 'product_code': 'ACHARGE'},
                      ],
                      'transactions': [
                          {'amount': Money(30, 'CHF')},
                          {'amount': Money(10, 'CHF')},
                      ]})
        assert self.invoice1.due() == Total(Money(0, 'CHF'))

        invoice2.refresh_from_db()
        assert_attrs(invoice2,
                     {'status': Invoice.PAID,
                      'items': [
                          {'amount': Money(5, 'EUR'), 'product_code': 'BCHARGE'},
                      ],
                      'transactions': [
                          {'amount': Money(5, 'EUR')},
                      ]})
        assert invoice2.due() == Total(Money(0, 'EUR'))
Exemple #4
0
 def test_sub_rev(self):
     t1 = Total(100, 'USD', 100, 'EUR')
     t2 = Total(80, 'USD', 150, 'GBP')
     t = t2 - t1
     assert t['USD'].amount == -20
     assert t['EUR'].amount == -100
     assert t['GBP'].amount == 150
Exemple #5
0
 def test_total_charges_should_select_just_the_right_charges(self):
     invoice = Invoice.objects.create(account=self.account,
                                      due_date=date.today())
     Charge.objects.create(account=self.account,
                           invoice=invoice,
                           amount=Money(8, 'CHF'),
                           product_code='ACHARGE')
     Charge.objects.create(account=self.account,
                           invoice=invoice,
                           amount=Money(2, 'CHF'),
                           product_code='BCHARGE')
     Charge.objects.create(account=self.account,
                           invoice=invoice,
                           amount=Money(-1, 'CHF'),
                           product_code='ACREDIT')
     Charge.objects.create(account=self.account,
                           invoice=invoice,
                           amount=Money(6, 'CHF'),
                           product_code=CARRIED_FORWARD)
     Transaction.objects.create(account=self.account,
                                invoice=invoice,
                                amount=Money(15, 'CHF'),
                                success=True)
     with self.assertNumQueries(1):
         assert invoice.total_charges() == Total(10, 'CHF')
     # Just to demonstrate that the due amount is completely different:
     assert invoice.due() == Total(0, 'CHF')
    def test_full_scenario(self):
        # 1- At first the invoice is partially paid.
        paid_invoice_ids = accounts.assign_funds_to_account_pending_invoices(account_id=self.account.id)
        assert paid_invoice_ids == []
        self.invoice1.refresh_from_db()
        assert_attrs(self.invoice1,
                     {'status': Invoice.PENDING,
                      'items': [
                          {'amount': Money(40, 'CHF'), 'product_code': 'ACHARGE'},
                      ],
                      'transactions': [
                          {'amount': Money(30, 'CHF')},
                      ]})

        # 2- A payment is made with more than enough money to pay the invoice.
        transaction2 = Transaction.objects.create(account=self.account, amount=Money(28, 'CHF'), success=True)
        paid_invoice_ids = accounts.assign_funds_to_account_pending_invoices(account_id=self.account.id)
        assert paid_invoice_ids == [self.invoice1.pk]
        transaction2.refresh_from_db()
        assert transaction2.invoice == self.invoice1
        self.invoice1.refresh_from_db()
        assert_attrs(self.invoice1,
                     {'status': Invoice.PAID,
                      'items': [
                          {'amount': Money(40, 'CHF'), 'product_code': 'ACHARGE'},
                          {'amount': Money(18, 'CHF'), 'product_code': CARRIED_FORWARD},
                      ],
                      'transactions': [
                          {'amount': Money(30, 'CHF')},
                          {'amount': Money(28, 'CHF')},
                      ]})
        assert self.invoice1.due() == Total(Money(0, 'CHF'))
        uninvoiced_charges = Charge.objects.uninvoiced(account_id=self.account.id)
        assert len(uninvoiced_charges) == 1
        uninvoiced_charge = uninvoiced_charges[0]
        assert_attrs(uninvoiced_charge,
                     {'amount': Money(-18, 'CHF'), 'product_code': CREDIT_REMAINING})

        # 3- A second charge is added to the account.
        invoice2 = Invoice.objects.create(account_id=self.account.id, due_date=date.today())
        Charge.objects.create(account=self.account, invoice=invoice2, amount=Money(12, 'CHF'),
                              product_code='BCHARGE')
        paid_invoice_ids = accounts.assign_funds_to_account_pending_invoices(account_id=self.account.id)
        assert paid_invoice_ids == [invoice2.pk]
        invoice2.refresh_from_db()
        assert_attrs(invoice2,
                     {'status': Invoice.PAID,
                      'items': [
                          {'amount': Money(-18, 'CHF'), 'product_code': CREDIT_REMAINING},
                          {'amount': Money(12, 'CHF'), 'product_code': 'BCHARGE'},
                          {'amount': Money(6, 'CHF'), 'product_code': CARRIED_FORWARD},
                      ]})
        assert invoice2.due() == Total(Money(0, 'CHF'))
        uninvoiced_charges = Charge.objects.uninvoiced(account_id=self.account.id)
        assert len(uninvoiced_charges) == 1
        uninvoiced_charge = uninvoiced_charges[0]
        assert_attrs(uninvoiced_charge, {'amount': Money(-6, 'CHF'), 'product_code': CREDIT_REMAINING})
 def test_it_should_create_an_invoice_even_when_enough_credit(self):
     Charge.objects.create(account=self.account,
                           amount=Money(10, 'CHF'),
                           product_code='ACHARGE')
     Charge.objects.create(account=self.account,
                           amount=Money(-30, 'CHF'),
                           product_code='ACREDIT')
     invoices = accounts.create_invoices(account_id=self.account.pk,
                                         due_date=date.today())
     assert len(invoices) == 1
     invoice = invoices[0]
     assert invoice.total_charges() == Total(10, 'CHF')
     assert invoice.due() == Total(10, 'CHF')
     assert invoice.items.count() == 1
Exemple #8
0
 def test_uninvoiced_should_ignore_invoiced_charges(self):
     Invoice.objects.create(id=1, account=self.account, due_date=date.today())
     Charge.objects.create(account=self.account, invoice_id=1, amount=Money(10, 'CHF'), product_code='ACHARGE')
     with self.assertNumQueries(2):
         uc = Charge.objects.uninvoiced(account_id=self.account.pk)
         assert len(uc) == 0
         assert total_amount(uc) == Total()
Exemple #9
0
 def test_uninvoiced_should_consider_credits(self):
     Charge.objects.create(account=self.account, amount=Money(10, 'CHF'), product_code='ACHARGE')
     Charge.objects.create(account=self.account, amount=Money(-30, 'CHF'), product_code='ACREDIT')
     with self.assertNumQueries(2):
         uc = Charge.objects.uninvoiced(account_id=self.account.pk)
         assert len(uc) == 2
         assert total_amount(uc) == Total(-20, 'CHF')
Exemple #10
0
 def test_uninvoiced_can_be_in_multiple_currencies(self):
     Charge.objects.create(account=self.account, amount=Money(10, 'CHF'), product_code='ACHARGE')
     Charge.objects.create(account=self.account, amount=Money(-30, 'EUR'), product_code='ACREDIT')
     with self.assertNumQueries(2):
         uc = Charge.objects.uninvoiced(account_id=self.account.pk)
         assert len(uc) == 2
         assert total_amount(uc) == Total(10, 'CHF', -30, 'EUR')
Exemple #11
0
 def test_uninvoiced_should_ignore_deleted_charges(self):
     Charge.objects.create(account=self.account, amount=Money(10, 'CHF'), product_code='ACHARGE')
     Charge.objects.create(account=self.account, deleted=True, amount=Money(5, 'CHF'), product_code='BCHARGE')
     with self.assertNumQueries(2):
         uc = Charge.objects.uninvoiced(account_id=self.account.pk)
         assert len(uc) == 1
         assert total_amount(uc) == Total(10, 'CHF')
Exemple #12
0
 def test_it_should_compute_the_invoice_due_ignoring_deleted_charges(self):
     invoice = Invoice.objects.create(account=self.account, due_date=date.today())
     Charge.objects.create(account=self.account, invoice=invoice, amount=Money(10, 'CHF'), product_code='ACHARGE')
     Charge.objects.create(account=self.account, invoice=invoice, amount=Money(-3, 'CHF'), product_code='ACREDIT')
     Charge.objects.create(account=self.account, invoice=invoice, amount=Money(1000, 'CHF'), product_code='ACHARGE',
                           deleted=True)
     with self.assertNumQueries(2):
         assert invoice.due() == Total(7, 'CHF')
Exemple #13
0
 def test_zero_and_nonzero_values(self):
     t = Total(100, 'USD', 0, 'EUR')
     assert TotalSerializer(t).data == [
         {'amount': '100.00', 'amount_currency': 'USD'}
     ]
     assert TotalIncludingZeroSerializer(t).data == [
         {'amount': '100.00', 'amount_currency': 'USD'},
         {'amount': '0.00', 'amount_currency': 'EUR'}
     ]
    def test_it_should_pay_invoice_with_already_assigned_payment(self):
        invoice = Invoice.objects.create(account_id=self.account.id, due_date=date.today())
        Transaction.objects.create(account=self.account, invoice=invoice, amount=Money(40, 'CHF'), success=True)
        Charge.objects.create(account=self.account, invoice=invoice, amount=Money(40, 'CHF'), product_code='ACHARGE')

        with self.assertNumQueries(4):
            paid = accounts.assign_funds_to_invoice(invoice_id=invoice.pk)
        assert paid
        assert invoice.due() == Total([Money(0, 'CHF')])
Exemple #15
0
 def test_unsuccessful_transactions_should_not_impact_the_balance(self):
     account = Account.objects.create(owner=self.user, currency='CHF')
     Charge.objects.create(account=account, amount=Money(10, 'CHF'), product_code='ACHARGE')
     psp_payment = MyPSPPayment(payment_ref='apaymentref')
     Transaction.objects.create(account=account, amount=Money(6, 'CHF'), success=False,
                                payment_method='VIS', credit_card_number='4111 1111 1111 1111',
                                psp_object=psp_payment)
     with self.assertNumQueries(2):
         assert account.balance() == Total(-10, 'CHF')
Exemple #16
0
 def test_balance_as_of_date_should_ignore_more_recent_charges(self):
     account = Account.objects.create(owner=self.user, currency='CHF')
     old_charge = Charge.objects.create(account=account, amount=Money(5, 'CHF'), product_code='OLD')
     # It's not possible to prevent auto-add-now from setting the current time, so we do 2 steps
     old_charge.created = parse_datetime('2015-01-01T00:00:00Z')
     old_charge.save()
     Charge.objects.create(account=account, amount=Money(10, 'CHF'), product_code='TODAY')
     with self.assertNumQueries(2):
         assert account.balance(as_of=parse_datetime('2016-06-06T00:00:00Z')) == Total([Money(-5, 'CHF')])
    def test_it_should_create_an_invoice_when_money_is_due(self):
        Charge.objects.create(account=self.account,
                              amount=Money(10, 'CHF'),
                              product_code='ACHARGE')
        Charge.objects.create(account=self.account,
                              amount=Money(-3, 'CHF'),
                              product_code='ACREDIT')

        invoices = accounts.create_invoices(account_id=self.account.pk,
                                            due_date=date.today())
        assert len(invoices) == 1
        invoice = invoices[0]
        assert invoice.total_charges() == Total(10, 'CHF')
        assert invoice.due() == Total(10, 'CHF')
        assert invoice.items.count() == 1

        # Verify there is nothing left to invoice on this account
        assert not accounts.create_invoices(account_id=self.account.pk,
                                            due_date=date.today())
Exemple #18
0
 def test_it_should_compute_the_account_balance_in_multiple_currencies(
         self):
     account = Account.objects.create(owner=self.user, currency='CHF')
     Charge.objects.create(account=account,
                           amount=Money(10, 'CHF'),
                           product_code='ACHARGE')
     Charge.objects.create(account=account,
                           amount=Money(-3, 'EUR'),
                           product_code='ACREDIT')
     with self.assertNumQueries(2):
         assert account.balance() == Total(-10, 'CHF', 3, 'EUR')
    def test_it_should_assign_funds_even_if_not_enough_to_pay_invoice_fully(self):
        invoice = Invoice.objects.create(account_id=self.account.id, due_date=date.today())
        Charge.objects.create(account=self.account, invoice=invoice, amount=Money(40, 'CHF'), product_code='ACHARGE')
        transaction = Transaction.objects.create(account=self.account, amount=Money(31, 'CHF'), success=True)

        with self.assertNumQueries(6):
            paid = accounts.assign_funds_to_invoice(invoice_id=invoice.pk)
        assert not paid
        transaction.refresh_from_db()
        assert transaction.invoice == invoice
        invoice.refresh_from_db()
        assert invoice.status == Invoice.PENDING
        assert invoice.due() == Total([Money(9, 'CHF')])
    def test_it_should_assign_credit_to_invoice_and_pay_it(self):
        invoice = Invoice.objects.create(account_id=self.account.id, due_date=date.today())
        Charge.objects.create(account=self.account, invoice=invoice, amount=Money(40, 'CHF'), product_code='ACHARGE')
        credit = Charge.objects.create(account=self.account, amount=Money(-40, 'CHF'))

        with self.assertNumQueries(7):
            paid = accounts.assign_funds_to_invoice(invoice_id=invoice.pk)
        assert paid
        credit.refresh_from_db()
        assert credit.invoice == invoice
        invoice.refresh_from_db()
        assert invoice.status == Invoice.PAID
        assert invoice.due() == Total([Money(0, 'CHF')])
Exemple #21
0
 def test_it_should_compute_the_invoice_due_in_multiple_currencies(self):
     invoice = Invoice.objects.create(account=self.account,
                                      due_date=date.today())
     Charge.objects.create(account=self.account,
                           invoice=invoice,
                           amount=Money(10, 'CHF'),
                           product_code='ACHARGE')
     Charge.objects.create(account=self.account,
                           invoice=invoice,
                           amount=Money(-3, 'EUR'),
                           product_code='ACREDIT')
     with self.assertNumQueries(2):
         assert invoice.due() == Total(10, 'CHF', -3, 'EUR')
Exemple #22
0
 def test_it_should_compute_the_invoice_due_when_overpayment(self):
     invoice = Invoice.objects.create(account=self.account,
                                      due_date=date.today())
     Charge.objects.create(account=self.account,
                           invoice=invoice,
                           amount=Money(10, 'CHF'),
                           product_code='ACHARGE')
     Transaction.objects.create(account=self.account,
                                invoice=invoice,
                                amount=Money(15, 'CHF'),
                                success=True)
     with self.assertNumQueries(2):
         assert invoice.due() == Total(-5, 'CHF')
    def test_it_should_assign_multiple_payments_to_invoice_and_pay_it(self):
        invoice = Invoice.objects.create(account_id=self.account.id, due_date=date.today())
        Charge.objects.create(account=self.account, invoice=invoice, amount=Money(40, 'CHF'), product_code='ACHARGE')
        transaction_1 = Transaction.objects.create(account=self.account, amount=Money(15, 'CHF'), success=True)
        transaction_2 = Transaction.objects.create(account=self.account, amount=Money(25, 'CHF'), success=True)

        with self.assertNumQueries(8):
            paid = accounts.assign_funds_to_invoice(invoice_id=invoice.pk)
        assert paid
        transaction_1.refresh_from_db()
        assert transaction_1.invoice == invoice
        transaction_2.refresh_from_db()
        assert transaction_2.invoice == invoice
        invoice.refresh_from_db()
        assert invoice.status == Invoice.PAID
        assert invoice.due() == Total([Money(0, 'CHF')])
Exemple #24
0
 def test_abs(self):
     total_neg = Total(-10, 'USD', 20, 'GBP')
     t = abs(total_neg)
     assert t['USD'].amount == 10
     assert t['GBP'].amount == 20
     assert t['EUR'].amount == 0
Exemple #25
0
    def test_neq(self):
        assert not (Total() != Total())
        assert not (Total(0, 'USD') != Total())

        t1 = Total(100, 'USD', 100, 'EUR')
        t2 = Total(80, 'USD', 150, 'GBP')
        assert not (t1 != +t1)
        assert t1 != t2
        assert not (Total([Money(100, 'USD')]) != Total([Money(100, 'USD')]))
        assert not (Total([Money(100, 'USD'), Money(0, 'EUR')]) != Total([Money(100, 'USD')]))

        assert Total([Money(100, 'USD'), Money(10, 'EUR')]) != Total([Money(100, 'USD')])
Exemple #26
0
 def test_unique_currency(self):
     with raises(ValueError):
         Total([Money(0, 'USD'), Money(0, 'USD')])
Exemple #27
0
 def test_eq_zero(self):
     assert Total() == 0
     assert Total(0, 'USD') == 0
     assert Total(0, 'USD', 0, 'CHF') == 0
     assert not (Total(100, 'USD', 100, 'EUR') == 0)
Exemple #28
0
    def test_eq(self):
        assert Total() == Total()
        assert Total(0, 'USD') == Total()

        t1 = Total(100, 'USD', 100, 'EUR')
        t2 = Total(80, 'USD', 150, 'GBP')
        assert t1 == +t1
        assert not (t1 == t2)
        assert Total(100, 'USD') == Total(100, 'USD')
        assert Total(100, 'USD', 0, 'EUR') == Total(100, 'USD')

        assert not (Total(100, 'USD', 10, 'EUR') == Total(100, 'USD'))
Exemple #29
0
 def test_bool(self):
     assert not bool(Total())
     assert not bool(Total(0, 'USD'))
     assert bool(Total(100, 'USD'))
     assert bool(Total(0, 'USD', 100, 'EUR'))
     assert not bool(Total(0, 'USD', 0, 'EUR'))
Exemple #30
0
 def test_pos(self):
     t1 = Total(100, 'USD', 100, 'EUR')
     t = +t1
     assert t['USD'].amount == 100
     assert t['EUR'].amount == 100
     assert t['GBP'].amount == 0