def test_it_should_do_nothing_when_no_funds(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')

        with self.assertNumQueries(5):
            paid = accounts.assign_funds_to_invoice(invoice_id=invoice.pk)
        assert not paid
    def test_it_shoud_generate_credit_remaining_when_payment_is_larger_than_invoice(self):
        invoice = Invoice.objects.create(account_id=self.account.id, due_date=date.today())
        charge = Charge.objects.create(account=self.account, invoice=invoice,
                                       amount=Money(40, 'CHF'), product_code='ACHARGE')
        transaction = Transaction.objects.create(account=self.account, amount=Money(50, 'CHF'), success=True)

        with self.assertNumQueries(11):
            paid = accounts.assign_funds_to_invoice(invoice_id=invoice.pk)
        assert paid
        transaction.refresh_from_db()
        assert transaction.invoice == invoice
        invoice.refresh_from_db()
        assert_attrs(invoice,
                     {'status': Invoice.PAID,
                      'items': [
                          {'id': charge.id, 'amount': Money(40, 'CHF'), 'product_code': 'ACHARGE'},
                          {'amount': Money(10, 'CHF'), 'product_code': CARRIED_FORWARD}
                      ],
                      'transactions': [
                          {'id': transaction.id, 'amount': Money(50, 'CHF'), 'success': True}
                      ]})
        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(-10, 'CHF'), 'product_code': CREDIT_REMAINING})
    def test_it_should_ignore_unsuccesful_payment(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.objects.create(account=self.account, amount=Money(100, 'CHF'), success=False)

        with self.assertNumQueries(5):
            paid = accounts.assign_funds_to_invoice(invoice_id=invoice.pk)
        assert not paid
    def test_it_should_ignore_funds_in_the_wrong_currency(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.objects.create(account=self.account, amount=Money(40, 'EUR'), success=True)
        Charge.objects.create(account=self.account, amount=Money(-40, 'EUR'))

        with self.assertNumQueries(5):
            paid = accounts.assign_funds_to_invoice(invoice_id=invoice.pk)
        assert not paid
    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')])
    def test_it_should_ignore_funds_that_are_assigned_to_an_other_invoice(self):
        old_invoice = Invoice.objects.create(account_id=self.account.id, due_date=date.today())
        Transaction.objects.create(account=self.account, amount=Money(100, 'CHF'), invoice=old_invoice, success=True)

        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')

        with self.assertNumQueries(5):
            paid = accounts.assign_funds_to_invoice(invoice_id=invoice.pk)
        assert not paid
    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')])
    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_use_credits_before_payments(self):
        invoice = Invoice.objects.create(account_id=self.account.id, due_date=date.today())
        Charge.objects.create(account=self.account, invoice=invoice, amount=Money(10, 'CHF'), product_code='ACHARGE')
        transaction = Transaction.objects.create(account=self.account, amount=Money(10, 'CHF'), success=True)
        credit = Charge.objects.create(account=self.account, amount=Money(-10, 'CHF'), product_code='ACREDIT')

        with self.assertNumQueries(7):
            paid = accounts.assign_funds_to_invoice(invoice_id=invoice.pk)
        assert paid
        # Verify that the credit was used (even though the transaction was older)
        transaction.refresh_from_db()
        assert transaction.invoice is None
        credit.refresh_from_db()
        assert credit.invoice == invoice
    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')])
    def test_it_should_use_oldest_payments_first(self):
        invoice = Invoice.objects.create(account_id=self.account.id, due_date=date.today())
        Charge.objects.create(account=self.account, invoice=invoice, amount=Money(11, 'CHF'), product_code='ACHARGE')
        transaction_1 = Transaction.objects.create(account=self.account, amount=Money(5, 'CHF'), success=True)
        transaction_2 = Transaction.objects.create(account=self.account, amount=Money(6, 'CHF'), success=True)
        transaction_3 = Transaction.objects.create(account=self.account, amount=Money(7, '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
        transaction_3.refresh_from_db()
        assert transaction_3.invoice is None