Changeset - 4a50d699365b
[Not reviewed]
0 5 0
Christopher Neugebauer - 8 years ago 2016-09-15 23:35:12
chrisjrn@gmail.com
Moves total_payments() to Invoice model; adds balance_due()
5 files changed with 43 insertions and 40 deletions:
0 comments (0 inline, 0 general)
registrasion/controllers/invoice.py
Show inline comments
...
 
@@ -266,25 +266,18 @@ class InvoiceController(ForId, object):
 
        if not self._invoice_matches_cart():
 
            raise ValidationError("The registration has been amended since "
 
                                  "generating this invoice.")
 

	
 
        CartController(self.invoice.cart).validate_cart()
 

	
 
    def total_payments(self):
 
        ''' Returns the total amount paid towards this invoice. '''
 

	
 
        payments = commerce.PaymentBase.objects.filter(invoice=self.invoice)
 
        total_paid = payments.aggregate(Sum("amount"))["amount__sum"] or 0
 
        return total_paid
 

	
 
    def update_status(self):
 
        ''' Updates the status of this invoice based upon the total
 
        payments.'''
 

	
 
        old_status = self.invoice.status
 
        total_paid = self.total_payments()
 
        total_paid = self.invoice.total_payments()
 
        num_payments = commerce.PaymentBase.objects.filter(
 
            invoice=self.invoice,
 
        ).count()
 
        remainder = self.invoice.value - total_paid
 

	
 
        if old_status == commerce.Invoice.STATUS_UNPAID:
...
 
@@ -363,21 +356,21 @@ class InvoiceController(ForId, object):
 

	
 
        return cart.revision == self.invoice.cart_revision
 

	
 
    def update_validity(self):
 
        ''' Voids this invoice if the cart it is attached to has updated. '''
 
        if not self._invoice_matches_cart():
 
            if self.total_payments() > 0:
 
            if self.invoice.total_payments() > 0:
 
                # Free up the payments made to this invoice
 
                self.refund()
 
            else:
 
                self.void()
 

	
 
    def void(self):
 
        ''' Voids the invoice if it is valid to do so. '''
 
        if self.total_payments() > 0:
 
        if self.invoice.total_payments() > 0:
 
            raise ValidationError("Invoices with payments must be refunded.")
 
        elif self.invoice.is_refunded:
 
            raise ValidationError("Refunded invoices may not be voided.")
 
        self._mark_void()
 

	
 
    @transaction.atomic
...
 
