Changeset - fd9980efc578
[Not reviewed]
0 2 0
Christopher Neugebauer - 8 years ago 2016-09-15 01:41:50
chrisjrn@gmail.com
Makes sure we only apply unclaimed credit notes when auto-applying credit notes.
2 files changed with 15 insertions and 1 deletions:
0 comments (0 inline, 0 general)
registrasion/controllers/invoice.py
Show inline comments
...
 
@@ -172,97 +172,99 @@ class InvoiceController(ForId, object):
 
        # Never generate a due time that is before the issue time
 
        issued = timezone.now()
 
        due = max(issued, min_due_time)
 

	
 
        # Get the invoice recipient
 
        profile = people.AttendeeProfileBase.objects.get_subclass(
 
            id=user.attendee.attendeeprofilebase.id,
 
        )
 
        recipient = profile.invoice_recipient()
 

	
 
        invoice_value = sum(item.quantity * item.price for item in line_items)
 

	
 
        invoice = commerce.Invoice.objects.create(
 
            user=user,
 
            cart=cart,
 
            cart_revision=cart.revision if cart else None,
 
            status=commerce.Invoice.STATUS_UNPAID,
 
            value=invoice_value,
 
            issue_time=issued,
 
            due_time=due,
 
            recipient=recipient,
 
        )
 

	
 
        # Associate the line items with the invoice
 
        for line_item in line_items:
 
            line_item.invoice = invoice
 

	
 
        commerce.LineItem.objects.bulk_create(line_items)
 

	
 
        cls._apply_credit_notes(invoice)
 
        cls.email_on_invoice_creation(invoice)
 

	
 
        return invoice
 

	
 
    @classmethod
 
    def _apply_credit_notes(cls, invoice):
 
        ''' Applies the user's credit notes to the given invoice on creation.
 
        '''
 

	
 
        # We only automatically apply credit notes if this is the *only*
 
        # unpaid invoice for this user.
 
        invoices = commerce.Invoice.objects.filter(
 
            user=invoice.user,
 
            status=commerce.Invoice.STATUS_UNPAID,
 
        )
 
        if invoices.count() > 1:
 
            return
 

	
 
        notes = commerce.CreditNote.objects.filter(invoice__user=invoice.user)
 
        notes = commerce.CreditNote.unclaimed().filter(
 
            invoice__user=invoice.user
 
        )
 
        for note in notes:
 
            try:
 
                CreditNoteController(note).apply_to_invoice(invoice)
 
            except ValidationError:
 
                # ValidationError will get raised once we're overpaying.
 
                break
 

	
 
        invoice.refresh_from_db()
 

	
 
    def can_view(self, user=None, access_code=None):
 
        ''' Returns true if the accessing user is allowed to view this invoice,
 
        or if the given access code matches this invoice's user's access code.
 
        '''
 

	
 
        if user == self.invoice.user:
 
            return True
 

	
 
        if user.is_staff:
 
            return True
 

	
 
        if self.invoice.user.attendee.access_code == access_code:
 
            return True
 

	
 
        return False
 

	
 
    def _refresh(self):
 
        ''' Refreshes the underlying invoice and cart objects. '''
 
        self.invoice.refresh_from_db()
 
        if self.invoice.cart:
 
            self.invoice.cart.refresh_from_db()
 

	
 
    def validate_allowed_to_pay(self):
 
        ''' Passes cleanly if we're allowed to pay, otherwise raise
 
        a ValidationError. '''
 

	
 
        self._refresh()
 

	
 
        if not self.invoice.is_unpaid:
 
            raise ValidationError("You can only pay for unpaid invoices.")
 

	
 
        if not self.invoice.cart:
 
            return
 

	
 
        if not self._invoice_matches_cart():
 
            raise ValidationError("The registration has been amended since "
 
                                  "generating this invoice.")
 

	
 
        CartController(self.invoice.cart).validate_cart()
registrasion/tests/test_credit_note.py
Show inline comments
...
 
@@ -383,48 +383,60 @@ class CreditNoteTestCase(TestHelperMixin, RegistrationCartTestCase):
 
        notes_value = self._generate_multiple_credit_notes()
 
        invoice = self._manual_invoice(notes_value - 1)
 

	
 

	
 
        self.assertEqual(notes_value - 1, invoice.total_payments())
 
        self.assertTrue(invoice.invoice.is_paid)
 

	
 
        user_unclaimed = commerce.CreditNote.unclaimed().filter(
 
            invoice__user=self.USER_1
 
        )
 
        self.assertEqual(1, user_unclaimed.count())
 

	
 
        excess = self._credit_note_for_invoice(invoice.invoice)
 
        self.assertEqual(excess.credit_note.value, 1)
 

	
 
    def test_credit_notes_are_left_over_if_not_all_are_needed(self):
 
        ''' Tests that excess credit notes are untouched if they're not needed
 
        '''
 

	
 
        notes_value = self._generate_multiple_credit_notes()
 
        notes_old = commerce.CreditNote.unclaimed().filter(
 
            invoice__user=self.USER_1
 
        )
 

	
 
        # Create a manual invoice whose value is smaller than any of the
 
        # credit notes we created
 
        invoice = self._manual_invoice(1)
 
        notes_new = commerce.CreditNote.unclaimed().filter(
 
            invoice__user=self.USER_1
 
        )
 

	
 
        # Item is True if the note was't consumed when generating invoice.
 
        note_was_unused = [(i in notes_old) for i in notes_new]
 
        self.assertIn(True, note_was_unused)
 

	
 
    def test_credit_notes_are_not_applied_if_user_has_multiple_invoices(self):
 

	
 
        # Have an invoice pending with no credit notes; no payment will be made
 
        invoice1 = self._invoice_containing_prod_1(1)
 
        # Create some credit notes.
 
        self._generate_multiple_credit_notes()
 

	
 
        invoice = self._manual_invoice(2)
 

	
 
        # Because there's already an invoice open for this user
 
        # The credit notes are not automatically applied.
 
        self.assertEqual(0, invoice.total_payments())
 
        self.assertTrue(invoice.invoice.is_unpaid)
 

	
 
    def test_credit_notes_are_applied_even_if_some_notes_are_claimed(self):
 

	
 
        for i in xrange(10):
 
            # Generate credit note
 
            invoice1 = self._manual_invoice(1)
 
            invoice1.pay("Pay", invoice1.invoice.value)
 
            invoice1.refund()
 

	
 
            # Generate invoice that should be automatically paid
 
            invoice2 = self._manual_invoice(1)
 
            self.assertTrue(invoice2.invoice.is_paid)
0 comments (0 inline, 0 general)