Credit notes are now generated when invoices are overpaid, or invoices are paid into void or refunded invoices. Closes #37.
This commit is contained in:
parent
9a4574ef2c
commit
12e04c248f
3 changed files with 64 additions and 18 deletions
|
@ -13,6 +13,7 @@ from cart import CartController
|
||||||
from credit_note import CreditNoteController
|
from credit_note import CreditNoteController
|
||||||
from for_id import ForId
|
from for_id import ForId
|
||||||
|
|
||||||
|
|
||||||
class InvoiceController(ForId, object):
|
class InvoiceController(ForId, object):
|
||||||
|
|
||||||
__MODEL__ = commerce.Invoice
|
__MODEL__ = commerce.Invoice
|
||||||
|
@ -195,11 +196,6 @@ class InvoiceController(ForId, object):
|
||||||
# Invoice no longer has amount owing
|
# Invoice no longer has amount owing
|
||||||
self._mark_paid()
|
self._mark_paid()
|
||||||
|
|
||||||
if remainder < 0:
|
|
||||||
CreditNoteController.generate_from_invoice(
|
|
||||||
self.invoice,
|
|
||||||
0 - remainder,
|
|
||||||
)
|
|
||||||
elif total_paid == 0 and num_payments > 0:
|
elif total_paid == 0 and num_payments > 0:
|
||||||
# Invoice has multiple payments totalling zero
|
# Invoice has multiple payments totalling zero
|
||||||
self._mark_void()
|
self._mark_void()
|
||||||
|
@ -215,6 +211,17 @@ class InvoiceController(ForId, object):
|
||||||
# Should not ever change from here
|
# Should not ever change from here
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Generate credit notes from residual payments
|
||||||
|
residual = 0
|
||||||
|
if self.invoice.is_paid:
|
||||||
|
if remainder < 0:
|
||||||
|
residual = 0 - remainder
|
||||||
|
elif self.invoice.is_void or self.invoice.is_refunded:
|
||||||
|
residual = total_paid
|
||||||
|
|
||||||
|
if residual != 0:
|
||||||
|
CreditNoteController.generate_from_invoice(self.invoice, residual)
|
||||||
|
|
||||||
def _mark_paid(self):
|
def _mark_paid(self):
|
||||||
''' Marks the invoice as paid, and updates the attached cart if
|
''' Marks the invoice as paid, and updates the attached cart if
|
||||||
necessary. '''
|
necessary. '''
|
||||||
|
|
|
@ -34,11 +34,14 @@ class TestingCartController(CartController):
|
||||||
|
|
||||||
class TestingInvoiceController(InvoiceController):
|
class TestingInvoiceController(InvoiceController):
|
||||||
|
|
||||||
def pay(self, reference, amount):
|
def pay(self, reference, amount, pre_validate=True):
|
||||||
''' Testing method for simulating an invoice paymenht by the given
|
''' Testing method for simulating an invoice paymenht by the given
|
||||||
amount. '''
|
amount. '''
|
||||||
|
|
||||||
self.validate_allowed_to_pay()
|
if pre_validate:
|
||||||
|
# Manual payments don't pre-validate; we should test that things
|
||||||
|
# still work if we do silly things.
|
||||||
|
self.validate_allowed_to_pay()
|
||||||
|
|
||||||
''' Adds a payment '''
|
''' Adds a payment '''
|
||||||
commerce.ManualPayment.objects.create(
|
commerce.ManualPayment.objects.create(
|
||||||
|
|
|
@ -24,6 +24,10 @@ class InvoiceTestCase(RegistrationCartTestCase):
|
||||||
|
|
||||||
return TestingInvoiceController.for_cart(self.reget(cart.cart))
|
return TestingInvoiceController.for_cart(self.reget(cart.cart))
|
||||||
|
|
||||||
|
def _credit_note_for_invoice(self, invoice):
|
||||||
|
note = commerce.CreditNote.objects.get(invoice=invoice)
|
||||||
|
return TestingCreditNoteController(note)
|
||||||
|
|
||||||
def test_create_invoice(self):
|
def test_create_invoice(self):
|
||||||
current_cart = TestingCartController.for_user(self.USER_1)
|
current_cart = TestingCartController.for_user(self.USER_1)
|
||||||
|
|
||||||
|
@ -314,8 +318,7 @@ class InvoiceTestCase(RegistrationCartTestCase):
|
||||||
invoice.refund()
|
invoice.refund()
|
||||||
|
|
||||||
# There should be one credit note generated out of the invoice.
|
# There should be one credit note generated out of the invoice.
|
||||||
credit_note = commerce.CreditNote.objects.get(invoice=invoice.invoice)
|
cn = self._credit_note_for_invoice(invoice.invoice)
|
||||||
cn = TestingCreditNoteController(credit_note)
|
|
||||||
|
|
||||||
# That credit note should be in the unclaimed pile
|
# That credit note should be in the unclaimed pile
|
||||||
self.assertEquals(1, commerce.CreditNote.unclaimed().count())
|
self.assertEquals(1, commerce.CreditNote.unclaimed().count())
|
||||||
|
@ -342,8 +345,7 @@ class InvoiceTestCase(RegistrationCartTestCase):
|
||||||
invoice.refund()
|
invoice.refund()
|
||||||
|
|
||||||
# There should be one credit note generated out of the invoice.
|
# There should be one credit note generated out of the invoice.
|
||||||
credit_note = commerce.CreditNote.objects.get(invoice=invoice.invoice)
|
cn = self._credit_note_for_invoice(invoice.invoice)
|
||||||
cn = TestingCreditNoteController(credit_note)
|
|
||||||
|
|
||||||
self.assertEquals(1, commerce.CreditNote.unclaimed().count())
|
self.assertEquals(1, commerce.CreditNote.unclaimed().count())
|
||||||
|
|
||||||
|
@ -381,8 +383,7 @@ class InvoiceTestCase(RegistrationCartTestCase):
|
||||||
invoice.refund()
|
invoice.refund()
|
||||||
|
|
||||||
# There should be one credit note generated out of the invoice.
|
# There should be one credit note generated out of the invoice.
|
||||||
credit_note = commerce.CreditNote.objects.get(invoice=invoice.invoice)
|
cn = self._credit_note_for_invoice(invoice.invoice)
|
||||||
cn = TestingCreditNoteController(credit_note)
|
|
||||||
|
|
||||||
# Create a new cart with invoice, pay it
|
# Create a new cart with invoice, pay it
|
||||||
invoice_2 = self._invoice_containing_prod_1(1)
|
invoice_2 = self._invoice_containing_prod_1(1)
|
||||||
|
@ -415,9 +416,8 @@ class InvoiceTestCase(RegistrationCartTestCase):
|
||||||
|
|
||||||
self.assertEquals(1, commerce.CreditNote.unclaimed().count())
|
self.assertEquals(1, commerce.CreditNote.unclaimed().count())
|
||||||
|
|
||||||
credit_note = commerce.CreditNote.objects.get(invoice=invoice.invoice)
|
cn = self._credit_note_for_invoice(invoice.invoice)
|
||||||
|
|
||||||
cn = TestingCreditNoteController(credit_note)
|
|
||||||
cn.refund()
|
cn.refund()
|
||||||
|
|
||||||
# Refunding a credit note should mark it as claimed
|
# Refunding a credit note should mark it as claimed
|
||||||
|
@ -444,9 +444,7 @@ class InvoiceTestCase(RegistrationCartTestCase):
|
||||||
|
|
||||||
self.assertEquals(1, commerce.CreditNote.unclaimed().count())
|
self.assertEquals(1, commerce.CreditNote.unclaimed().count())
|
||||||
|
|
||||||
credit_note = commerce.CreditNote.objects.get(invoice=invoice.invoice)
|
cn = self._credit_note_for_invoice(invoice.invoice)
|
||||||
|
|
||||||
cn = TestingCreditNoteController(credit_note)
|
|
||||||
|
|
||||||
# Create a new cart with invoice
|
# Create a new cart with invoice
|
||||||
cart = TestingCartController.for_user(self.USER_1)
|
cart = TestingCartController.for_user(self.USER_1)
|
||||||
|
@ -460,3 +458,41 @@ class InvoiceTestCase(RegistrationCartTestCase):
|
||||||
# Cannot refund this credit note as it is already applied.
|
# Cannot refund this credit note as it is already applied.
|
||||||
with self.assertRaises(ValidationError):
|
with self.assertRaises(ValidationError):
|
||||||
cn.refund()
|
cn.refund()
|
||||||
|
|
||||||
|
def test_money_into_void_invoice_generates_credit_note(self):
|
||||||
|
invoice = self._invoice_containing_prod_1(1)
|
||||||
|
invoice.void()
|
||||||
|
|
||||||
|
val = invoice.invoice.value
|
||||||
|
|
||||||
|
invoice.pay("Paying into the void.", val, pre_validate=False)
|
||||||
|
cn = self._credit_note_for_invoice(invoice.invoice)
|
||||||
|
self.assertEqual(val, cn.credit_note.value)
|
||||||
|
|
||||||
|
def test_money_into_refunded_invoice_generates_credit_note(self):
|
||||||
|
invoice = self._invoice_containing_prod_1(1)
|
||||||
|
|
||||||
|
val = invoice.invoice.value
|
||||||
|
|
||||||
|
invoice.pay("Paying the first time.", val)
|
||||||
|
invoice.refund()
|
||||||
|
|
||||||
|
cnval = val - 1
|
||||||
|
invoice.pay("Paying into the void.", cnval, pre_validate=False)
|
||||||
|
|
||||||
|
notes = commerce.CreditNote.objects.filter(invoice=invoice.invoice)
|
||||||
|
notes = sorted(notes, key = lambda note: note.value)
|
||||||
|
|
||||||
|
self.assertEqual(cnval, notes[0].value)
|
||||||
|
self.assertEqual(val, notes[1].value)
|
||||||
|
|
||||||
|
def test_money_into_paid_invoice_generates_credit_note(self):
|
||||||
|
invoice = self._invoice_containing_prod_1(1)
|
||||||
|
|
||||||
|
val = invoice.invoice.value
|
||||||
|
|
||||||
|
invoice.pay("Paying the first time.", val)
|
||||||
|
|
||||||
|
invoice.pay("Paying into the void.", val, pre_validate=False)
|
||||||
|
cn = self._credit_note_for_invoice(invoice.invoice)
|
||||||
|
self.assertEqual(val, cn.credit_note.value)
|
||||||
|
|
Loading…
Reference in a new issue