commit
						eb5dd59036
					
				
					 6 changed files with 233 additions and 38 deletions
				
			
		|  | @ -120,14 +120,12 @@ class CartController(object): | ||||||
|         # Test each product limit here |         # Test each product limit here | ||||||
|         for product, quantity in product_quantities: |         for product, quantity in product_quantities: | ||||||
|             if quantity < 0: |             if quantity < 0: | ||||||
|                 # TODO: batch errors |  | ||||||
|                 errors.append((product, "Value must be zero or greater.")) |                 errors.append((product, "Value must be zero or greater.")) | ||||||
| 
 | 
 | ||||||
|             prod = ProductController(product) |             prod = ProductController(product) | ||||||
|             limit = prod.user_quantity_remaining(self.cart.user) |             limit = prod.user_quantity_remaining(self.cart.user) | ||||||
| 
 | 
 | ||||||
|             if quantity > limit: |             if quantity > limit: | ||||||
|                 # TODO: batch errors |  | ||||||
|                 errors.append(( |                 errors.append(( | ||||||
|                     product, |                     product, | ||||||
|                     "You may only have %d of product: %s" % ( |                     "You may only have %d of product: %s" % ( | ||||||
|  | @ -149,7 +147,6 @@ class CartController(object): | ||||||
|             to_add = sum(i[1] for i in by_cat[category]) |             to_add = sum(i[1] for i in by_cat[category]) | ||||||
| 
 | 
 | ||||||
|             if to_add > limit: |             if to_add > limit: | ||||||
|                 # TODO: batch errors |  | ||||||
|                 errors.append(( |                 errors.append(( | ||||||
|                     category, |                     category, | ||||||
|                     "You may only have %d items in category: %s" % ( |                     "You may only have %d items in category: %s" % ( | ||||||
|  | @ -164,10 +161,8 @@ class CartController(object): | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         if errs: |         if errs: | ||||||
|             # TODO: batch errors |             for error in errs: | ||||||
|             errors.append( |                 errors.append(error) | ||||||
|                 ("enabling_conditions", "An enabling condition failed") |  | ||||||
|             ) |  | ||||||
| 
 | 
 | ||||||
|         if errors: |         if errors: | ||||||
|             raise CartValidationError(errors) |             raise CartValidationError(errors) | ||||||
|  | @ -175,43 +170,79 @@ class CartController(object): | ||||||
|     def apply_voucher(self, voucher_code): |     def apply_voucher(self, voucher_code): | ||||||
|         ''' Applies the voucher with the given code to this cart. ''' |         ''' Applies the voucher with the given code to this cart. ''' | ||||||
| 
 | 
 | ||||||
|         # Is voucher exhausted? |  | ||||||
|         active_carts = rego.Cart.reserved_carts() |  | ||||||
| 
 |  | ||||||
|         # Try and find the voucher |         # Try and find the voucher | ||||||
|         voucher = rego.Voucher.objects.get(code=voucher_code.upper()) |         voucher = rego.Voucher.objects.get(code=voucher_code.upper()) | ||||||
| 
 | 
 | ||||||
|         # It's invalid for a user to enter a voucher that's exhausted |         # Re-applying vouchers should be idempotent | ||||||
|         carts_with_voucher = active_carts.filter(vouchers=voucher) |         if voucher in self.cart.vouchers.all(): | ||||||
|         if len(carts_with_voucher) >= voucher.limit: |             return | ||||||
|             raise ValidationError("This voucher is no longer available") |  | ||||||
| 
 | 
 | ||||||
|         # It's not valid for users to re-enter a voucher they already have |         self._test_voucher(voucher) | ||||||
|         user_carts_with_voucher = rego.Cart.objects.filter( |  | ||||||
|             user=self.cart.user, |  | ||||||
|             released=False, |  | ||||||
|             vouchers=voucher, |  | ||||||
|         ) |  | ||||||
|         if len(user_carts_with_voucher) > 0: |  | ||||||
|             raise ValidationError("You have already entered this voucher.") |  | ||||||
| 
 | 
 | ||||||
|         # If successful... |         # If successful... | ||||||
|         self.cart.vouchers.add(voucher) |         self.cart.vouchers.add(voucher) | ||||||
|         self.end_batch() |         self.end_batch() | ||||||
| 
 | 
 | ||||||
|  |     def _test_voucher(self, voucher): | ||||||
|  |         ''' Tests whether this voucher is allowed to be applied to this cart. | ||||||
|  |         Raises ValidationError if not. ''' | ||||||
|  | 
 | ||||||
|  |         # Is voucher exhausted? | ||||||
|  |         active_carts = rego.Cart.reserved_carts() | ||||||
|  | 
 | ||||||
|  |         # It's invalid for a user to enter a voucher that's exhausted | ||||||
|  |         carts_with_voucher = active_carts.filter(vouchers=voucher) | ||||||
|  |         carts_with_voucher = carts_with_voucher.exclude(pk=self.cart.id) | ||||||
|  |         if carts_with_voucher.count() >= voucher.limit: | ||||||
|  |             raise ValidationError("Voucher %s is no longer available" % voucher.code) | ||||||
|  | 
 | ||||||
|  |         # It's not valid for users to re-enter a voucher they already have | ||||||
|  |         user_carts_with_voucher = carts_with_voucher.filter( | ||||||
|  |             user=self.cart.user, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         if user_carts_with_voucher.count() > 0: | ||||||
|  |             raise ValidationError("You have already entered this voucher.") | ||||||
|  | 
 | ||||||
|  |     def _test_vouchers(self, vouchers): | ||||||
|  |         ''' Tests each of the vouchers against self._test_voucher() and raises | ||||||
|  |         the collective ValidationError. | ||||||
|  |         Future work will refactor _test_voucher in terms of this, and save some | ||||||
|  |         queries. ''' | ||||||
|  |         errors = [] | ||||||
|  |         for voucher in vouchers: | ||||||
|  |             try: | ||||||
|  |                 self._test_voucher(voucher) | ||||||
|  |             except ValidationError as ve: | ||||||
|  |                 errors.append(ve) | ||||||
|  | 
 | ||||||
|  |         if errors: | ||||||
|  |             raise(ValidationError(ve)) | ||||||
|  | 
 | ||||||
|     def validate_cart(self): |     def validate_cart(self): | ||||||
|         ''' Determines whether the status of the current cart is valid; |         ''' Determines whether the status of the current cart is valid; | ||||||
|         this is normally called before generating or paying an invoice ''' |         this is normally called before generating or paying an invoice ''' | ||||||
| 
 | 
 | ||||||
|         # TODO: validate vouchers |         cart = self.cart | ||||||
|  |         user = self.cart.user | ||||||
|  |         errors = [] | ||||||
| 
 | 
 | ||||||
|         items = rego.ProductItem.objects.filter(cart=self.cart) |         try: | ||||||
|  |             self._test_vouchers(self.cart.vouchers.all()) | ||||||
|  |         except ValidationError as ve: | ||||||
|  |             errors.append(ve) | ||||||
|  | 
 | ||||||
|  |         items = rego.ProductItem.objects.filter(cart=cart) | ||||||
| 
 | 
 | ||||||
|         product_quantities = list((i.product, i.quantity) for i in items) |         product_quantities = list((i.product, i.quantity) for i in items) | ||||||
|         self._test_limits(product_quantities) |         try: | ||||||
|  |             self._test_limits(product_quantities) | ||||||
|  |         except ValidationError as ve: | ||||||
|  |             for error in ve.error_list: | ||||||
|  |                 errors.append(error.message[1]) | ||||||
| 
 | 
 | ||||||
|         # Validate the discounts |         # Validate the discounts | ||||||
|         discount_items = rego.DiscountItem.objects.filter(cart=self.cart) |         discount_items = rego.DiscountItem.objects.filter(cart=cart) | ||||||
|         seen_discounts = set() |         seen_discounts = set() | ||||||
| 
 | 
 | ||||||
|         for discount_item in discount_items: |         for discount_item in discount_items: | ||||||
|  | @ -223,12 +254,49 @@ class CartController(object): | ||||||
|                 pk=discount.pk) |                 pk=discount.pk) | ||||||
|             cond = ConditionController.for_condition(real_discount) |             cond = ConditionController.for_condition(real_discount) | ||||||
| 
 | 
 | ||||||
|             if not cond.is_met(self.cart.user): |             if not cond.is_met(user): | ||||||
|                 raise ValidationError("Discounts are no longer available") |                 errors.append( | ||||||
|  |                     ValidationError("Discounts are no longer available") | ||||||
|  |                 ) | ||||||
| 
 | 
 | ||||||
|  |         if errors: | ||||||
|  |             raise ValidationError(errors) | ||||||
|  | 
 | ||||||
|  |     @transaction.atomic | ||||||
|  |     def fix_simple_errors(self): | ||||||
|  |         ''' This attempts to fix the easy errors raised by ValidationError. | ||||||
|  |         This includes removing items from the cart that are no longer | ||||||
|  |         available, recalculating all of the discounts, and removing voucher | ||||||
|  |         codes that are no longer available. ''' | ||||||
|  | 
 | ||||||
|  |         # Fix vouchers first (this affects available discounts) | ||||||
|  |         active_carts = rego.Cart.reserved_carts() | ||||||
|  |         to_remove = [] | ||||||
|  |         for voucher in self.cart.vouchers.all(): | ||||||
|  |             try: | ||||||
|  |                 self._test_voucher(voucher) | ||||||
|  |             except ValidationError as ve: | ||||||
|  |                 to_remove.append(voucher) | ||||||
|  | 
 | ||||||
|  |         for voucher in to_remove: | ||||||
|  |             self.cart.vouchers.remove(voucher) | ||||||
|  | 
 | ||||||
|  |         # Fix products and discounts | ||||||
|  |         items = rego.ProductItem.objects.filter(cart=self.cart) | ||||||
|  |         products = set(i.product for i in items) | ||||||
|  |         available = set(ProductController.available_products( | ||||||
|  |             self.cart.user, | ||||||
|  |             products=products, | ||||||
|  |         )) | ||||||
|  | 
 | ||||||
|  |         not_available = products - available | ||||||
|  |         zeros = [(product, 0) for product in not_available] | ||||||
|  | 
 | ||||||
|  |         self.set_quantities(zeros) | ||||||
|  | 
 | ||||||
|  |     @transaction.atomic | ||||||
|     def recalculate_discounts(self): |     def recalculate_discounts(self): | ||||||
|         ''' Calculates all of the discounts available for this product. |         ''' Calculates all of the discounts available for this product. | ||||||
|         NB should be transactional, and it's terribly inefficient. |  | ||||||
|         ''' |         ''' | ||||||
| 
 | 
 | ||||||
|         # Delete the existing entries. |         # Delete the existing entries. | ||||||
|  |  | ||||||
|  | @ -44,6 +44,26 @@ class ConditionController(object): | ||||||
|         except KeyError: |         except KeyError: | ||||||
|             return ConditionController() |             return ConditionController() | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  |     SINGLE = True | ||||||
|  |     PLURAL = False | ||||||
|  |     NONE = True | ||||||
|  |     SOME = False | ||||||
|  |     MESSAGE = { | ||||||
|  |         NONE: { | ||||||
|  |             SINGLE: | ||||||
|  |                 "%(items)s is no longer available to you", | ||||||
|  |             PLURAL: | ||||||
|  |                 "%(items)s are no longer available to you", | ||||||
|  |         }, | ||||||
|  |         SOME: { | ||||||
|  |             SINGLE: | ||||||
|  |                 "Only %(remainder)d of the following item remains: %(items)s", | ||||||
|  |             PLURAL: | ||||||
|  |                 "Only %(remainder)d of the following items remain: %(items)s" | ||||||
|  |         }, | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @classmethod |     @classmethod | ||||||
|     def test_enabling_conditions( |     def test_enabling_conditions( | ||||||
|             cls, user, products=None, product_quantities=None): |             cls, user, products=None, product_quantities=None): | ||||||
|  | @ -83,6 +103,8 @@ class ConditionController(object): | ||||||
|         # if there are no mandatory conditions |         # if there are no mandatory conditions | ||||||
|         non_mandatory = defaultdict(lambda: False) |         non_mandatory = defaultdict(lambda: False) | ||||||
| 
 | 
 | ||||||
|  |         messages = {} | ||||||
|  | 
 | ||||||
|         for condition in all_conditions: |         for condition in all_conditions: | ||||||
|             cond = cls.for_condition(condition) |             cond = cls.for_condition(condition) | ||||||
|             remainder = cond.user_quantity_remaining(user) |             remainder = cond.user_quantity_remaining(user) | ||||||
|  | @ -104,12 +126,21 @@ class ConditionController(object): | ||||||
|                 consumed = 1 |                 consumed = 1 | ||||||
|             met = consumed <= remainder |             met = consumed <= remainder | ||||||
| 
 | 
 | ||||||
|  |             if not met: | ||||||
|  |                 items = ", ".join(str(product) for product in all_products) | ||||||
|  |                 base = cls.MESSAGE[remainder == 0][len(all_products) == 1] | ||||||
|  |                 message = base % {"items": items, "remainder": remainder} | ||||||
|  | 
 | ||||||
|             for product in all_products: |             for product in all_products: | ||||||
|                 if condition.mandatory: |                 if condition.mandatory: | ||||||
|                     mandatory[product] &= met |                     mandatory[product] &= met | ||||||
|                 else: |                 else: | ||||||
|                     non_mandatory[product] |= met |                     non_mandatory[product] |= met | ||||||
| 
 | 
 | ||||||
|  |                 if not met and product not in messages: | ||||||
|  |                     messages[product] = message | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|         valid = defaultdict(lambda: True) |         valid = defaultdict(lambda: True) | ||||||
|         for product in itertools.chain(mandatory, non_mandatory): |         for product in itertools.chain(mandatory, non_mandatory): | ||||||
|             if product in mandatory: |             if product in mandatory: | ||||||
|  | @ -119,7 +150,11 @@ class ConditionController(object): | ||||||
|                 # Otherwise, we need just one non-mandatory condition met |                 # Otherwise, we need just one non-mandatory condition met | ||||||
|                 valid[product] = non_mandatory[product] |                 valid[product] = non_mandatory[product] | ||||||
| 
 | 
 | ||||||
|         error_fields = [product for product in valid if not valid[product]] |         error_fields = [ | ||||||
|  |             (product, messages[product]) | ||||||
|  |             for product in valid if not valid[product] | ||||||
|  |         ] | ||||||
|  | 
 | ||||||
|         return error_fields |         return error_fields | ||||||
| 
 | 
 | ||||||
|     def user_quantity_remaining(self, user): |     def user_quantity_remaining(self, user): | ||||||
|  |  | ||||||
|  | @ -38,9 +38,10 @@ class ProductController(object): | ||||||
|             if cls(product).user_quantity_remaining(user) > 0 |             if cls(product).user_quantity_remaining(user) > 0 | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         failed_conditions = set(ConditionController.test_enabling_conditions( |         failed_and_messages = ConditionController.test_enabling_conditions( | ||||||
|             user, products=passed_limits |             user, products=passed_limits | ||||||
|         )) |         ) | ||||||
|  |         failed_conditions = set(i[0] for i in failed_and_messages) | ||||||
| 
 | 
 | ||||||
|         out = list(passed_limits - failed_conditions) |         out = list(passed_limits - failed_conditions) | ||||||
|         out.sort(key=lambda product: product.order) |         out.sort(key=lambda product: product.order) | ||||||
|  |  | ||||||
|  | @ -293,3 +293,48 @@ class EnablingConditionTestCases(RegistrationCartTestCase): | ||||||
| 
 | 
 | ||||||
|         self.assertTrue(self.CAT_1 in cats) |         self.assertTrue(self.CAT_1 in cats) | ||||||
|         self.assertTrue(self.CAT_2 in cats) |         self.assertTrue(self.CAT_2 in cats) | ||||||
|  | 
 | ||||||
|  |     def test_validate_cart_when_enabling_conditions_become_unmet(self): | ||||||
|  |         self.add_product_enabling_condition(mandatory=False) | ||||||
|  | 
 | ||||||
|  |         cart = TestingCartController.for_user(self.USER_1) | ||||||
|  |         cart.add_to_cart(self.PROD_2, 1) | ||||||
|  |         cart.add_to_cart(self.PROD_1, 1) | ||||||
|  | 
 | ||||||
|  |         # Should pass | ||||||
|  |         cart.validate_cart() | ||||||
|  | 
 | ||||||
|  |         cart.set_quantity(self.PROD_2, 0) | ||||||
|  | 
 | ||||||
|  |         # Should fail | ||||||
|  |         with self.assertRaises(ValidationError): | ||||||
|  |             cart.validate_cart() | ||||||
|  | 
 | ||||||
|  |     def test_fix_simple_errors_resolves_unavailable_products(self): | ||||||
|  |         self.test_validate_cart_when_enabling_conditions_become_unmet() | ||||||
|  |         cart = TestingCartController.for_user(self.USER_1) | ||||||
|  | 
 | ||||||
|  |         # Should just remove all of the unavailable products | ||||||
|  |         cart.fix_simple_errors() | ||||||
|  |         # Should now succeed | ||||||
|  |         cart.validate_cart() | ||||||
|  | 
 | ||||||
|  |         # Should keep PROD_2 in the cart | ||||||
|  |         items = rego.ProductItem.objects.filter(cart=cart.cart) | ||||||
|  |         self.assertFalse([i for i in items if i.product == self.PROD_1]) | ||||||
|  | 
 | ||||||
|  |     def test_fix_simple_errors_does_not_remove_limited_items(self): | ||||||
|  |         cart = TestingCartController.for_user(self.USER_1) | ||||||
|  | 
 | ||||||
|  |         cart.add_to_cart(self.PROD_2, 1) | ||||||
|  |         cart.add_to_cart(self.PROD_1, 10) | ||||||
|  | 
 | ||||||
|  |         # Should just remove all of the unavailable products | ||||||
|  |         cart.fix_simple_errors() | ||||||
|  |         # Should now succeed | ||||||
|  |         cart.validate_cart() | ||||||
|  | 
 | ||||||
|  |         # Should keep PROD_2 in the cart | ||||||
|  |         # and also PROD_1, which is now exhausted for user. | ||||||
|  |         items = rego.ProductItem.objects.filter(cart=cart.cart) | ||||||
|  |         self.assertTrue([i for i in items if i.product == self.PROD_1]) | ||||||
|  |  | ||||||
|  | @ -37,11 +37,23 @@ class VoucherTestCases(RegistrationCartTestCase): | ||||||
|         cart_2.cart.active = False |         cart_2.cart.active = False | ||||||
|         cart_2.cart.save() |         cart_2.cart.save() | ||||||
| 
 | 
 | ||||||
|         # After the reservation duration, user 1 should not be able to apply |         # After the reservation duration, even though the voucher has applied, | ||||||
|         # voucher, as user 2 has paid for their cart. |         # it exceeds the number of vouchers available. | ||||||
|         self.add_timedelta(rego.Voucher.RESERVATION_DURATION * 2) |         self.add_timedelta(rego.Voucher.RESERVATION_DURATION * 2) | ||||||
|         with self.assertRaises(ValidationError): |         with self.assertRaises(ValidationError): | ||||||
|             cart_1.apply_voucher(voucher.code) |             cart_1.validate_cart() | ||||||
|  | 
 | ||||||
|  |     def test_fix_simple_errors_resolves_unavailable_voucher(self): | ||||||
|  |         self.test_apply_voucher() | ||||||
|  | 
 | ||||||
|  |         # User has an exhausted voucher leftover from test_apply_voucher | ||||||
|  |         cart_1 = TestingCartController.for_user(self.USER_1) | ||||||
|  |         with self.assertRaises(ValidationError): | ||||||
|  |             cart_1.validate_cart() | ||||||
|  | 
 | ||||||
|  |         cart_1.fix_simple_errors() | ||||||
|  |         # This should work now. | ||||||
|  |         cart_1.validate_cart() | ||||||
| 
 | 
 | ||||||
|     def test_voucher_enables_item(self): |     def test_voucher_enables_item(self): | ||||||
|         voucher = self.new_voucher() |         voucher = self.new_voucher() | ||||||
|  | @ -103,8 +115,10 @@ class VoucherTestCases(RegistrationCartTestCase): | ||||||
|         voucher = self.new_voucher(limit=2) |         voucher = self.new_voucher(limit=2) | ||||||
|         current_cart = TestingCartController.for_user(self.USER_1) |         current_cart = TestingCartController.for_user(self.USER_1) | ||||||
|         current_cart.apply_voucher(voucher.code) |         current_cart.apply_voucher(voucher.code) | ||||||
|         with self.assertRaises(ValidationError): |         current_cart.apply_voucher(voucher.code) | ||||||
|             current_cart.apply_voucher(voucher.code) | 
 | ||||||
|  |         # You can apply the code twice, but it will only add to the cart once. | ||||||
|  |         self.assertEqual(1, current_cart.cart.vouchers.count()) | ||||||
| 
 | 
 | ||||||
|     def test_voucher_can_only_be_applied_once_across_multiple_carts(self): |     def test_voucher_can_only_be_applied_once_across_multiple_carts(self): | ||||||
|         voucher = self.new_voucher(limit=2) |         voucher = self.new_voucher(limit=2) | ||||||
|  | @ -114,6 +128,8 @@ class VoucherTestCases(RegistrationCartTestCase): | ||||||
|         inv = InvoiceController.for_cart(current_cart.cart) |         inv = InvoiceController.for_cart(current_cart.cart) | ||||||
|         inv.pay("Hello!", inv.invoice.value) |         inv.pay("Hello!", inv.invoice.value) | ||||||
| 
 | 
 | ||||||
|  |         current_cart = TestingCartController.for_user(self.USER_1) | ||||||
|  | 
 | ||||||
|         with self.assertRaises(ValidationError): |         with self.assertRaises(ValidationError): | ||||||
|             current_cart.apply_voucher(voucher.code) |             current_cart.apply_voucher(voucher.code) | ||||||
| 
 | 
 | ||||||
|  | @ -133,3 +149,11 @@ class VoucherTestCases(RegistrationCartTestCase): | ||||||
| 
 | 
 | ||||||
|         inv.refund("Hello!", inv.invoice.value) |         inv.refund("Hello!", inv.invoice.value) | ||||||
|         current_cart.apply_voucher(voucher.code) |         current_cart.apply_voucher(voucher.code) | ||||||
|  | 
 | ||||||
|  |     def test_fix_simple_errors_does_not_remove_limited_voucher(self): | ||||||
|  |         voucher = self.new_voucher(code="VOUCHER") | ||||||
|  |         current_cart = TestingCartController.for_user(self.USER_1) | ||||||
|  |         current_cart.apply_voucher(voucher.code) | ||||||
|  | 
 | ||||||
|  |         current_cart.fix_simple_errors() | ||||||
|  |         self.assertEqual(1, current_cart.cart.vouchers.count()) | ||||||
|  |  | ||||||
|  | @ -337,6 +337,8 @@ def set_quantities_from_products_form(products_form, current_cart): | ||||||
|             product, message = ve_field.message |             product, message = ve_field.message | ||||||
|             if product in field_names: |             if product in field_names: | ||||||
|                 field = field_names[product] |                 field = field_names[product] | ||||||
|  |             elif isinstance(product, rego.Product): | ||||||
|  |                 continue | ||||||
|             else: |             else: | ||||||
|                 field = None |                 field = None | ||||||
|             products_form.add_error(field, message) |             products_form.add_error(field, message) | ||||||
|  | @ -377,10 +379,30 @@ def checkout(request): | ||||||
|     invoice. ''' |     invoice. ''' | ||||||
| 
 | 
 | ||||||
|     current_cart = CartController.for_user(request.user) |     current_cart = CartController.for_user(request.user) | ||||||
|     current_invoice = InvoiceController.for_cart(current_cart.cart) | 
 | ||||||
|  |     if "fix_errors" in request.GET and request.GET["fix_errors"] == "true": | ||||||
|  |         current_cart.fix_simple_errors() | ||||||
|  | 
 | ||||||
|  |     try: | ||||||
|  |         current_invoice = InvoiceController.for_cart(current_cart.cart) | ||||||
|  |     except ValidationError as ve: | ||||||
|  |         return checkout_errors(request, ve) | ||||||
| 
 | 
 | ||||||
|     return redirect("invoice", current_invoice.invoice.id) |     return redirect("invoice", current_invoice.invoice.id) | ||||||
| 
 | 
 | ||||||
|  | def checkout_errors(request, errors): | ||||||
|  | 
 | ||||||
|  |     error_list = [] | ||||||
|  |     for error in errors.error_list: | ||||||
|  |         if isinstance(error, tuple): | ||||||
|  |             error = error[1] | ||||||
|  |         error_list.append(error) | ||||||
|  | 
 | ||||||
|  |     data = { | ||||||
|  |         "error_list": error_list, | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return render(request, "registrasion/checkout_errors.html", data) | ||||||
| 
 | 
 | ||||||
| @login_required | @login_required | ||||||
| def invoice(request, invoice_id): | def invoice(request, invoice_id): | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Christopher Neugebauer
						Christopher Neugebauer