Add constraint for a maximum number of a ticket per order

This commit is contained in:
Raphael Michel
2017-03-24 17:11:48 +01:00
parent 36d6b6f9ab
commit 69faab01b2
10 changed files with 141 additions and 4 deletions

View File

@@ -33,6 +33,7 @@ error_messages = {
'in_part': _('Some of the products you selected are no longer available in '
'the quantity you selected. Please see below for details.'),
'max_items': _("You cannot select more than %s items per order."),
'max_items_per_product': _("You cannot select more than %(max)s items of the product %(product)s."),
'not_started': _('The presale period for this event has not yet started.'),
'ended': _('The presale period has ended.'),
'price_too_high': _('The entered price is to high.'),
@@ -131,6 +132,21 @@ class CartManager:
if op.voucher and not op.voucher.applies_to(op.item, op.variation):
raise CartError(error_messages['voucher_invalid_item'])
if isinstance(op, self.AddOperation):
if op.item.max_per_order:
new_total = (
len([1 for p in self.positions if p.item_id == op.item.pk]) +
sum([_op.count for _op in self._operations
if isinstance(_op, self.AddOperation) and _op.item == op.item]) +
op.count -
len([1 for _op in self._operations
if isinstance(_op, self.RemoveOperation) and _op.position.item_id == op.item.pk])
)
if new_total > op.item.max_per_order:
raise CartError(error_messages['max_items_per_product'], {'max': op.item.max_per_order,
'product': op.item.name})
def _get_price(self, item: Item, variation: Optional[ItemVariation],
voucher: Optional[Voucher], custom_price: Optional[Decimal]):
price = item.default_price if variation is None else (

View File

@@ -45,6 +45,8 @@ error_messages = {
'meantime. Please see below for details.'),
'internal': _("An internal error occured, please try again."),
'empty': _("Your cart is empty."),
'max_items_per_product': _("You cannot select more than %(max)s items of the product %(product)s. We removed the "
"surplus items from your cart."),
'busy': _('We were not able to process your request completely as the '
'server was too busy. Please try again.'),
'not_started': _('The presale period for this event has not yet started.'),
@@ -206,8 +208,10 @@ def _check_date(event: Event, now_dt: datetime):
def _check_positions(event: Event, now_dt: datetime, positions: List[CartPosition]):
err = None
errargs = None
_check_date(event, now_dt)
products_seen = Counter()
for i, cp in enumerate(positions):
if not cp.item.active or (cp.variation and not cp.variation.active):
err = err or error_messages['unavailable']
@@ -215,6 +219,14 @@ def _check_positions(event: Event, now_dt: datetime, positions: List[CartPositio
continue
quotas = list(cp.item.quotas.all()) if cp.variation is None else list(cp.variation.quotas.all())
products_seen[cp.item] += 1
if cp.item.max_per_order and products_seen[cp.item] > cp.item.max_per_order:
err = error_messages['max_items_per_product']
errargs = {'max': cp.item.max_per_order,
'product': cp.item.name}
cp.delete() # Sorry!
break
if cp.voucher:
redeemed_in_carts = CartPosition.objects.filter(
Q(voucher=cp.voucher) & Q(event=event) & Q(expires__gte=now_dt)
@@ -286,7 +298,7 @@ def _check_positions(event: Event, now_dt: datetime, positions: List[CartPositio
else:
cp.delete() # Sorry!
if err:
raise OrderError(err)
raise OrderError(err, errargs)
def _create_order(event: Event, email: str, positions: List[CartPosition], now_dt: datetime,