@@ -391,13 +384,13 @@ class InvoiceController(ForId, object):
 
        '''
 

	
 
        if self.invoice.is_void:
 
            raise ValidationError("Void invoices cannot be refunded")
 

	
 
        # Raises a credit note fot the value of the invoice.
 
        amount = self.total_payments()
 
        amount = self.invoice.total_payments()
 

	
 
        if amount == 0:
 
            self.void()
 
            return
 

	
 
        CreditNoteController.generate_from_invoice(self.invoice, amount)
registrasion/models/commerce.py
Show inline comments
 
from . import conditions
 
from . import inventory
 

	
 
from django.contrib.auth.models import User
 
from django.core.exceptions import ValidationError
 
from django.db import models
 
from django.db.models import F, Q
 
from django.db.models import F, Q, Sum
 
from django.utils import timezone
 
from django.utils.encoding import python_2_unicode_compatible
 
from django.utils.translation import ugettext_lazy as _
 
from model_utils.managers import InheritanceManager
 

	
 

	
...
 
@@ -172,12 +172,23 @@ class Invoice(models.Model):
 
        return self.status == self.STATUS_PAID
 

	
 
    @property
 
    def is_refunded(self):
 
        return self.status == self.STATUS_REFUNDED
 

	
 
    def total_payments(self):
 
        ''' Returns the total amount paid towards this invoice. '''
 

	
 
        payments = PaymentBase.objects.filter(invoice=self)
 
        total_paid = payments.aggregate(Sum("amount"))["amount__sum"] or 0
 
        return total_paid
 

	
 
    def balance_due(self):
 
        ''' Returns the total balance remaining towards this invoice. '''
 
        return self.value - self.total_payments()
 

	
 
    # Invoice Number
 
    user = models.ForeignKey(User)
 
    cart = models.ForeignKey(Cart, null=True)
 
    cart_revision = models.IntegerField(
 
        null=True,
 
        db_index=True,
...
 
@@ -221,12 +232,17 @@ class LineItem(models.Model):
 
        ordering = ("id", )
 

	
 
    def __str__(self):
 
        return "Line: %s * %d @ %s" % (
 
            self.description, self.quantity, self.price)
 

	
 
    @property
 
    def total_price(self):
 
        ''' price * quantity '''
 
        return self.price * self.quantity
 

	
 
    invoice = models.ForeignKey(Invoice)
 
    description = models.CharField(max_length=255)
 
    quantity = models.PositiveIntegerField()
 
    price = models.DecimalField(max_digits=8, decimal_places=2)
 
    product = models.ForeignKey(inventory.Product, null=True, blank=True)
 

	
registrasion/templatetags/registrasion_tags.py
Show inline comments
...
 
@@ -71,27 +71,6 @@ def items_pending(context):
 
def items_purchased(context, category=None):
 
    ''' Returns the items purchased for this user. '''
 

	
 
    return ItemController(context.request.user).items_purchased(
 
        category=category
 
    )
 

	
 

	
 
@register.filter
 
def multiply(value, arg):
 
    ''' Multiplies value by arg.
 

	
 
    This is useful when displaying invoices, as it lets you multiply the
 
    quantity by the unit value.
 

	
 
    Arguments:
 

	
 
        value (number)
 

	
 
        arg (number)
 

	
 
    Returns:
 
        number: value * arg
 

	
 
    '''
 

	
 
    return value * arg
registrasion/tests/test_credit_note.py
Show inline comments
...
 
@@ -26,13 +26,15 @@ class CreditNoteTestCase(TestHelperMixin, RegistrationCartTestCase):
 

	
 
        # Invoice is overpaid by 1 unit
 
        to_pay = invoice.invoice.value + 1
 
        invoice.pay("Reference", to_pay)
 

	
 
        # The total paid should be equal to the value of the invoice only
 
        self.assertEqual(invoice.invoice.value, invoice.total_payments())
 
        self.assertEqual(
 
            invoice.invoice.value, invoice.invoice.total_payments()
 
        )
 
        self.assertTrue(invoice.invoice.is_paid)
 

	
 
        # There should be a credit note generated out of the invoice.
 
        credit_notes = commerce.CreditNote.objects.filter(
 
            invoice=invoice.invoice,
 
        )
...
 
@@ -43,13 +45,15 @@ class CreditNoteTestCase(TestHelperMixin, RegistrationCartTestCase):
 
        invoice = self._invoice_containing_prod_1(1)
 

	
 
        # Invoice is paid evenly
 
        invoice.pay("Reference", invoice.invoice.value)
 

	
 
        # The total paid should be equal to the value of the invoice only
 
        self.assertEqual(invoice.invoice.value, invoice.total_payments())
 
        self.assertEqual(
 
            invoice.invoice.value, invoice.invoice.total_payments()
 
        )
 
        self.assertTrue(invoice.invoice.is_paid)
 

	
 
        # There should be no credit notes
 
        credit_notes = commerce.CreditNote.objects.filter(
 
            invoice=invoice.invoice,
 
        )
...
 
@@ -61,13 +65,13 @@ class CreditNoteTestCase(TestHelperMixin, RegistrationCartTestCase):
 
        # Invoice is underpaid by 1 unit
 
        to_pay = invoice.invoice.value - 1
 
        invoice.pay("Reference", to_pay)
 
        invoice.refund()
 

	
 
        # The total paid should be zero
 
        self.assertEqual(0, invoice.total_payments())
 
        self.assertEqual(0, invoice.invoice.total_payments())
 
        self.assertTrue(invoice.invoice.is_void)
 

	
 
        # There should be a credit note generated out of the invoice.
 
        credit_notes = commerce.CreditNote.objects.filter(
 
            invoice=invoice.invoice,
 
        )
...
 
@@ -81,13 +85,13 @@ class CreditNoteTestCase(TestHelperMixin, RegistrationCartTestCase):
 
        invoice.pay("Reference", to_pay)
 
        self.assertTrue(invoice.invoice.is_paid)
 

	
 
        invoice.refund()
 

	
 
        # The total paid should be zero
 
        self.assertEqual(0, invoice.total_payments())
 
        self.assertEqual(0, invoice.invoice.total_payments())
 
        self.assertTrue(invoice.invoice.is_refunded)
 

	
 
        # There should be a credit note generated out of the invoice.
 
        credit_notes = commerce.CreditNote.objects.filter(
 
            invoice=invoice.invoice,
 
        )
...
 
@@ -364,13 +368,13 @@ class CreditNoteTestCase(TestHelperMixin, RegistrationCartTestCase):
 
        Sum of credit note values will be *LESS* than the new invoice.
 
        '''
 

	
 
        notes_value = self._generate_multiple_credit_notes()
 
        invoice = self._manual_invoice(notes_value + 1)
 

	
 
        self.assertEqual(notes_value, invoice.total_payments())
 
        self.assertEqual(notes_value, invoice.invoice.total_payments())
 
        self.assertTrue(invoice.invoice.is_unpaid)
 

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

	
...
 
@@ -381,13 +385,13 @@ 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.assertEqual(notes_value - 1, invoice.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())
...
 
@@ -423,13 +427,13 @@ class CreditNoteTestCase(TestHelperMixin, RegistrationCartTestCase):
 
        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.assertEqual(0, invoice.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
registrasion/tests/test_invoice.py
Show inline comments
...
 
@@ -94,12 +94,23 @@ class InvoiceTestCase(TestHelperMixin, RegistrationCartTestCase):
 
        )
 

	
 
        # Asking for a cart should generate a new one
 
        new_cart = TestingCartController.for_user(self.USER_1)
 
        self.assertNotEqual(invoice.invoice.cart, new_cart.cart)
 

	
 
    def test_total_payments_balance_due(self):
 
        invoice = self._invoice_containing_prod_1(2)
 
        for i in xrange(0, invoice.invoice.value):
 
            self.assertTrue(
 
                i + 1, invoice.invoice.total_payments()
 
            )
 
            self.assertTrue(
 
                invoice.invoice.value - i, invoice.invoice.balance_due()
 
            )
 
            invoice.pay("Pay 1", 1)
 

	
 
    def test_invoice_includes_discounts(self):
 
        voucher = inventory.Voucher.objects.create(
 
            recipient="Voucher recipient",
 
            code="VOUCHER",
 
            limit=1
 
        )
0 comments (0 inline, 0 general)