Clean up localization or error messages in cart (#3049)

This commit is contained in:
Richard Schreiber
2023-01-30 17:24:09 +01:00
committed by GitHub
parent 59f409b1c6
commit a7f9e100d2
5 changed files with 245 additions and 186 deletions

View File

@@ -43,7 +43,9 @@ from django.db import DatabaseError, transaction
from django.db.models import Count, Exists, IntegerField, OuterRef, Q, Value
from django.dispatch import receiver
from django.utils.timezone import make_aware, now
from django.utils.translation import gettext as _, pgettext_lazy
from django.utils.translation import (
gettext as _, gettext_lazy, ngettext_lazy, pgettext_lazy,
)
from django_scopes import scopes_disabled
from pretix.base.channels import get_all_sales_channels
@@ -81,74 +83,119 @@ class CartError(Exception):
if msgargs:
msg = _(msg) % msgargs
else:
msg = _(msg)
# force msg to string to make sure lazy-translation is done in current locale-context
# otherwise translation might happen in celery-context, which uses default-locale
# also translate with _/gettext to keep it backwards compatible
msg = _(str(msg))
super().__init__(msg)
error_messages = {
'busy': _('We were not able to process your request completely as the '
'server was too busy. Please try again.'),
'empty': _('You did not select any products.'),
'unknown_position': _('Unknown cart position.'),
'busy': gettext_lazy(
'We were not able to process your request completely as the '
'server was too busy. Please try again.'
),
'empty': gettext_lazy('You did not select any products.'),
'unknown_position': gettext_lazy('Unknown cart position.'),
'subevent_required': pgettext_lazy('subevent', 'No date was specified.'),
'not_for_sale': _('You selected a product which is not available for sale.'),
'unavailable': _('Some of the products you selected are no longer available. '
'Please see below for details.'),
'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."),
'min_items_per_product': _("You need to select at least %(min)s items of the product %(product)s."),
'min_items_per_product_removed': _("We removed %(product)s from your cart as you can not buy less than "
"%(min)s items of it."),
'not_started': _('The booking period for this event has not yet started.'),
'ended': _('The booking period for this event has ended.'),
'payment_ended': _('All payments for this event need to be confirmed already, so no new orders can be created.'),
'some_subevent_not_started': _('The booking period for this event has not yet started. The affected positions '
'have been removed from your cart.'),
'some_subevent_ended': _('The booking period for one of the events in your cart has ended. The affected '
'positions have been removed from your cart.'),
'price_too_high': _('The entered price is to high.'),
'voucher_invalid': _('This voucher code is not known in our database.'),
'voucher_min_usages': _('The voucher code "%(voucher)s" can only be used if you select at least %(number)s '
'matching products.'),
'voucher_min_usages_removed': _('The voucher code "%(voucher)s" can only be used if you select at least '
'%(number)s matching products. We have therefore removed some positions from '
'your cart that can no longer be purchased like this.'),
'voucher_redeemed': _('This voucher code has already been used the maximum number of times allowed.'),
'voucher_redeemed_cart': _('This voucher code is currently locked since it is already contained in a cart. This '
'might mean that someone else is redeeming this voucher right now, or that you tried '
'to redeem it before but did not complete the checkout process. You can try to use it '
'again in %d minutes.'),
'voucher_redeemed_partial': _('This voucher code can only be redeemed %d more times.'),
'voucher_double': _('You already used this voucher code. Remove the associated line from your '
'cart if you want to use it for a different product.'),
'voucher_expired': _('This voucher is expired.'),
'voucher_invalid_item': _('This voucher is not valid for this product.'),
'voucher_invalid_seat': _('This voucher is not valid for this seat.'),
'voucher_no_match': _('We did not find any position in your cart that we could use this voucher for. If you want '
'to add something new to your cart using that voucher, you can do so with the voucher '
'redemption option on the bottom of the page.'),
'voucher_item_not_available': _(
'not_for_sale': gettext_lazy('You selected a product which is not available for sale.'),
'unavailable': gettext_lazy(
'Some of the products you selected are no longer available. '
'Please see below for details.'
),
'in_part': gettext_lazy(
'Some of the products you selected are no longer available in '
'the quantity you selected. Please see below for details.'
),
'max_items': ngettext_lazy(
"You cannot select more than %s item per order.",
"You cannot select more than %s items per order."
),
'max_items_per_product': ngettext_lazy(
"You cannot select more than %(max)s item of the product %(product)s.",
"You cannot select more than %(max)s items of the product %(product)s.",
"max"
),
'min_items_per_product': ngettext_lazy(
"You need to select at least %(min)s item of the product %(product)s.",
"You need to select at least %(min)s items of the product %(product)s.",
"min"
),
'min_items_per_product_removed': ngettext_lazy(
"We removed %(product)s from your cart as you can not buy less than %(min)s item of it.",
"We removed %(product)s from your cart as you can not buy less than %(min)s items of it.",
"min"
),
'not_started': gettext_lazy('The booking period for this event has not yet started.'),
'ended': gettext_lazy('The booking period for this event has ended.'),
'payment_ended': gettext_lazy('All payments for this event need to be confirmed already, so no new orders can be created.'),
'some_subevent_not_started': gettext_lazy(
'The booking period for this event has not yet started. The affected positions '
'have been removed from your cart.'),
'some_subevent_ended': gettext_lazy(
'The booking period for one of the events in your cart has ended. The affected '
'positions have been removed from your cart.'),
'price_too_high': gettext_lazy('The entered price is to high.'),
'voucher_invalid': gettext_lazy('This voucher code is not known in our database.'),
'voucher_min_usages': gettext_lazy(
'The voucher code "%(voucher)s" can only be used if you select at least %(number)s '
'matching products.'
),
'voucher_min_usages_removed': ngettext_lazy(
'The voucher code "%(voucher)s" can only be used if you select at least %(number)s matching products. '
'We have therefore removed some positions from your cart that can no longer be purchased like this.',
'The voucher code "%(voucher)s" can only be used if you select at least %(number)s matching products. '
'We have therefore removed some positions from your cart that can no longer be purchased like this.',
'number'
),
'voucher_redeemed': gettext_lazy('This voucher code has already been used the maximum number of times allowed.'),
'voucher_redeemed_cart': gettext_lazy(
'This voucher code is currently locked since it is already contained in a cart. This '
'might mean that someone else is redeeming this voucher right now, or that you tried '
'to redeem it before but did not complete the checkout process. You can try to use it '
'again in %d minutes.'
),
'voucher_redeemed_partial': gettext_lazy('This voucher code can only be redeemed %d more times.'),
'voucher_whole_cart_not_combined': gettext_lazy('Applying a voucher to the whole cart should not be combined with other operations.'),
'voucher_double': gettext_lazy(
'You already used this voucher code. Remove the associated line from your '
'cart if you want to use it for a different product.'
),
'voucher_expired': gettext_lazy('This voucher is expired.'),
'voucher_invalid_item': gettext_lazy('This voucher is not valid for this product.'),
'voucher_invalid_seat': gettext_lazy('This voucher is not valid for this seat.'),
'voucher_no_match': gettext_lazy(
'We did not find any position in your cart that we could use this voucher for. If you want '
'to add something new to your cart using that voucher, you can do so with the voucher '
'redemption option on the bottom of the page.'
),
'voucher_item_not_available': gettext_lazy(
'Your voucher is valid for a product that is currently not for sale.'),
'voucher_invalid_subevent': pgettext_lazy('subevent', 'This voucher is not valid for this event date.'),
'voucher_required': _('You need a valid voucher code to order this product.'),
'voucher_required': gettext_lazy('You need a valid voucher code to order this product.'),
'inactive_subevent': pgettext_lazy('subevent', 'The selected event date is not active.'),
'addon_invalid_base': _('You can not select an add-on for the selected product.'),
'addon_duplicate_item': _('You can not select two variations of the same add-on product.'),
'addon_max_count': _('You can select at most %(max)s add-ons from the category %(cat)s for the product %(base)s.'),
'addon_min_count': _('You need to select at least %(min)s add-ons from the category %(cat)s for the '
'product %(base)s.'),
'addon_no_multi': _('You can select every add-ons from the category %(cat)s for the product %(base)s at most once.'),
'addon_only': _('One of the products you selected can only be bought as an add-on to another product.'),
'bundled_only': _('One of the products you selected can only be bought part of a bundle.'),
'seat_required': _('You need to select a specific seat.'),
'seat_invalid': _('Please select a valid seat.'),
'seat_forbidden': _('You can not select a seat for this position.'),
'seat_unavailable': _('The seat you selected has already been taken. Please select a different seat.'),
'seat_multiple': _('You can not select the same seat multiple times.'),
'gift_card': _("You entered a gift card instead of a voucher. Gift cards can be entered later on when you're asked for your payment details."),
'country_blocked': _('One of the selected products is not available in the selected country.'),
'addon_invalid_base': gettext_lazy('You can not select an add-on for the selected product.'),
'addon_duplicate_item': gettext_lazy('You can not select two variations of the same add-on product.'),
'addon_max_count': ngettext_lazy(
'You can select at most %(max)s add-on from the category %(cat)s for the product %(base)s.',
'You can select at most %(max)s add-ons from the category %(cat)s for the product %(base)s.',
'max'
),
'addon_min_count': ngettext_lazy(
'You need to select at least %(min)s add-on from the category %(cat)s for the product %(base)s.',
'You need to select at least %(min)s add-ons from the category %(cat)s for the product %(base)s.',
'min'
),
'addon_no_multi': gettext_lazy('You can select every add-ons from the category %(cat)s for the product %(base)s at most once.'),
'addon_only': gettext_lazy('One of the products you selected can only be bought as an add-on to another product.'),
'bundled_only': gettext_lazy('One of the products you selected can only be bought part of a bundle.'),
'seat_required': gettext_lazy('You need to select a specific seat.'),
'seat_invalid': gettext_lazy('Please select a valid seat.'),
'seat_forbidden': gettext_lazy('You can not select a seat for this position.'),
'seat_unavailable': gettext_lazy('The seat you selected has already been taken. Please select a different seat.'),
'seat_multiple': gettext_lazy('You can not select the same seat multiple times.'),
'gift_card': gettext_lazy("You entered a gift card instead of a voucher. Gift cards can be entered later on when you're asked for your payment details."),
'country_blocked': gettext_lazy('One of the selected products is not available in the selected country.'),
}
@@ -320,8 +367,7 @@ class CartManager:
cartsize -= len([1 for op in self._operations if isinstance(op, self.RemoveOperation) if
not op.position.addon_to_id])
if cartsize > int(self.event.settings.max_items_per_order):
# TODO: i18n plurals
raise CartError(_(error_messages['max_items']) % (self.event.settings.max_items_per_order,))
raise CartError(error_messages['max_items'] % self.event.settings.max_items_per_order)
def _check_item_constraints(self, op, current_ops=[]):
if isinstance(op, (self.AddOperation, self.ExtendOperation)):
@@ -494,7 +540,7 @@ class CartManager:
def apply_voucher(self, voucher_code: str):
if self._operations:
raise CartError('Applying a voucher to the whole cart should not be combined with other operations.')
raise CartError(error_messages['voucher_whole_cart_not_combined'])
try:
voucher = self.event.vouchers.get(code__iexact=voucher_code.strip())
except Voucher.DoesNotExist:
@@ -539,7 +585,7 @@ class CartManager:
for voucher, cnt in list(voucher_use_diff.items()):
if 0 < cnt < voucher.min_usages_remaining:
raise CartError(
_(error_messages['voucher_min_usages']) % {
error_messages['voucher_min_usages'] % {
'voucher': voucher.code,
'number': voucher.min_usages_remaining,
}
@@ -843,22 +889,16 @@ class CartManager:
for (i, v), c in selected.items():
n_per_i[i] += c
if sum(selected.values()) > iao.max_count:
# TODO: Proper i18n
# TODO: Proper pluralization
raise CartError(
error_messages['addon_max_count'],
{
error_messages['addon_max_count'] % {
'base': str(item.name),
'max': iao.max_count,
'cat': str(iao.addon_category.name),
}
)
elif sum(selected.values()) < iao.min_count:
# TODO: Proper i18n
# TODO: Proper pluralization
raise CartError(
error_messages['addon_min_count'],
{
error_messages['addon_min_count'] % {
'base': str(item.name),
'min': iao.min_count,
'cat': str(iao.addon_category.name),
@@ -866,8 +906,7 @@ class CartManager:
)
elif any(v > 1 for v in n_per_i.values()) and not iao.multi_allowed:
raise CartError(
error_messages['addon_no_multi'],
{
error_messages['addon_no_multi'] % {
'base': str(item.name),
'cat': str(iao.addon_category.name),
}
@@ -931,7 +970,7 @@ class CartManager:
if item.max_per_order and count > item.max_per_order:
raise CartError(
_(error_messages['max_items_per_product']) % {
error_messages['max_items_per_product'] % {
'max': item.max_per_order,
'product': item.name
}
@@ -945,13 +984,13 @@ class CartManager:
for p in self.positions:
if p.item_id == item.pk and p.pk not in removals:
self._operations.append(self.RemoveOperation(position=p))
err = _(error_messages['min_items_per_product_removed']) % {
err = error_messages['min_items_per_product_removed'] % {
'min': item.min_per_order,
'product': item.name
}
if not err:
raise CartError(
_(error_messages['min_items_per_product']) % {
error_messages['min_items_per_product'] % {
'min': item.min_per_order,
'product': item.name
}
@@ -980,13 +1019,13 @@ class CartManager:
for p in self.positions:
if p.voucher_id == voucher.pk and p.pk not in removals:
self._operations.append(self.RemoveOperation(position=p))
err = _(error_messages['voucher_min_usages_removed']) % {
err = error_messages['voucher_min_usages_removed'] % {
'voucher': voucher.code,
'number': voucher.min_usages_remaining,
}
if not err:
raise CartError(
_(error_messages['voucher_min_usages']) % {
error_messages['voucher_min_usages'] % {
'voucher': voucher.code,
'number': voucher.min_usages_remaining,
}

View File

@@ -54,15 +54,13 @@ from django.db.transaction import get_connection
from django.dispatch import receiver
from django.utils.functional import cached_property
from django.utils.timezone import make_aware, now
from django.utils.translation import gettext as _
from django.utils.translation import gettext as _, gettext_lazy, ngettext_lazy
from django_scopes import scopes_disabled
from pretix.api.models import OAuthApplication
from pretix.base.channels import get_all_sales_channels
from pretix.base.email import get_email_context
from pretix.base.i18n import (
LazyLocaleException, get_language_without_region, language,
)
from pretix.base.i18n import get_language_without_region, language
from pretix.base.models import (
CartPosition, Device, Event, GiftCard, Item, ItemVariation, Membership,
Order, OrderPayment, OrderPosition, Quota, Seat, SeatCategoryMapping, User,
@@ -101,47 +99,94 @@ from pretix.helpers import OF_SELF
from pretix.helpers.models import modelcopy
from pretix.helpers.periodic import minimum_interval
class OrderError(Exception):
def __init__(self, *args):
msg = args[0]
msgargs = args[1] if len(args) > 1 else None
self.args = args
if msgargs:
msg = _(msg) % msgargs
else:
# force msg to string to make sure lazy-translation is done in current locale-context
# otherwise translation might happen in celery-context, which uses default-locale
# also translate with _/gettext to keep it backwards compatible
msg = _(str(msg))
super().__init__(msg)
error_messages = {
'unavailable': _('Some of the products you selected were no longer available. '
'Please see below for details.'),
'in_part': _('Some of the products you selected were no longer available in '
'the quantity you selected. Please see below for details.'),
'price_changed': _('The price of some of the items in your cart has changed in the '
'meantime. Please see below for details.'),
'internal': _("An internal error occurred, 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 booking period for this event has not yet started.'),
'ended': _('The booking period has ended.'),
'voucher_min_usages': _('The voucher code "%(voucher)s" can only be used if you select at least %(number)s '
'matching products.'),
'voucher_invalid': _('The voucher code used for one of the items in your cart is not known in our database.'),
'voucher_redeemed': _('The voucher code used for one of the items in your cart has already been used the maximum '
'number of times allowed. We removed this item from your cart.'),
'voucher_budget_used': _('The voucher code used for one of the items in your cart has already been too often. We '
'adjusted the price of the item in your cart.'),
'voucher_expired': _('The voucher code used for one of the items in your cart is expired. We removed this item '
'from your cart.'),
'voucher_invalid_item': _('The voucher code used for one of the items in your cart is not valid for this item. We '
'removed this item from your cart.'),
'voucher_required': _('You need a valid voucher code to order one of the products.'),
'some_subevent_not_started': _('The booking period for one of the events in your cart has not yet started. The '
'affected positions have been removed from your cart.'),
'some_subevent_ended': _('The booking period for one of the events in your cart has ended. The affected '
'positions have been removed from your cart.'),
'seat_invalid': _('One of the seats in your order was invalid, we removed the position from your cart.'),
'seat_unavailable': _('One of the seats in your order has been taken in the meantime, we removed the position from your cart.'),
'country_blocked': _('One of the selected products is not available in the selected country.'),
'not_for_sale': _('You selected a product which is not available for sale.'),
'addon_invalid_base': _('You can not select an add-on for the selected product.'),
'addon_duplicate_item': _('You can not select two variations of the same add-on product.'),
'addon_max_count': _('You can select at most %(max)s add-ons from the category %(cat)s for the product %(base)s.'),
'addon_min_count': _('You need to select at least %(min)s add-ons from the category %(cat)s for the '
'product %(base)s.'),
'addon_no_multi': _('You can select every add-ons from the category %(cat)s for the product %(base)s at most once.'),
'unavailable': gettext_lazy(
'Some of the products you selected were no longer available. '
'Please see below for details.'
),
'in_part': gettext_lazy(
'Some of the products you selected were no longer available in '
'the quantity you selected. Please see below for details.'
),
'price_changed': gettext_lazy(
'The price of some of the items in your cart has changed in the '
'meantime. Please see below for details.'
),
'internal': gettext_lazy("An internal error occurred, please try again."),
'empty': gettext_lazy("Your cart is empty."),
'max_items_per_product': ngettext_lazy(
"You cannot select more than %(max)s item of the product %(product)s. We removed the surplus items from your cart.",
"You cannot select more than %(max)s items of the product %(product)s. We removed the surplus items from your cart.",
"max"
),
'busy': gettext_lazy(
'We were not able to process your request completely as the '
'server was too busy. Please try again.'
),
'not_started': gettext_lazy('The booking period for this event has not yet started.'),
'ended': gettext_lazy('The booking period has ended.'),
'voucher_min_usages': ngettext_lazy(
'The voucher code "%(voucher)s" can only be used if you select at least %(number)s matching products.',
'The voucher code "%(voucher)s" can only be used if you select at least %(number)s matching products.',
'number'
),
'voucher_invalid': gettext_lazy('The voucher code used for one of the items in your cart is not known in our database.'),
'voucher_redeemed': gettext_lazy(
'The voucher code used for one of the items in your cart has already been used the maximum '
'number of times allowed. We removed this item from your cart.'
),
'voucher_budget_used': gettext_lazy(
'The voucher code used for one of the items in your cart has already been too often. We '
'adjusted the price of the item in your cart.'
),
'voucher_expired': gettext_lazy(
'The voucher code used for one of the items in your cart is expired. We removed this item from your cart.'
),
'voucher_invalid_item': gettext_lazy(
'The voucher code used for one of the items in your cart is not valid for this item. We removed this item from your cart.'
),
'voucher_required': gettext_lazy('You need a valid voucher code to order one of the products.'),
'some_subevent_not_started': gettext_lazy(
'The booking period for one of the events in your cart has not yet started. The '
'affected positions have been removed from your cart.'
),
'some_subevent_ended': gettext_lazy(
'The booking period for one of the events in your cart has ended. The affected '
'positions have been removed from your cart.'
),
'seat_invalid': gettext_lazy('One of the seats in your order was invalid, we removed the position from your cart.'),
'seat_unavailable': gettext_lazy('One of the seats in your order has been taken in the meantime, we removed the position from your cart.'),
'country_blocked': gettext_lazy('One of the selected products is not available in the selected country.'),
'not_for_sale': gettext_lazy('You selected a product which is not available for sale.'),
'addon_invalid_base': gettext_lazy('You can not select an add-on for the selected product.'),
'addon_duplicate_item': gettext_lazy('You can not select two variations of the same add-on product.'),
'addon_max_count': ngettext_lazy(
'You can select at most %(max)s add-on from the category %(cat)s for the product %(base)s.',
'You can select at most %(max)s add-ons from the category %(cat)s for the product %(base)s.',
'max'
),
'addon_min_count': ngettext_lazy(
'You need to select at least %(min)s add-on from the category %(cat)s for the product %(base)s.',
'You need to select at least %(min)s add-ons from the category %(cat)s for the product %(base)s.',
'min'
),
'addon_no_multi': gettext_lazy('You can select every add-ons from the category %(cat)s for the product %(base)s at most once.'),
}
logger = logging.getLogger(__name__)
@@ -157,7 +202,7 @@ def reactivate_order(order: Order, force: bool=False, user: User=None, auth=None
enough quota.
"""
if order.status != Order.STATUS_CANCELED:
raise OrderError('The order was not canceled.')
raise OrderError(_('The order was not canceled.'))
with order.event.lock() as now_dt:
is_available = order._is_still_available(now_dt, count_waitinglist=False, check_voucher_usage=True,
@@ -538,18 +583,6 @@ def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device
return order.pk
class OrderError(LazyLocaleException):
def __init__(self, *args):
msg = args[0]
msgargs = args[1] if len(args) > 1 else None
self.args = args
if msgargs:
msg = _(msg) % msgargs
else:
msg = _(msg)
super().__init__(msg)
def _check_date(event: Event, now_dt: datetime):
if event.presale_start and now_dt < event.presale_start:
raise OrderError(error_messages['not_started'])
@@ -570,7 +603,6 @@ def _check_date(event: Event, now_dt: datetime):
def _check_positions(event: Event, now_dt: datetime, positions: List[CartPosition], address: InvoiceAddress=None,
sales_channel='web', customer=None):
err = None
errargs = None
_check_date(event, now_dt)
products_seen = Counter()
@@ -607,9 +639,10 @@ def _check_positions(event: Event, now_dt: datetime, positions: List[CartPositio
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}
err = error_messages['max_items_per_product'] % {
'max': cp.item.max_per_order,
'product': cp.item.name
}
delete(cp)
break
@@ -728,7 +761,7 @@ def _check_positions(event: Event, now_dt: datetime, positions: List[CartPositio
for voucher, cnt in v_usages.items():
if 0 < cnt < voucher.min_usages_remaining:
raise OrderError(error_messages['voucher_min_usages'], {
raise OrderError(error_messages['voucher_min_usages'] % {
'voucher': voucher.code,
'number': voucher.min_usages_remaining,
})
@@ -791,7 +824,7 @@ def _check_positions(event: Event, now_dt: datetime, positions: List[CartPositio
cp.save()
if err:
raise OrderError(err, errargs)
raise OrderError(err)
def _get_fees(positions: List[CartPosition], payment_requests: List[dict], address: InvoiceAddress,
@@ -1344,22 +1377,26 @@ def notify_user_changed_order(order, user=None, auth=None, invoices=[]):
class OrderChangeManager:
error_messages = {
'product_without_variation': _('You need to select a variation of the product.'),
'quota': _('The quota {name} does not have enough capacity left to perform the operation.'),
'quota_missing': _('There is no quota defined that allows this operation.'),
'product_invalid': _('The selected product is not active or has no price set.'),
'complete_cancel': _('This operation would leave the order empty. Please cancel the order itself instead.'),
'paid_to_free_exceeded': _('This operation would make the order free and therefore immediately paid, however '
'no quota is available.'),
'addon_to_required': _('This is an add-on product, please select the base position it should be added to.'),
'addon_invalid': _('The selected base position does not allow you to add this product as an add-on.'),
'subevent_required': _('You need to choose a subevent for the new position.'),
'seat_unavailable': _('The selected seat "{seat}" is not available.'),
'seat_subevent_mismatch': _('You selected seat "{seat}" for a date that does not match the selected ticket date. Please choose a seat again.'),
'seat_required': _('The selected product requires you to select a seat.'),
'seat_forbidden': _('The selected product does not allow to select a seat.'),
'tax_rule_country_blocked': _('The selected country is blocked by your tax rule.'),
'gift_card_change': _('You cannot change the price of a position that has been used to issue a gift card.'),
'product_without_variation': gettext_lazy('You need to select a variation of the product.'),
'quota': gettext_lazy('The quota {name} does not have enough capacity left to perform the operation.'),
'quota_missing': gettext_lazy('There is no quota defined that allows this operation.'),
'product_invalid': gettext_lazy('The selected product is not active or has no price set.'),
'complete_cancel': gettext_lazy('This operation would leave the order empty. Please cancel the order itself instead.'),
'paid_to_free_exceeded': gettext_lazy(
'This operation would make the order free and therefore immediately paid, however '
'no quota is available.'
),
'addon_to_required': gettext_lazy('This is an add-on product, please select the base position it should be added to.'),
'addon_invalid': gettext_lazy('The selected base position does not allow you to add this product as an add-on.'),
'subevent_required': gettext_lazy('You need to choose a subevent for the new position.'),
'seat_unavailable': gettext_lazy('The selected seat "{seat}" is not available.'),
'seat_subevent_mismatch': gettext_lazy(
'You selected seat "{seat}" for a date that does not match the selected ticket date. Please choose a seat again.'
),
'seat_required': gettext_lazy('The selected product requires you to select a seat.'),
'seat_forbidden': gettext_lazy('The selected product does not allow to select a seat.'),
'tax_rule_country_blocked': gettext_lazy('The selected country is blocked by your tax rule.'),
'gift_card_change': gettext_lazy('You cannot change the price of a position that has been used to issue a gift card.'),
}
ItemOperation = namedtuple('ItemOperation', ('position', 'item', 'variation'))
SubeventOperation = namedtuple('SubeventOperation', ('position', 'subevent'))
@@ -1766,22 +1803,16 @@ class OrderChangeManager:
for (i, v), c in selected.items():
n_per_i[i] += c
if sum(selected.values()) > iao.max_count:
# TODO: Proper i18n
# TODO: Proper pluralization
raise OrderError(
error_messages['addon_max_count'],
{
error_messages['addon_max_count'] % {
'base': str(item.name),
'max': iao.max_count,
'cat': str(iao.addon_category.name),
}
)
elif sum(selected.values()) < iao.min_count:
# TODO: Proper i18n
# TODO: Proper pluralization
raise OrderError(
error_messages['addon_min_count'],
{
error_messages['addon_min_count'] % {
'base': str(item.name),
'min': iao.min_count,
'cat': str(iao.addon_category.name),
@@ -1789,8 +1820,7 @@ class OrderChangeManager:
)
elif any(v > 1 for v in n_per_i.values()) and not iao.multi_allowed:
raise OrderError(
error_messages['addon_no_multi'],
{
error_messages['addon_no_multi'] % {
'base': str(item.name),
'cat': str(iao.addon_category.name),
}
@@ -2471,7 +2501,7 @@ def perform_order(self, event: Event, payments: List[dict], positions: List[str]
except LockTimeoutException:
self.retry()
except (MaxRetriesExceededError, LockTimeoutException):
raise OrderError(str(error_messages['busy']))
raise OrderError(error_messages['busy'])
_unset = object()

View File

@@ -525,7 +525,7 @@ class CartAdd(EventViewMixin, CartActionMixin, AsyncAction, View):
return JsonResponse({
'redirect': self.get_error_url(),
'success': False,
'message': _(error_messages['empty'])
'message': str(error_messages['empty'])
})
else:
return redirect(self.get_error_url())
@@ -597,8 +597,6 @@ class RedeemView(NoSearchIndexViewMixin, EventViewMixin, CartMixin, TemplateView
return context
def dispatch(self, request, *args, **kwargs):
from pretix.base.services.cart import error_messages
err = None
v = request.GET.get('voucher')
@@ -652,7 +650,7 @@ class RedeemView(NoSearchIndexViewMixin, EventViewMixin, CartMixin, TemplateView
pass
if err:
messages.error(request, _(err))
messages.error(request, str(err))
return redirect_to_url(self.get_next_url() + "?voucher_invalid")
return super().dispatch(request, *args, **kwargs)

View File

@@ -1389,22 +1389,16 @@ class OrderChange(EventViewMixin, OrderDetailMixin, TemplateView):
selected[i, None] = val, price
if sum(a[0] for a in selected.values()) > category['max_count']:
# TODO: Proper pluralization
raise ValidationError(
_(error_messages['addon_max_count']),
'addon_max_count',
{
error_messages['addon_max_count'] % {
'base': str(form['pos'].item.name),
'max': category['max_count'],
'cat': str(category['category'].name),
}
)
elif sum(a[0] for a in selected.values()) < category['min_count']:
# TODO: Proper pluralization
raise ValidationError(
_(error_messages['addon_min_count']),
'addon_min_count',
{
error_messages['addon_min_count'] % {
'base': str(form['pos'].item.name),
'min': category['min_count'],
'cat': str(category['category'].name),
@@ -1412,9 +1406,7 @@ class OrderChange(EventViewMixin, OrderDetailMixin, TemplateView):
)
elif any(sum(v[0] for k, v in selected.items() if k[0] == i) > 1 for i in category['items']) and not category['multi_allowed']:
raise ValidationError(
_(error_messages['addon_no_multi']),
'addon_no_multi',
{
error_messages['addon_no_multi'] % {
'base': str(form['pos'].item.name),
'cat': str(category['category'].name),
}

View File

@@ -1529,7 +1529,7 @@ class CartTest(CartTestMixin, TestCase):
response = self.client.get('/%s/%s/redeem' % (self.orga.slug, self.event.slug),
{'voucher': v.code},
follow=True)
assert error_messages['voucher_item_not_available'] in response.rendered_content
assert str(error_messages['voucher_item_not_available']) in response.rendered_content
def test_voucher_price(self):
with scopes_disabled():