Merge branch 'cart_status_overhaul'
This commit is contained in:
commit
c135c77d6c
19 changed files with 158 additions and 85 deletions
|
@ -30,14 +30,16 @@ class CartController(object):
|
||||||
if there isn't one ready yet. '''
|
if there isn't one ready yet. '''
|
||||||
|
|
||||||
try:
|
try:
|
||||||
existing = commerce.Cart.objects.get(user=user, active=True)
|
existing = commerce.Cart.objects.get(
|
||||||
|
user=user,
|
||||||
|
status=commerce.Cart.STATUS_ACTIVE,
|
||||||
|
)
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
existing = commerce.Cart.objects.create(
|
existing = commerce.Cart.objects.create(
|
||||||
user=user,
|
user=user,
|
||||||
time_last_updated=timezone.now(),
|
time_last_updated=timezone.now(),
|
||||||
reservation_duration=datetime.timedelta(),
|
reservation_duration=datetime.timedelta(),
|
||||||
)
|
)
|
||||||
existing.save()
|
|
||||||
return cls(existing)
|
return cls(existing)
|
||||||
|
|
||||||
def extend_reservation(self):
|
def extend_reservation(self):
|
||||||
|
@ -367,11 +369,10 @@ class CartController(object):
|
||||||
allowed = candidate.quantity
|
allowed = candidate.quantity
|
||||||
if ours > allowed:
|
if ours > allowed:
|
||||||
discount_item.quantity = allowed
|
discount_item.quantity = allowed
|
||||||
|
discount_item.save()
|
||||||
# Update the remaining quantity.
|
# Update the remaining quantity.
|
||||||
quantity = ours - allowed
|
quantity = ours - allowed
|
||||||
else:
|
else:
|
||||||
quantity = 0
|
quantity = 0
|
||||||
|
|
||||||
candidate.quantity -= discount_item.quantity
|
candidate.quantity -= discount_item.quantity
|
||||||
|
|
||||||
discount_item.save()
|
|
||||||
|
|
|
@ -46,8 +46,7 @@ class CategoryController(object):
|
||||||
|
|
||||||
carts = commerce.Cart.objects.filter(
|
carts = commerce.Cart.objects.filter(
|
||||||
user=user,
|
user=user,
|
||||||
active=False,
|
status=commerce.Cart.STATUS_PAID,
|
||||||
released=False,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
items = commerce.ProductItem.objects.filter(
|
items = commerce.ProductItem.objects.filter(
|
||||||
|
|
|
@ -201,7 +201,8 @@ class CategoryConditionController(ConditionController):
|
||||||
''' returns True if the user has a product from a category that invokes
|
''' returns True if the user has a product from a category that invokes
|
||||||
this condition in one of their carts '''
|
this condition in one of their carts '''
|
||||||
|
|
||||||
carts = commerce.Cart.objects.filter(user=user, released=False)
|
carts = commerce.Cart.objects.filter(user=user)
|
||||||
|
carts = carts.exclude(status=commerce.Cart.STATUS_RELEASED)
|
||||||
enabling_products = inventory.Product.objects.filter(
|
enabling_products = inventory.Product.objects.filter(
|
||||||
category=self.condition.enabling_category,
|
category=self.condition.enabling_category,
|
||||||
)
|
)
|
||||||
|
@ -223,7 +224,8 @@ class ProductConditionController(ConditionController):
|
||||||
''' returns True if the user has a product that invokes this
|
''' returns True if the user has a product that invokes this
|
||||||
condition in one of their carts '''
|
condition in one of their carts '''
|
||||||
|
|
||||||
carts = commerce.Cart.objects.filter(user=user, released=False)
|
carts = commerce.Cart.objects.filter(user=user)
|
||||||
|
carts = carts.exclude(status=commerce.Cart.STATUS_RELEASED)
|
||||||
products_count = commerce.ProductItem.objects.filter(
|
products_count = commerce.ProductItem.objects.filter(
|
||||||
cart__in=carts,
|
cart__in=carts,
|
||||||
product__in=self.condition.enabling_products.all(),
|
product__in=self.condition.enabling_products.all(),
|
||||||
|
@ -272,7 +274,7 @@ class TimeOrStockLimitConditionController(ConditionController):
|
||||||
reserved_carts = commerce.Cart.reserved_carts()
|
reserved_carts = commerce.Cart.reserved_carts()
|
||||||
reserved_carts = reserved_carts.exclude(
|
reserved_carts = reserved_carts.exclude(
|
||||||
user=user,
|
user=user,
|
||||||
active=True,
|
status=commerce.Cart.STATUS_ACTIVE,
|
||||||
)
|
)
|
||||||
|
|
||||||
items = self._items()
|
items = self._items()
|
||||||
|
|
|
@ -71,8 +71,7 @@ def available_discounts(user, categories, products):
|
||||||
# is not available any more.
|
# is not available any more.
|
||||||
past_uses = commerce.DiscountItem.objects.filter(
|
past_uses = commerce.DiscountItem.objects.filter(
|
||||||
cart__user=user,
|
cart__user=user,
|
||||||
cart__active=False, # Only past carts count
|
cart__status=commerce.Cart.STATUS_PAID, # Only past carts count
|
||||||
cart__released=False, # You can reuse refunded discounts
|
|
||||||
discount=real_discount,
|
discount=real_discount,
|
||||||
)
|
)
|
||||||
agg = past_uses.aggregate(Sum("quantity"))
|
agg = past_uses.aggregate(Sum("quantity"))
|
||||||
|
|
|
@ -227,7 +227,7 @@ class InvoiceController(ForId, object):
|
||||||
necessary. '''
|
necessary. '''
|
||||||
cart = self.invoice.cart
|
cart = self.invoice.cart
|
||||||
if cart:
|
if cart:
|
||||||
cart.active = False
|
cart.status = commerce.Cart.STATUS_PAID
|
||||||
cart.save()
|
cart.save()
|
||||||
self.invoice.status = commerce.Invoice.STATUS_PAID
|
self.invoice.status = commerce.Invoice.STATUS_PAID
|
||||||
self.invoice.save()
|
self.invoice.save()
|
||||||
|
@ -237,8 +237,7 @@ class InvoiceController(ForId, object):
|
||||||
necessary. '''
|
necessary. '''
|
||||||
cart = self.invoice.cart
|
cart = self.invoice.cart
|
||||||
if cart:
|
if cart:
|
||||||
cart.active = False
|
cart.status = commerce.Cart.STATUS_RELEASED
|
||||||
cart.released = True
|
|
||||||
cart.save()
|
cart.save()
|
||||||
self.invoice.status = commerce.Invoice.STATUS_REFUNDED
|
self.invoice.status = commerce.Invoice.STATUS_REFUNDED
|
||||||
self.invoice.save()
|
self.invoice.save()
|
||||||
|
|
|
@ -68,8 +68,7 @@ class ProductController(object):
|
||||||
|
|
||||||
carts = commerce.Cart.objects.filter(
|
carts = commerce.Cart.objects.filter(
|
||||||
user=user,
|
user=user,
|
||||||
active=False,
|
status=commerce.Cart.STATUS_PAID,
|
||||||
released=False,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
items = commerce.ProductItem.objects.filter(
|
items = commerce.ProductItem.objects.filter(
|
||||||
|
|
19
registrasion/migrations/0023_auto_20160425_0409.py
Normal file
19
registrasion/migrations/0023_auto_20160425_0409.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.2 on 2016-04-25 04:09
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('registrasion', '0022_merge'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterIndexTogether(
|
||||||
|
name='cart',
|
||||||
|
index_together=set([]),
|
||||||
|
),
|
||||||
|
]
|
33
registrasion/migrations/0024_auto_20160425_0410.py
Normal file
33
registrasion/migrations/0024_auto_20160425_0410.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.2 on 2016-04-25 04:10
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('registrasion', '0023_auto_20160425_0409'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='cart',
|
||||||
|
name='status',
|
||||||
|
field=models.IntegerField(choices=[(1, 'Active'), (2, 'Paid'), (3, 'Released')], db_index=True, default=1),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='cart',
|
||||||
|
name='active',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='cart',
|
||||||
|
name='released',
|
||||||
|
),
|
||||||
|
migrations.AlterIndexTogether(
|
||||||
|
name='cart',
|
||||||
|
index_together=set([('status', 'user'), ('status', 'time_last_updated')]),
|
||||||
|
),
|
||||||
|
]
|
20
registrasion/migrations/0025_auto_20160425_0411.py
Normal file
20
registrasion/migrations/0025_auto_20160425_0411.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.2 on 2016-04-25 04:11
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('registrasion', '0024_auto_20160425_0410'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='cart',
|
||||||
|
name='status',
|
||||||
|
field=models.IntegerField(choices=[(1, 'Active'), (2, 'Paid'), (3, 'Released')], db_index=True, default=1),
|
||||||
|
),
|
||||||
|
]
|
|
@ -21,15 +21,23 @@ class Cart(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = "registrasion"
|
app_label = "registrasion"
|
||||||
index_together = [
|
index_together = [
|
||||||
("active", "time_last_updated"),
|
("status", "time_last_updated"),
|
||||||
("active", "released"),
|
("status", "user"),
|
||||||
("active", "user"),
|
|
||||||
("released", "user"),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%d rev #%d" % (self.id, self.revision)
|
return "%d rev #%d" % (self.id, self.revision)
|
||||||
|
|
||||||
|
STATUS_ACTIVE = 1
|
||||||
|
STATUS_PAID = 2
|
||||||
|
STATUS_RELEASED = 3
|
||||||
|
|
||||||
|
STATUS_TYPES = [
|
||||||
|
(STATUS_ACTIVE, _("Active")),
|
||||||
|
(STATUS_PAID, _("Paid")),
|
||||||
|
(STATUS_RELEASED, _("Released")),
|
||||||
|
]
|
||||||
|
|
||||||
user = models.ForeignKey(User)
|
user = models.ForeignKey(User)
|
||||||
# ProductItems (foreign key)
|
# ProductItems (foreign key)
|
||||||
vouchers = models.ManyToManyField(inventory.Voucher, blank=True)
|
vouchers = models.ManyToManyField(inventory.Voucher, blank=True)
|
||||||
|
@ -38,24 +46,21 @@ class Cart(models.Model):
|
||||||
)
|
)
|
||||||
reservation_duration = models.DurationField()
|
reservation_duration = models.DurationField()
|
||||||
revision = models.PositiveIntegerField(default=1)
|
revision = models.PositiveIntegerField(default=1)
|
||||||
active = models.BooleanField(
|
status = models.IntegerField(
|
||||||
default=True,
|
choices=STATUS_TYPES,
|
||||||
db_index=True,
|
db_index=True,
|
||||||
|
default=STATUS_ACTIVE,
|
||||||
)
|
)
|
||||||
released = models.BooleanField(
|
|
||||||
default=False,
|
|
||||||
db_index=True
|
|
||||||
) # Refunds etc
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def reserved_carts(cls):
|
def reserved_carts(cls):
|
||||||
''' Gets all carts that are 'reserved' '''
|
''' Gets all carts that are 'reserved' '''
|
||||||
return Cart.objects.filter(
|
return Cart.objects.filter(
|
||||||
(Q(active=True) &
|
(Q(status=Cart.STATUS_ACTIVE) &
|
||||||
Q(time_last_updated__gt=(
|
Q(time_last_updated__gt=(
|
||||||
timezone.now()-F('reservation_duration')
|
timezone.now()-F('reservation_duration')
|
||||||
))) |
|
))) |
|
||||||
(Q(active=False) & Q(released=False))
|
Q(status=Cart.STATUS_PAID)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ def items_pending(context):
|
||||||
|
|
||||||
all_items = commerce.ProductItem.objects.filter(
|
all_items = commerce.ProductItem.objects.filter(
|
||||||
cart__user=context.request.user,
|
cart__user=context.request.user,
|
||||||
cart__active=True,
|
cart__status=commerce.Cart.STATUS_ACTIVE,
|
||||||
).select_related(
|
).select_related(
|
||||||
"product",
|
"product",
|
||||||
"product__category",
|
"product__category",
|
||||||
|
@ -100,8 +100,7 @@ def items_purchased(context, category=None):
|
||||||
|
|
||||||
all_items = commerce.ProductItem.objects.filter(
|
all_items = commerce.ProductItem.objects.filter(
|
||||||
cart__user=context.request.user,
|
cart__user=context.request.user,
|
||||||
cart__active=False,
|
cart__status=commerce.Cart.STATUS_PAID,
|
||||||
cart__released=False,
|
|
||||||
).select_related("product", "product__category")
|
).select_related("product", "product__category")
|
||||||
|
|
||||||
if category:
|
if category:
|
||||||
|
|
|
@ -28,8 +28,9 @@ class TestingCartController(CartController):
|
||||||
self.set_quantity(product, old_quantity + quantity)
|
self.set_quantity(product, old_quantity + quantity)
|
||||||
|
|
||||||
def next_cart(self):
|
def next_cart(self):
|
||||||
self.cart.active = False
|
if self.cart.status == commerce.Cart.STATUS_ACTIVE:
|
||||||
self.cart.save()
|
self.cart.status = commerce.Cart.STATUS_PAID
|
||||||
|
self.cart.save()
|
||||||
|
|
||||||
|
|
||||||
class TestingInvoiceController(InvoiceController):
|
class TestingInvoiceController(InvoiceController):
|
||||||
|
|
|
@ -5,6 +5,7 @@ from decimal import Decimal
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.core.management import call_command
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from registrasion.models import commerce
|
from registrasion.models import commerce
|
||||||
|
@ -24,6 +25,16 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(RegistrationCartTestCase, self).setUp()
|
super(RegistrationCartTestCase, self).setUp()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
if False:
|
||||||
|
# If you're seeing segfaults in tests, enable this.
|
||||||
|
call_command('flush', verbosity=0, interactive=False,
|
||||||
|
reset_sequences=False,
|
||||||
|
allow_cascade=False,
|
||||||
|
inhibit_post_migrate=False)
|
||||||
|
|
||||||
|
super(RegistrationCartTestCase, self).tearDown()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
|
@ -40,17 +51,13 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
|
||||||
password='top_secret')
|
password='top_secret')
|
||||||
|
|
||||||
attendee1 = people.Attendee.get_instance(cls.USER_1)
|
attendee1 = people.Attendee.get_instance(cls.USER_1)
|
||||||
attendee1.save()
|
|
||||||
profile1 = people.AttendeeProfileBase.objects.create(
|
profile1 = people.AttendeeProfileBase.objects.create(
|
||||||
attendee=attendee1,
|
attendee=attendee1,
|
||||||
)
|
)
|
||||||
profile1.save()
|
|
||||||
attendee2 = people.Attendee.get_instance(cls.USER_2)
|
attendee2 = people.Attendee.get_instance(cls.USER_2)
|
||||||
attendee2.save()
|
|
||||||
profile2 = people.AttendeeProfileBase.objects.create(
|
profile2 = people.AttendeeProfileBase.objects.create(
|
||||||
attendee=attendee2,
|
attendee=attendee2,
|
||||||
)
|
)
|
||||||
profile2.save()
|
|
||||||
|
|
||||||
cls.RESERVATION = datetime.timedelta(hours=1)
|
cls.RESERVATION = datetime.timedelta(hours=1)
|
||||||
|
|
||||||
|
@ -63,7 +70,6 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
|
||||||
render_type=inventory.Category.RENDER_TYPE_RADIO,
|
render_type=inventory.Category.RENDER_TYPE_RADIO,
|
||||||
required=False,
|
required=False,
|
||||||
)
|
)
|
||||||
cat.save()
|
|
||||||
cls.categories.append(cat)
|
cls.categories.append(cat)
|
||||||
|
|
||||||
cls.CAT_1 = cls.categories[0]
|
cls.CAT_1 = cls.categories[0]
|
||||||
|
@ -80,7 +86,6 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
|
||||||
limit_per_user=10,
|
limit_per_user=10,
|
||||||
order=1,
|
order=1,
|
||||||
)
|
)
|
||||||
prod.save()
|
|
||||||
cls.products.append(prod)
|
cls.products.append(prod)
|
||||||
|
|
||||||
cls.PROD_1 = cls.products[0]
|
cls.PROD_1 = cls.products[0]
|
||||||
|
@ -91,7 +96,7 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
|
||||||
cls.PROD_4.price = Decimal("5.00")
|
cls.PROD_4.price = Decimal("5.00")
|
||||||
cls.PROD_4.save()
|
cls.PROD_4.save()
|
||||||
|
|
||||||
# Burn through some carts -- this made some past EC tests fail
|
# Burn through some carts -- this made some past flag tests fail
|
||||||
current_cart = TestingCartController.for_user(cls.USER_1)
|
current_cart = TestingCartController.for_user(cls.USER_1)
|
||||||
|
|
||||||
current_cart.next_cart()
|
current_cart.next_cart()
|
||||||
|
@ -109,9 +114,7 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
|
||||||
start_time=start_time,
|
start_time=start_time,
|
||||||
end_time=end_time
|
end_time=end_time
|
||||||
)
|
)
|
||||||
limit_ceiling.save()
|
|
||||||
limit_ceiling.products.add(cls.PROD_1, cls.PROD_2)
|
limit_ceiling.products.add(cls.PROD_1, cls.PROD_2)
|
||||||
limit_ceiling.save()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def make_category_ceiling(
|
def make_category_ceiling(
|
||||||
|
@ -123,9 +126,7 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
|
||||||
start_time=start_time,
|
start_time=start_time,
|
||||||
end_time=end_time
|
end_time=end_time
|
||||||
)
|
)
|
||||||
limit_ceiling.save()
|
|
||||||
limit_ceiling.categories.add(cls.CAT_1)
|
limit_ceiling.categories.add(cls.CAT_1)
|
||||||
limit_ceiling.save()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def make_discount_ceiling(
|
def make_discount_ceiling(
|
||||||
|
@ -137,13 +138,12 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
|
||||||
end_time=end_time,
|
end_time=end_time,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
)
|
)
|
||||||
limit_ceiling.save()
|
|
||||||
conditions.DiscountForProduct.objects.create(
|
conditions.DiscountForProduct.objects.create(
|
||||||
discount=limit_ceiling,
|
discount=limit_ceiling,
|
||||||
product=cls.PROD_1,
|
product=cls.PROD_1,
|
||||||
percentage=percentage,
|
percentage=percentage,
|
||||||
quantity=10,
|
quantity=10,
|
||||||
).save()
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def new_voucher(self, code="VOUCHER", limit=1):
|
def new_voucher(self, code="VOUCHER", limit=1):
|
||||||
|
@ -152,7 +152,6 @@ class RegistrationCartTestCase(SetTimeMixin, TestCase):
|
||||||
code=code,
|
code=code,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
)
|
)
|
||||||
voucher.save()
|
|
||||||
return voucher
|
return voucher
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -6,6 +6,7 @@ from django.core.exceptions import ValidationError
|
||||||
from controller_helpers import TestingCartController
|
from controller_helpers import TestingCartController
|
||||||
from test_cart import RegistrationCartTestCase
|
from test_cart import RegistrationCartTestCase
|
||||||
|
|
||||||
|
from registrasion.models import commerce
|
||||||
from registrasion.models import conditions
|
from registrasion.models import conditions
|
||||||
|
|
||||||
UTC = pytz.timezone('UTC')
|
UTC = pytz.timezone('UTC')
|
||||||
|
@ -146,8 +147,8 @@ class CeilingsTestCases(RegistrationCartTestCase):
|
||||||
with self.assertRaises(ValidationError):
|
with self.assertRaises(ValidationError):
|
||||||
second_cart.add_to_cart(self.PROD_1, 1)
|
second_cart.add_to_cart(self.PROD_1, 1)
|
||||||
|
|
||||||
first_cart.cart.released = True
|
first_cart.cart.status = commerce.Cart.STATUS_RELEASED
|
||||||
first_cart.next_cart()
|
first_cart.cart.save()
|
||||||
|
|
||||||
second_cart.add_to_cart(self.PROD_1, 1)
|
second_cart.add_to_cart(self.PROD_1, 1)
|
||||||
|
|
||||||
|
@ -159,13 +160,12 @@ class CeilingsTestCases(RegistrationCartTestCase):
|
||||||
description="VOUCHER RECIPIENT",
|
description="VOUCHER RECIPIENT",
|
||||||
voucher=voucher,
|
voucher=voucher,
|
||||||
)
|
)
|
||||||
discount.save()
|
|
||||||
conditions.DiscountForProduct.objects.create(
|
conditions.DiscountForProduct.objects.create(
|
||||||
discount=discount,
|
discount=discount,
|
||||||
product=self.PROD_1,
|
product=self.PROD_1,
|
||||||
percentage=100,
|
percentage=100,
|
||||||
quantity=1
|
quantity=1
|
||||||
).save()
|
)
|
||||||
|
|
||||||
# Buy two of PROD_1, in separate carts:
|
# Buy two of PROD_1, in separate carts:
|
||||||
cart = TestingCartController.for_user(self.USER_1)
|
cart = TestingCartController.for_user(self.USER_1)
|
||||||
|
@ -173,7 +173,7 @@ class CeilingsTestCases(RegistrationCartTestCase):
|
||||||
# and not the ceiling discount.
|
# and not the ceiling discount.
|
||||||
cart.apply_voucher("VOUCHER")
|
cart.apply_voucher("VOUCHER")
|
||||||
cart.add_to_cart(self.PROD_1, 1)
|
cart.add_to_cart(self.PROD_1, 1)
|
||||||
self.assertEqual(1, len(cart.cart.discountitem_set.all()))
|
self.assertEqual(1, cart.cart.discountitem_set.count())
|
||||||
|
|
||||||
cart.next_cart()
|
cart.next_cart()
|
||||||
|
|
||||||
|
@ -181,4 +181,4 @@ class CeilingsTestCases(RegistrationCartTestCase):
|
||||||
# ceiling discount
|
# ceiling discount
|
||||||
cart = TestingCartController.for_user(self.USER_1)
|
cart = TestingCartController.for_user(self.USER_1)
|
||||||
cart.add_to_cart(self.PROD_1, 1)
|
cart.add_to_cart(self.PROD_1, 1)
|
||||||
self.assertEqual(1, len(cart.cart.discountitem_set.all()))
|
self.assertEqual(1, cart.cart.discountitem_set.count())
|
||||||
|
|
|
@ -2,6 +2,7 @@ import pytz
|
||||||
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from registrasion.models import commerce
|
||||||
from registrasion.models import conditions
|
from registrasion.models import conditions
|
||||||
from registrasion.controllers import discount
|
from registrasion.controllers import discount
|
||||||
from controller_helpers import TestingCartController
|
from controller_helpers import TestingCartController
|
||||||
|
@ -22,15 +23,13 @@ class DiscountTestCase(RegistrationCartTestCase):
|
||||||
discount = conditions.IncludedProductDiscount.objects.create(
|
discount = conditions.IncludedProductDiscount.objects.create(
|
||||||
description="PROD_1 includes PROD_2 " + str(amount) + "%",
|
description="PROD_1 includes PROD_2 " + str(amount) + "%",
|
||||||
)
|
)
|
||||||
discount.save()
|
|
||||||
discount.enabling_products.add(cls.PROD_1)
|
discount.enabling_products.add(cls.PROD_1)
|
||||||
discount.save()
|
|
||||||
conditions.DiscountForProduct.objects.create(
|
conditions.DiscountForProduct.objects.create(
|
||||||
discount=discount,
|
discount=discount,
|
||||||
product=cls.PROD_2,
|
product=cls.PROD_2,
|
||||||
percentage=amount,
|
percentage=amount,
|
||||||
quantity=quantity,
|
quantity=quantity,
|
||||||
).save()
|
)
|
||||||
return discount
|
return discount
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -42,15 +41,13 @@ class DiscountTestCase(RegistrationCartTestCase):
|
||||||
discount = conditions.IncludedProductDiscount.objects.create(
|
discount = conditions.IncludedProductDiscount.objects.create(
|
||||||
description="PROD_1 includes CAT_2 " + str(amount) + "%",
|
description="PROD_1 includes CAT_2 " + str(amount) + "%",
|
||||||
)
|
)
|
||||||
discount.save()
|
|
||||||
discount.enabling_products.add(cls.PROD_1)
|
discount.enabling_products.add(cls.PROD_1)
|
||||||
discount.save()
|
|
||||||
conditions.DiscountForCategory.objects.create(
|
conditions.DiscountForCategory.objects.create(
|
||||||
discount=discount,
|
discount=discount,
|
||||||
category=cls.CAT_2,
|
category=cls.CAT_2,
|
||||||
percentage=amount,
|
percentage=amount,
|
||||||
quantity=quantity,
|
quantity=quantity,
|
||||||
).save()
|
)
|
||||||
return discount
|
return discount
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -63,21 +60,19 @@ class DiscountTestCase(RegistrationCartTestCase):
|
||||||
description="PROD_1 includes PROD_3 and PROD_4 " +
|
description="PROD_1 includes PROD_3 and PROD_4 " +
|
||||||
str(amount) + "%",
|
str(amount) + "%",
|
||||||
)
|
)
|
||||||
discount.save()
|
|
||||||
discount.enabling_products.add(cls.PROD_1)
|
discount.enabling_products.add(cls.PROD_1)
|
||||||
discount.save()
|
|
||||||
conditions.DiscountForProduct.objects.create(
|
conditions.DiscountForProduct.objects.create(
|
||||||
discount=discount,
|
discount=discount,
|
||||||
product=cls.PROD_3,
|
product=cls.PROD_3,
|
||||||
percentage=amount,
|
percentage=amount,
|
||||||
quantity=quantity,
|
quantity=quantity,
|
||||||
).save()
|
)
|
||||||
conditions.DiscountForProduct.objects.create(
|
conditions.DiscountForProduct.objects.create(
|
||||||
discount=discount,
|
discount=discount,
|
||||||
product=cls.PROD_4,
|
product=cls.PROD_4,
|
||||||
percentage=amount,
|
percentage=amount,
|
||||||
quantity=quantity,
|
quantity=quantity,
|
||||||
).save()
|
)
|
||||||
return discount
|
return discount
|
||||||
|
|
||||||
def test_discount_is_applied(self):
|
def test_discount_is_applied(self):
|
||||||
|
@ -386,7 +381,6 @@ class DiscountTestCase(RegistrationCartTestCase):
|
||||||
)
|
)
|
||||||
self.assertEqual(1, len(discounts))
|
self.assertEqual(1, len(discounts))
|
||||||
|
|
||||||
cart.cart.active = False # Keep discount enabled
|
|
||||||
cart.next_cart()
|
cart.next_cart()
|
||||||
|
|
||||||
cart = TestingCartController.for_user(self.USER_1)
|
cart = TestingCartController.for_user(self.USER_1)
|
||||||
|
@ -401,8 +395,8 @@ class DiscountTestCase(RegistrationCartTestCase):
|
||||||
)
|
)
|
||||||
self.assertEqual(0, len(discounts))
|
self.assertEqual(0, len(discounts))
|
||||||
|
|
||||||
cart.cart.released = True
|
cart.cart.status = commerce.Cart.STATUS_RELEASED
|
||||||
cart.next_cart()
|
cart.cart.save()
|
||||||
|
|
||||||
discounts = discount.available_discounts(
|
discounts = discount.available_discounts(
|
||||||
self.USER_1,
|
self.USER_1,
|
||||||
|
|
|
@ -23,10 +23,8 @@ class FlagTestCases(RegistrationCartTestCase):
|
||||||
description="Product condition",
|
description="Product condition",
|
||||||
condition=condition,
|
condition=condition,
|
||||||
)
|
)
|
||||||
flag.save()
|
|
||||||
flag.products.add(cls.PROD_1)
|
flag.products.add(cls.PROD_1)
|
||||||
flag.enabling_products.add(cls.PROD_2)
|
flag.enabling_products.add(cls.PROD_2)
|
||||||
flag.save()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_product_flag_on_category(
|
def add_product_flag_on_category(
|
||||||
|
@ -39,10 +37,8 @@ class FlagTestCases(RegistrationCartTestCase):
|
||||||
description="Product condition",
|
description="Product condition",
|
||||||
condition=condition,
|
condition=condition,
|
||||||
)
|
)
|
||||||
flag.save()
|
|
||||||
flag.categories.add(cls.CAT_1)
|
flag.categories.add(cls.CAT_1)
|
||||||
flag.enabling_products.add(cls.PROD_3)
|
flag.enabling_products.add(cls.PROD_3)
|
||||||
flag.save()
|
|
||||||
|
|
||||||
def add_category_flag(cls, condition=conditions.FlagBase.ENABLE_IF_TRUE):
|
def add_category_flag(cls, condition=conditions.FlagBase.ENABLE_IF_TRUE):
|
||||||
''' Adds a category flag condition: adding PROD_1 to a cart is
|
''' Adds a category flag condition: adding PROD_1 to a cart is
|
||||||
|
@ -52,9 +48,7 @@ class FlagTestCases(RegistrationCartTestCase):
|
||||||
condition=condition,
|
condition=condition,
|
||||||
enabling_category=cls.CAT_2,
|
enabling_category=cls.CAT_2,
|
||||||
)
|
)
|
||||||
flag.save()
|
|
||||||
flag.products.add(cls.PROD_1)
|
flag.products.add(cls.PROD_1)
|
||||||
flag.save()
|
|
||||||
|
|
||||||
def test_product_flag_enables_product(self):
|
def test_product_flag_enables_product(self):
|
||||||
self.add_product_flag()
|
self.add_product_flag()
|
||||||
|
@ -265,8 +259,8 @@ class FlagTestCases(RegistrationCartTestCase):
|
||||||
cart_2.add_to_cart(self.PROD_1, 1)
|
cart_2.add_to_cart(self.PROD_1, 1)
|
||||||
cart_2.set_quantity(self.PROD_1, 0)
|
cart_2.set_quantity(self.PROD_1, 0)
|
||||||
|
|
||||||
cart.cart.released = True
|
cart.cart.status = commerce.Cart.STATUS_RELEASED
|
||||||
cart.next_cart()
|
cart.cart.save()
|
||||||
|
|
||||||
with self.assertRaises(ValidationError):
|
with self.assertRaises(ValidationError):
|
||||||
cart_2.set_quantity(self.PROD_1, 1)
|
cart_2.set_quantity(self.PROD_1, 1)
|
||||||
|
@ -283,8 +277,8 @@ class FlagTestCases(RegistrationCartTestCase):
|
||||||
cart_2.add_to_cart(self.PROD_1, 1)
|
cart_2.add_to_cart(self.PROD_1, 1)
|
||||||
cart_2.set_quantity(self.PROD_1, 0)
|
cart_2.set_quantity(self.PROD_1, 0)
|
||||||
|
|
||||||
cart.cart.released = True
|
cart.cart.status = commerce.Cart.STATUS_RELEASED
|
||||||
cart.next_cart()
|
cart.cart.save()
|
||||||
|
|
||||||
with self.assertRaises(ValidationError):
|
with self.assertRaises(ValidationError):
|
||||||
cart_2.set_quantity(self.PROD_1, 1)
|
cart_2.set_quantity(self.PROD_1, 1)
|
||||||
|
|
|
@ -97,7 +97,10 @@ class InvoiceTestCase(RegistrationCartTestCase):
|
||||||
self.assertTrue(invoice.invoice.is_paid)
|
self.assertTrue(invoice.invoice.is_paid)
|
||||||
|
|
||||||
# Cart should not be active
|
# Cart should not be active
|
||||||
self.assertFalse(invoice.invoice.cart.active)
|
self.assertNotEqual(
|
||||||
|
commerce.Cart.STATUS_ACTIVE,
|
||||||
|
invoice.invoice.cart.status,
|
||||||
|
)
|
||||||
|
|
||||||
# Asking for a cart should generate a new one
|
# Asking for a cart should generate a new one
|
||||||
new_cart = TestingCartController.for_user(self.USER_1)
|
new_cart = TestingCartController.for_user(self.USER_1)
|
||||||
|
@ -482,7 +485,7 @@ class InvoiceTestCase(RegistrationCartTestCase):
|
||||||
|
|
||||||
notes = commerce.CreditNote.objects.filter(invoice=invoice.invoice)
|
notes = commerce.CreditNote.objects.filter(invoice=invoice.invoice)
|
||||||
notes = sorted(notes, key = lambda note: note.value)
|
notes = sorted(notes, key = lambda note: note.value)
|
||||||
|
|
||||||
self.assertEqual(cnval, notes[0].value)
|
self.assertEqual(cnval, notes[0].value)
|
||||||
self.assertEqual(val, notes[1].value)
|
self.assertEqual(val, notes[1].value)
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ from controller_helpers import TestingInvoiceController
|
||||||
|
|
||||||
from test_cart import RegistrationCartTestCase
|
from test_cart import RegistrationCartTestCase
|
||||||
|
|
||||||
|
from registrasion.models import commerce
|
||||||
|
|
||||||
UTC = pytz.timezone('UTC')
|
UTC = pytz.timezone('UTC')
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,10 +23,16 @@ class RefundTestCase(RegistrationCartTestCase):
|
||||||
self.assertFalse(invoice.invoice.is_void)
|
self.assertFalse(invoice.invoice.is_void)
|
||||||
self.assertTrue(invoice.invoice.is_paid)
|
self.assertTrue(invoice.invoice.is_paid)
|
||||||
self.assertFalse(invoice.invoice.is_refunded)
|
self.assertFalse(invoice.invoice.is_refunded)
|
||||||
self.assertFalse(invoice.invoice.cart.released)
|
self.assertNotEqual(
|
||||||
|
commerce.Cart.STATUS_RELEASED,
|
||||||
|
invoice.invoice.cart.status,
|
||||||
|
)
|
||||||
|
|
||||||
invoice.refund()
|
invoice.refund()
|
||||||
self.assertFalse(invoice.invoice.is_void)
|
self.assertFalse(invoice.invoice.is_void)
|
||||||
self.assertFalse(invoice.invoice.is_paid)
|
self.assertFalse(invoice.invoice.is_paid)
|
||||||
self.assertTrue(invoice.invoice.is_refunded)
|
self.assertTrue(invoice.invoice.is_refunded)
|
||||||
self.assertTrue(invoice.invoice.cart.released)
|
self.assertEqual(
|
||||||
|
commerce.Cart.STATUS_RELEASED,
|
||||||
|
invoice.invoice.cart.status,
|
||||||
|
)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import pytz
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import IntegrityError
|
from django.db import IntegrityError
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
from registrasion.models import conditions
|
from registrasion.models import conditions
|
||||||
from registrasion.models import inventory
|
from registrasion.models import inventory
|
||||||
|
@ -64,9 +65,7 @@ class VoucherTestCases(RegistrationCartTestCase):
|
||||||
voucher=voucher,
|
voucher=voucher,
|
||||||
condition=conditions.FlagBase.ENABLE_IF_TRUE,
|
condition=conditions.FlagBase.ENABLE_IF_TRUE,
|
||||||
)
|
)
|
||||||
flag.save()
|
|
||||||
flag.products.add(self.PROD_1)
|
flag.products.add(self.PROD_1)
|
||||||
flag.save()
|
|
||||||
|
|
||||||
# Adding the product without a voucher will not work
|
# Adding the product without a voucher will not work
|
||||||
current_cart = TestingCartController.for_user(self.USER_1)
|
current_cart = TestingCartController.for_user(self.USER_1)
|
||||||
|
@ -84,13 +83,12 @@ class VoucherTestCases(RegistrationCartTestCase):
|
||||||
description="VOUCHER RECIPIENT",
|
description="VOUCHER RECIPIENT",
|
||||||
voucher=voucher,
|
voucher=voucher,
|
||||||
)
|
)
|
||||||
discount.save()
|
|
||||||
conditions.DiscountForProduct.objects.create(
|
conditions.DiscountForProduct.objects.create(
|
||||||
discount=discount,
|
discount=discount,
|
||||||
product=self.PROD_1,
|
product=self.PROD_1,
|
||||||
percentage=Decimal(100),
|
percentage=Decimal(100),
|
||||||
quantity=1
|
quantity=1
|
||||||
).save()
|
)
|
||||||
|
|
||||||
# Having PROD_1 in place should add a discount
|
# Having PROD_1 in place should add a discount
|
||||||
current_cart = TestingCartController.for_user(self.USER_1)
|
current_cart = TestingCartController.for_user(self.USER_1)
|
||||||
|
@ -98,6 +96,7 @@ class VoucherTestCases(RegistrationCartTestCase):
|
||||||
current_cart.add_to_cart(self.PROD_1, 1)
|
current_cart.add_to_cart(self.PROD_1, 1)
|
||||||
self.assertEqual(1, len(current_cart.cart.discountitem_set.all()))
|
self.assertEqual(1, len(current_cart.cart.discountitem_set.all()))
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
def test_voucher_codes_unique(self):
|
def test_voucher_codes_unique(self):
|
||||||
self.new_voucher(code="VOUCHER")
|
self.new_voucher(code="VOUCHER")
|
||||||
with self.assertRaises(IntegrityError):
|
with self.assertRaises(IntegrityError):
|
||||||
|
|
Loading…
Reference in a new issue