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:
Christopher Neugebauer 2016-04-25 08:13:44 +10:00
parent 9a4574ef2c
commit 12e04c248f
3 changed files with 64 additions and 18 deletions

View file

@ -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. '''

View file

@ -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(

View file

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