Changeset - 38cdb8aa6330
[Not reviewed]
0 6 1
Christopher Neugebauer - 8 years ago 2016-04-07 07:16:56
chrisjrn@gmail.com
Makes invoice model, controller, and test changes to match issue #15 design doc
7 files changed with 310 insertions and 96 deletions:
0 comments (0 inline, 0 general)
registrasion/controllers/invoice.py
Show inline comments
 
from decimal import Decimal
 
from django.core.exceptions import ObjectDoesNotExist
 
from django.core.exceptions import ValidationError
 
from django.db import transaction
 
from django.db.models import Sum
 
from django.utils import timezone
 

	
 
from registrasion import models as rego
 

	
 
from cart import CartController
 

	
 

	
 
class InvoiceController(object):
 

	
 
    def __init__(self, invoice):
 
        self.invoice = invoice
 
        self.update_status()
 
        self.update_validity()  # Make sure this invoice is up-to-date
 

	
 
    @classmethod
 
    def for_cart(cls, cart):
 
        ''' Returns an invoice object for a given cart at its current revision.
 
        If such an invoice does not exist, the cart is validated, and if valid,
 
        an invoice is generated.'''
 

	
 
        try:
 
            invoice = rego.Invoice.objects.get(
 
            invoice = rego.Invoice.objects.exclude(
 
                status=rego.Invoice.STATUS_VOID,
 
            ).get(
 
                cart=cart,
 
                cart_revision=cart.revision,
 
                void=False,
 
            )
 
        except ObjectDoesNotExist:
 
            cart_controller = CartController(cart)
 
            cart_controller.validate_cart()  # Raises ValidationError on fail.
 

	
 
            # Void past invoices for this cart
 
            rego.Invoice.objects.filter(cart=cart).update(void=True)
 

	
 
            cls.void_all_invoices(cart)
 
            invoice = cls._generate(cart)
 

	
 
        return InvoiceController(invoice)
 
        return cls(invoice)
 

	
 
    @classmethod
 
    def void_all_invoices(cls, cart):
 
        invoices = rego.Invoice.objects.filter(cart=cart).all()
 
        for invoice in invoices:
 
            cls(invoice).void()
 

	
 
    @classmethod
 
    def resolve_discount_value(cls, item):
 
        try:
 
            condition = rego.DiscountForProduct.objects.get(
 
                discount=item.discount,
...
 
@@ -57,17 +64,27 @@ class InvoiceController(object):
 
        return value
 

	
 
    @classmethod
 
    @transaction.atomic
 
    def _generate(cls, cart):
 
        ''' Generates an invoice for the given cart. '''
 

	
 
        issued = timezone.now()
 
        reservation_limit = cart.reservation_duration + cart.time_last_updated
 
        # Never generate a due time that is before the issue time
 
        due = max(issued, reservation_limit)
 

	
 
        invoice = rego.Invoice.objects.create(
 
            user=cart.user,
 
            cart=cart,
 
            cart_revision=cart.revision,
 
            value=Decimal()
 
            status=rego.Invoice.STATUS_UNPAID,
 
            value=Decimal(),
 
            issue_time=issued,
 
            due_time=due,
 
            recipient="BOB_THOMAS", # TODO: add recipient generating code
 
        )
 

	
 
        product_items = rego.ProductItem.objects.filter(cart=cart)
 

	
 
        if len(product_items) == 0:
 
            raise ValidationError("Your cart is empty.")
...
 
@@ -81,106 +98,134 @@ class InvoiceController(object):
 
            product = item.product
 
            line_item = rego.LineItem.objects.create(
 
                invoice=invoice,
 
                description="%s - %s" % (product.category.name, product.name),
 
                quantity=item.quantity,
 
                price=product.price,
 
                product=product,
 
            )
 
            invoice_value += line_item.quantity * line_item.price
 

	
 
        for item in discount_items:
 
            line_item = rego.LineItem.objects.create(
 
                invoice=invoice,
 
                description=item.discount.description,
 
                quantity=item.quantity,
 
                price=cls.resolve_discount_value(item) * -1,
 
                product=item.product,
 
            )
 
            invoice_value += line_item.quantity * line_item.price
 

	
 
        invoice.value = invoice_value
 

	
 
        if invoice.value == 0:
 
            invoice.paid = True
 

	
 
        invoice.save()
 

	
 
        return invoice
 

	
 
    def update_validity(self):
 
        ''' Updates the validity of this invoice if the cart it is attached to
 
        has updated. '''
 
        if self.invoice.cart is not None:
 
            if self.invoice.cart.revision != self.invoice.cart_revision:
 
                self.void()
 
    def total_payments(self):
 
        ''' Returns the total amount paid towards this invoice. '''
 

	
 
    def void(self):
 
        ''' Voids the invoice if it is valid to do so. '''
 
        if self.invoice.paid:
 
            raise ValidationError("Paid invoices cannot be voided, "
 
                                  "only refunded.")
 
        self.invoice.void = True
 
        self.invoice.save()
 
        payments = rego.PaymentBase.objects.filter(invoice=self.invoice)
 
        total_paid = payments.aggregate(Sum("amount"))["amount__sum"] or 0
 
        return total_paid
 

	
 
    @transaction.atomic
 
    def pay(self, reference, amount):
 
        ''' Pays the invoice by the given amount. If the payment
 
        equals the total on the invoice, finalise the invoice.
 
        (NB should be transactional.)
 
        '''
 
        if self.invoice.cart:
 
            cart = CartController(self.invoice.cart)
 
            cart.validate_cart()  # Raises ValidationError if invalid
 
    def update_status(self):
 
        ''' Updates the status of this invoice based upon the total
 
        payments.'''
 

	
 
        if self.invoice.void:
 
            raise ValidationError("Void invoices cannot be paid")
 
        old_status = self.invoice.status
 
        total_paid = self.total_payments()
 
        num_payments = rego.PaymentBase.objects.filter(
 
            invoice=self.invoice,
 
        ).count()
 
        remainder = self.invoice.value - total_paid
 

	
 
        if old_status == rego.Invoice.STATUS_UNPAID:
 
            # Invoice had an amount owing
 
            if remainder <= 0:
 
                # Invoice no longer has amount owing
 
                self._mark_paid()
 
            elif total_paid == 0 and num_payments > 0:
 
                # Invoice has multiple payments totalling zero
 
                self._mark_void()
 
        elif old_status == rego.Invoice.STATUS_PAID:
 
            if remainder > 0:
 
                # Invoice went from having a remainder of zero or less
 
                # to having a positive remainder -- must be a refund
 
                self._mark_refunded()
 
        elif old_status == rego.Invoice.STATUS_REFUNDED:
 
            # Should not ever change from here
 
            pass
 
        elif old_status == rego.Invoice.STATUS_VOID:
 
            # Should not ever change from here
 
            pass
 

	
 
    def _mark_paid(self):
 
        ''' Marks the invoice as paid, and updates the attached cart if
 
        necessary. '''
 
        cart = self.invoice.cart
 
        if cart:
 
            cart.active = False
 
            cart.save()
 
        self.invoice.status = rego.Invoice.STATUS_PAID
 
        self.invoice.save()
 

	
 
        if self.invoice.paid:
 
            raise ValidationError("Paid invoices cannot be paid again")
 
    def _mark_refunded(self):
 
        ''' Marks the invoice as refunded, and updates the attached cart if
 
        necessary. '''
 
        cart = self.invoice.cart
 
        if cart:
 
            cart.active = False
 
            cart.released = True
 
            cart.save()
 
        self.invoice.status = rego.Invoice.STATUS_REFUNDED
 
        self.invoice.save()
 

	
 
        ''' Adds a payment '''
 
        payment = rego.Payment.objects.create(
 
            invoice=self.invoice,
 
            reference=reference,
 
            amount=amount,
 
        )
 
        payment.save()
 
    def _mark_void(self):
 
        ''' Marks the invoice as refunded, and updates the attached cart if
 
        necessary. '''
 
        self.invoice.status = rego.Invoice.STATUS_VOID
 
        self.invoice.save()
 

	
 
        payments = rego.Payment.objects.filter(invoice=self.invoice)
 
        agg = payments.aggregate(Sum("amount"))
 
        total = agg["amount__sum"]
 
    def _invoice_matches_cart(self):
 
        ''' Returns true if there is no cart, or if the revision of this
 
        invoice matches the current revision of the cart. '''
 
        cart = self.invoice.cart
 
        if not cart:
 
            return True
 

	
 
        if total == self.invoice.value:
 
            self.invoice.paid = True
 
        return cart.revision == self.invoice.cart_revision
 

	
 
            if self.invoice.cart:
 
                cart = self.invoice.cart
 
                cart.active = False
 
                cart.save()
 
    def update_validity(self):
 
        ''' Voids this invoice if the cart it is attached to has updated. '''
 
        if not self._invoice_matches_cart():
 
            self.void()
 

	
 
            self.invoice.save()
 
    def void(self):
 
        ''' Voids the invoice if it is valid to do so. '''
 
        if self.invoice.status == rego.Invoice.STATUS_PAID:
 
            raise ValidationError("Paid invoices cannot be voided, "
 
                                  "only refunded.")
 
        self._mark_void()
 

	
 
    @transaction.atomic
 
    def refund(self, reference, amount):
 
        ''' Refunds the invoice by the given amount. The invoice is
 
        marked as unpaid, and the underlying cart is marked as released.
 
        ''' Refunds the invoice by the given amount.
 

	
 
        The invoice is marked as refunded, and the underlying cart is marked
 
        as released.
 

	
 
        TODO: replace with credit notes work instead.
 
        '''
 

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

	
 
        ''' Adds a payment '''
 
        payment = rego.Payment.objects.create(
 
        # Adds a payment
 
        # TODO: replace by creating a credit note instead
 
        rego.ManualPayment.objects.create(
 
            invoice=self.invoice,
 
            reference=reference,
 
            amount=0 - amount,
 
        )
 
        payment.save()
 

	
 
        self.invoice.paid = False
 
        self.invoice.void = True
 

	
 
        if self.invoice.cart:
 
            cart = self.invoice.cart
 
            cart.released = True
 
            cart.save()
 

	
 
        self.invoice.save()
 
        self.update_status()
registrasion/migrations/0013_auto_20160406_2228_squashed_0015_auto_20160406_1942.py
Show inline comments
 
new file 100644
 
# -*- coding: utf-8 -*-
 
# Generated by Django 1.9.2 on 2016-04-07 03:13
 
from __future__ import unicode_literals
 

	
 
from django.db import migrations, models
 
import django.db.models.deletion
 
import django.utils.timezone
 

	
 

	
 
class Migration(migrations.Migration):
 

	
 
    replaces = [('registrasion', '0013_auto_20160406_2228'), ('registrasion', '0014_auto_20160406_1847'), ('registrasion', '0015_auto_20160406_1942')]
 

	
 
    dependencies = [
 
        ('registrasion', '0012_auto_20160406_1212'),
 
    ]
 

	
 
    operations = [
 
        migrations.CreateModel(
 
            name='PaymentBase',
 
            fields=[
 
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 
                ('time', models.DateTimeField(default=django.utils.timezone.now)),
 
                ('reference', models.CharField(max_length=255)),
 
                ('amount', models.DecimalField(decimal_places=2, max_digits=8)),
 
            ],
 
        ),
 
        migrations.RemoveField(
 
            model_name='payment',
 
            name='invoice',
 
        ),
 
        migrations.RemoveField(
 
            model_name='invoice',
 
            name='paid',
 
        ),
 
        migrations.RemoveField(
 
            model_name='invoice',
 
            name='void',
 
        ),
 
        migrations.AddField(
 
            model_name='invoice',
 
            name='due_time',
 
            field=models.DateTimeField(default=django.utils.timezone.now),
 
            preserve_default=False,
 
        ),
 
        migrations.AddField(
 
            model_name='invoice',
 
            name='issue_time',
 
            field=models.DateTimeField(default=django.utils.timezone.now),
 
            preserve_default=False,
 
        ),
 
        migrations.AddField(
 
            model_name='invoice',
 
            name='recipient',
 
            field=models.CharField(default='Lol', max_length=1024),
 
            preserve_default=False,
 
        ),
 
        migrations.AddField(
 
            model_name='invoice',
 
            name='status',
 
            field=models.IntegerField(choices=[(1, 'Unpaid'), (2, 'Paid'), (3, 'Refunded'), (4, 'VOID')], db_index=True),
 
        ),
 
        migrations.AddField(
 
            model_name='lineitem',
 
            name='product',
 
            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='registrasion.Product'),
 
        ),
 
        migrations.CreateModel(
 
            name='ManualPayment',
 
            fields=[
 
                ('paymentbase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='registrasion.PaymentBase')),
 
            ],
 
            bases=('registrasion.paymentbase',),
 
        ),
 
        migrations.DeleteModel(
 
            name='Payment',
 
        ),
 
        migrations.AddField(
 
            model_name='paymentbase',
 
            name='invoice',
 
            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='registrasion.Invoice'),
 
        ),
 
        migrations.AlterField(
 
            model_name='invoice',
 
            name='cart_revision',
 
            field=models.IntegerField(db_index=True, null=True),
 
        ),
 
    ]
registrasion/models.py
Show inline comments
 
from __future__ import unicode_literals
 

	
 
import datetime
 
import itertools
 

	
 
from django.core.exceptions import ObjectDoesNotExist
 
from django.core.exceptions import ValidationError
 
from django.contrib.auth.models import User
 
from django.db import models
 
from django.db.models import F, Q
 
from django.utils import timezone
 
from django.utils.encoding import python_2_unicode_compatible
...
 
@@ -23,19 +24,16 @@ class Attendee(models.Model):
 
        return "%s" % self.user
 

	
 
    @staticmethod
 
    def get_instance(user):
 
        ''' Returns the instance of attendee for the given user, or creates
 
        a new one. '''
 
        attendees = Attendee.objects.filter(user=user)
 
        if len(attendees) > 0:
 
            return attendees[0]
 
        else:
 
            attendee = Attendee(user=user)
 
            attendee.save()
 
            return attendee
 
        try:
 
            return Attendee.objects.get(user=user)
 
        except ObjectDoesNotExist:
 
            return Attendee.objects.create(user=user)
 

	
 
    user = models.OneToOneField(User, on_delete=models.CASCADE)
 
    # Badge/profile is linked
 
    completed_registration = models.BooleanField(default=False)
 
    highest_complete_category = models.IntegerField(default=0)
 

	
...
 
@@ -51,12 +49,25 @@ class AttendeeProfileBase(models.Model):
 
    @classmethod
 
    def name_field(cls):
 
        ''' This is used to pre-fill the attendee's name from the
 
        speaker profile. If it's None, that functionality is disabled. '''
 
        return None
 

	
 
    def invoice_recipient(self):
 
        ''' Returns a representation of this attendee profile for the purpose
 
        of rendering to an invoice. Override in subclasses. '''
 

	
 
        # Manual dispatch to subclass. Fleh.
 
        slf = AttendeeProfileBase.objects.get_subclass(id=self.id)
 
        # Actually compare the functions.
 
        if type(slf).invoice_recipient != type(self).invoice_recipient:
 
            return type(slf).invoice_recipient(slf)
 

	
 
        # Return a default
 
        return slf.attendee.user.username
 

	
 
    attendee = models.OneToOneField(Attendee, on_delete=models.CASCADE)
 

	
 

	
 
# Inventory Models
 

	
 
@python_2_unicode_compatible
...
 
@@ -530,27 +541,63 @@ class DiscountItem(models.Model):
 

	
 
@python_2_unicode_compatible
 
class Invoice(models.Model):
 
    ''' An invoice. Invoices can be automatically generated when checking out
 
    a Cart, in which case, it is attached to a given revision of a Cart. '''
 

	
 
    STATUS_UNPAID = 1
 
    STATUS_PAID = 2
 
    STATUS_REFUNDED = 3
 
    STATUS_VOID = 4
 

	
 
    STATUS_TYPES = [
 
        (STATUS_UNPAID, _("Unpaid")),
 
        (STATUS_PAID, _("Paid")),
 
        (STATUS_REFUNDED, _("Refunded")),
 
        (STATUS_VOID, _("VOID")),
 
    ]
 

	
 
    def __str__(self):
 
        return "Invoice #%d" % self.id
 

	
 
    def clean(self):
 
        if self.cart is not None and self.cart_revision is None:
 
            raise ValidationError(
 
                "If this is a cart invoice, it must have a revision")
 

	
 
    @property
 
    def is_unpaid(self):
 
        return self.status == self.STATUS_UNPAID
 

	
 
    @property
 
    def is_void(self):
 
        return self.status == self.STATUS_VOID
 

	
 
    @property
 
    def is_paid(self):
 
        return self.status == self.STATUS_PAID
 

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

	
 
    # Invoice Number
 
    user = models.ForeignKey(User)
 
    cart = models.ForeignKey(Cart, null=True)
 
    cart_revision = models.IntegerField(null=True)
 
    cart_revision = models.IntegerField(
 
        null=True,
 
        db_index=True,
 
    )
 
    # Line Items (foreign key)
 
    void = models.BooleanField(default=False)
 
    paid = models.BooleanField(default=False)
 
    status = models.IntegerField(
 
        choices=STATUS_TYPES,
 
        db_index=True,
 
    )
 
    recipient = models.CharField(max_length=1024)
 
    issue_time = models.DateTimeField()
 
    due_time = models.DateTimeField()
 
    value = models.DecimalField(max_digits=8, decimal_places=2)
 

	
 

	
 
@python_2_unicode_compatible
 
class LineItem(models.Model):
 
    ''' Line items for an invoice. These are denormalised from the ProductItems
...
 
@@ -562,20 +609,28 @@ class LineItem(models.Model):
 
            self.description, self.quantity, self.price)
 

	
 
    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(Product, null=True, blank=True)
 

	
 

	
 
@python_2_unicode_compatible
 
class Payment(models.Model):
 
    ''' A payment for an invoice. Each invoice can have multiple payments
 
    attached to it.'''
 
class PaymentBase(models.Model):
 
    ''' The base payment type for invoices. Payment apps should subclass this
 
    class to handle implementation-specific issues. '''
 

	
 
    objects = InheritanceManager()
 

	
 
    def __str__(self):
 
        return "Payment: ref=%s amount=%s" % (self.reference, self.amount)
 

	
 
    invoice = models.ForeignKey(Invoice)
 
    time = models.DateTimeField(default=timezone.now)
 
    reference = models.CharField(max_length=64)
 
    reference = models.CharField(max_length=255)
 
    amount = models.DecimalField(max_digits=8, decimal_places=2)
 

	
 

	
 
class ManualPayment(PaymentBase):
 
    ''' Payments that are manually entered by staff. '''
 
    pass
registrasion/tests/controller_helpers.py
Show inline comments
 
from registrasion.controllers.cart import CartController
 
from registrasion.controllers.invoice import InvoiceController
 
from registrasion import models as rego
 

	
 
from django.core.exceptions import ObjectDoesNotExist
 
from django.core.exceptions import ValidationError
 

	
 

	
 
class TestingCartController(CartController):
 

	
 
    def set_quantity(self, product, quantity, batched=False):
 
        ''' Sets the _quantity_ of the given _product_ in the cart to the given
...
 
@@ -29,7 +30,30 @@ class TestingCartController(CartController):
 
    def next_cart(self):
 
        self.cart.active = False
 
        self.cart.save()
 

	
 

	
 
class TestingInvoiceController(InvoiceController):
 
    pass
 

	
 
    def pay(self, reference, amount):
 
        ''' Testing method for simulating an invoice paymenht by the given
 
        amount. '''
 
        if self.invoice.cart:
 
            cart = CartController(self.invoice.cart)
 
            cart.validate_cart()  # Raises ValidationError if invalid
 

	
 
        status = self.invoice.status
 
        if status == rego.Invoice.STATUS_VOID:
 
            raise ValidationError("Void invoices cannot be paid")
 
        elif status == rego.Invoice.STATUS_PAID:
 
            raise ValidationError("Paid invoices cannot be paid again")
 
        elif status == rego.Invoice.STATUS_REFUNDED:
 
            raise ValidationError("Refunded invoices cannot be paid")
 

	
 
        ''' Adds a payment '''
 
        payment = rego.ManualPayment.objects.create(
 
            invoice=self.invoice,
 
            reference=reference,
 
            amount=amount,
 
        )
 

	
 
        self.update_status()
registrasion/tests/test_invoice.py
Show inline comments
...
 
@@ -32,14 +32,14 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        invoice_2 = TestingInvoiceController.for_cart(current_cart.cart)
 
        self.assertNotEqual(invoice_1.invoice, invoice_2.invoice)
 

	
 
        # The old invoice should automatically be voided
 
        invoice_1_new = rego.Invoice.objects.get(pk=invoice_1.invoice.id)
 
        invoice_2_new = rego.Invoice.objects.get(pk=invoice_2.invoice.id)
 
        self.assertTrue(invoice_1_new.void)
 
        self.assertFalse(invoice_2_new.void)
 
        self.assertTrue(invoice_1_new.is_void)
 
        self.assertFalse(invoice_2_new.is_void)
 

	
 
        # Invoice should have two line items
 
        line_items = rego.LineItem.objects.filter(invoice=invoice_2.invoice)
 
        self.assertEqual(2, len(line_items))
 
        # Invoice should have a value equal to cost of PROD_1 and PROD_2
 
        self.assertEqual(
...
 
@@ -65,13 +65,13 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        current_cart.add_to_cart(self.PROD_1, 1)
 

	
 
        invoice = TestingInvoiceController.for_cart(current_cart.cart)
 
        invoice.pay("A payment!", invoice.invoice.value)
 

	
 
        # This payment is for the correct amount invoice should be paid.
 
        self.assertTrue(invoice.invoice.paid)
 
        self.assertTrue(invoice.invoice.is_paid)
 

	
 
        # Cart should not be active
 
        self.assertFalse(invoice.invoice.cart.active)
 

	
 
        # Asking for a cart should generate a new one
 
        new_cart = TestingCartController.for_user(self.USER_1)
...
 
@@ -130,44 +130,44 @@ class InvoiceTestCase(RegistrationCartTestCase):
 
        current_cart.apply_voucher(voucher.code)
 

	
 
        # Should be able to create an invoice after the product is added
 
        current_cart.add_to_cart(self.PROD_1, 1)
 
        invoice_1 = TestingInvoiceController.for_cart(current_cart.cart)
 

	
 
        self.assertTrue(invoice_1.invoice.paid)
 
        self.assertTrue(invoice_1.invoice.is_paid)
 

	
 
    def test_invoice_voids_self_if_cart_is_invalid(self):
 
        current_cart = TestingCartController.for_user(self.USER_1)
 

	
 
        # Should be able to create an invoice after the product is added
 
        current_cart.add_to_cart(self.PROD_1, 1)
 
        invoice_1 = TestingInvoiceController.for_cart(current_cart.cart)
 

	
 
        self.assertFalse(invoice_1.invoice.void)
 
        self.assertFalse(invoice_1.invoice.is_void)
 

	
 
        # Adding item to cart should produce a new invoice
 
        current_cart.add_to_cart(self.PROD_2, 1)
 
        invoice_2 = TestingInvoiceController.for_cart(current_cart.cart)
 
        self.assertNotEqual(invoice_1.invoice, invoice_2.invoice)
 

	
 
        # Viewing invoice_1's invoice should show it as void
 
        invoice_1_new = TestingInvoiceController(invoice_1.invoice)
 
        self.assertTrue(invoice_1_new.invoice.void)
 
        self.assertTrue(invoice_1_new.invoice.is_void)
 

	
 
        # Viewing invoice_2's invoice should *not* show it as void
 
        invoice_2_new = TestingInvoiceController(invoice_2.invoice)
 
        self.assertFalse(invoice_2_new.invoice.void)
 
        self.assertFalse(invoice_2_new.invoice.is_void)
 

	
 
    def test_voiding_invoice_creates_new_invoice(self):
 
        current_cart = TestingCartController.for_user(self.USER_1)
 

	
 
        # Should be able to create an invoice after the product is added
 
        current_cart.add_to_cart(self.PROD_1, 1)
 
        invoice_1 = TestingInvoiceController.for_cart(current_cart.cart)
 

	
 
        self.assertFalse(invoice_1.invoice.void)
 
        self.assertFalse(invoice_1.invoice.is_void)
 
        invoice_1.void()
 

	
 
        invoice_2 = TestingInvoiceController.for_cart(current_cart.cart)
 
        self.assertNotEqual(invoice_1.invoice, invoice_2.invoice)
 

	
 
    def test_cannot_pay_void_invoice(self):
registrasion/tests/test_refund.py
Show inline comments
...
 
@@ -15,14 +15,16 @@ class RefundTestCase(RegistrationCartTestCase):
 

	
 
        # Should be able to create an invoice after the product is added
 
        current_cart.add_to_cart(self.PROD_1, 1)
 
        invoice = TestingInvoiceController.for_cart(current_cart.cart)
 

	
 
        invoice.pay("A Payment!", invoice.invoice.value)
 
        self.assertFalse(invoice.invoice.void)
 
        self.assertTrue(invoice.invoice.paid)
 
        self.assertFalse(invoice.invoice.is_void)
 
        self.assertTrue(invoice.invoice.is_paid)
 
        self.assertFalse(invoice.invoice.is_refunded)
 
        self.assertFalse(invoice.invoice.cart.released)
 

	
 
        invoice.refund("A Refund!", invoice.invoice.value)
 
        self.assertTrue(invoice.invoice.void)
 
        self.assertFalse(invoice.invoice.paid)
 
        self.assertFalse(invoice.invoice.is_void)
 
        self.assertFalse(invoice.invoice.is_paid)
 
        self.assertTrue(invoice.invoice.is_refunded)
 
        self.assertTrue(invoice.invoice.cart.released)
registrasion/tests/test_voucher.py
Show inline comments
...
 
@@ -138,13 +138,13 @@ class VoucherTestCases(RegistrationCartTestCase):
 
        voucher = self.new_voucher(limit=2)
 
        current_cart = TestingCartController.for_user(self.USER_1)
 
        current_cart.apply_voucher(voucher.code)
 
        current_cart.add_to_cart(self.PROD_1, 1)
 

	
 
        inv = TestingInvoiceController.for_cart(current_cart.cart)
 
        if not inv.invoice.paid:
 
        if not inv.invoice.is_paid:
 
            inv.pay("Hello!", inv.invoice.value)
 

	
 
        current_cart = TestingCartController.for_user(self.USER_1)
 
        with self.assertRaises(ValidationError):
 
            current_cart.apply_voucher(voucher.code)
 

	
0 comments (0 inline, 0 general)