Makes invoice model, controller, and test changes to match issue #15 design doc
This commit is contained in:
		
							parent
							
								
									5633554854
								
							
						
					
					
						commit
						38cdb8aa63
					
				
					 7 changed files with 313 additions and 99 deletions
				
			
		|  | @ -3,6 +3,7 @@ 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 | ||||
| 
 | ||||
|  | @ -13,6 +14,7 @@ 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 | ||||
|  | @ -22,21 +24,26 @@ class InvoiceController(object): | |||
|         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): | ||||
|  | @ -60,11 +67,21 @@ class InvoiceController(object): | |||
|     @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) | ||||
|  | @ -84,6 +101,7 @@ class InvoiceController(object): | |||
|                 description="%s - %s" % (product.category.name, product.name), | ||||
|                 quantity=item.quantity, | ||||
|                 price=product.price, | ||||
|                 product=product, | ||||
|             ) | ||||
|             invoice_value += line_item.quantity * line_item.price | ||||
| 
 | ||||
|  | @ -93,94 +111,121 @@ class InvoiceController(object): | |||
|                 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 total_payments(self): | ||||
|         ''' Returns the total amount paid towards this invoice. ''' | ||||
| 
 | ||||
|         payments = rego.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() | ||||
|         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() | ||||
| 
 | ||||
|     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() | ||||
| 
 | ||||
|     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() | ||||
| 
 | ||||
|     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 | ||||
| 
 | ||||
|         return cart.revision == self.invoice.cart_revision | ||||
| 
 | ||||
|     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: | ||||
|         ''' Voids this invoice if the cart it is attached to has updated. ''' | ||||
|         if not self._invoice_matches_cart(): | ||||
|             self.void() | ||||
| 
 | ||||
|     def void(self): | ||||
|         ''' Voids the invoice if it is valid to do so. ''' | ||||
|         if self.invoice.paid: | ||||
|         if self.invoice.status == rego.Invoice.STATUS_PAID: | ||||
|             raise ValidationError("Paid invoices cannot be voided, " | ||||
|                                   "only refunded.") | ||||
|         self.invoice.void = True | ||||
|         self.invoice.save() | ||||
| 
 | ||||
|     @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 | ||||
| 
 | ||||
|         if self.invoice.void: | ||||
|             raise ValidationError("Void invoices cannot be paid") | ||||
| 
 | ||||
|         if self.invoice.paid: | ||||
|             raise ValidationError("Paid invoices cannot be paid again") | ||||
| 
 | ||||
|         ''' Adds a payment ''' | ||||
|         payment = rego.Payment.objects.create( | ||||
|             invoice=self.invoice, | ||||
|             reference=reference, | ||||
|             amount=amount, | ||||
|         ) | ||||
|         payment.save() | ||||
| 
 | ||||
|         payments = rego.Payment.objects.filter(invoice=self.invoice) | ||||
|         agg = payments.aggregate(Sum("amount")) | ||||
|         total = agg["amount__sum"] | ||||
| 
 | ||||
|         if total == self.invoice.value: | ||||
|             self.invoice.paid = True | ||||
| 
 | ||||
|             if self.invoice.cart: | ||||
|                 cart = self.invoice.cart | ||||
|                 cart.active = False | ||||
|                 cart.save() | ||||
| 
 | ||||
|             self.invoice.save() | ||||
|         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() | ||||
|  |  | |||
|  | @ -0,0 +1,88 @@ | |||
| # -*- 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), | ||||
|         ), | ||||
|     ] | ||||
|  | @ -3,6 +3,7 @@ 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 | ||||
|  | @ -26,13 +27,10 @@ class Attendee(models.Model): | |||
|     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 | ||||
|  | @ -54,6 +52,19 @@ class AttendeeProfileBase(models.Model): | |||
|         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) | ||||
| 
 | ||||
| 
 | ||||
|  | @ -533,6 +544,18 @@ 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 | ||||
| 
 | ||||
|  | @ -541,13 +564,37 @@ class Invoice(models.Model): | |||
|             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) | ||||
| 
 | ||||
| 
 | ||||
|  | @ -565,17 +612,25 @@ class LineItem(models.Model): | |||
|     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 | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ 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): | ||||
|  | @ -32,4 +33,27 @@ class TestingCartController(CartController): | |||
| 
 | ||||
| 
 | ||||
| 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() | ||||
|  |  | |||
|  | @ -35,8 +35,8 @@ class InvoiceTestCase(RegistrationCartTestCase): | |||
|         # 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) | ||||
|  | @ -68,7 +68,7 @@ class InvoiceTestCase(RegistrationCartTestCase): | |||
|         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) | ||||
|  | @ -133,7 +133,7 @@ class InvoiceTestCase(RegistrationCartTestCase): | |||
|         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) | ||||
|  | @ -142,7 +142,7 @@ class InvoiceTestCase(RegistrationCartTestCase): | |||
|         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) | ||||
|  | @ -151,11 +151,11 @@ class InvoiceTestCase(RegistrationCartTestCase): | |||
| 
 | ||||
|         # 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) | ||||
|  | @ -164,7 +164,7 @@ class InvoiceTestCase(RegistrationCartTestCase): | |||
|         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) | ||||
|  |  | |||
|  | @ -18,11 +18,13 @@ class RefundTestCase(RegistrationCartTestCase): | |||
|         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) | ||||
|  |  | |||
|  | @ -141,7 +141,7 @@ class VoucherTestCases(RegistrationCartTestCase): | |||
|         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) | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Christopher Neugebauer
						Christopher Neugebauer