diff --git a/src/pretix/base/models/items.py b/src/pretix/base/models/items.py index ae250799cd..dc4479252a 100644 --- a/src/pretix/base/models/items.py +++ b/src/pretix/base/models/items.py @@ -309,6 +309,10 @@ class ItemVariation(models.Model): def __str__(self): return str(self.value) + @property + def price(self): + return self.default_price if self.default_price is not None else self.item.default_price + def delete(self, *args, **kwargs): super().delete(*args, **kwargs) if self.item: diff --git a/src/pretix/base/models/orders.py b/src/pretix/base/models/orders.py index 1e4f9641fb..d8db2e22fb 100644 --- a/src/pretix/base/models/orders.py +++ b/src/pretix/base/models/orders.py @@ -190,6 +190,10 @@ class Order(LoggedModel): """ return '{event}-{code}'.format(event=self.event.slug.upper(), code=self.code) + @property + def changable(self): + return self.status in (Order.STATUS_PAID, Order.STATUS_PENDING) + def save(self, *args, **kwargs): if not self.code: self.assign_code() diff --git a/src/pretix/base/services/orders.py b/src/pretix/base/services/orders.py index ac06c849df..81d468a770 100644 --- a/src/pretix/base/services/orders.py +++ b/src/pretix/base/services/orders.py @@ -480,9 +480,11 @@ class OrderChangeManager: 'quota': _('The quota {name} does not have enough capacity left to perform the 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.'), - 'not_pending': _('Only pending orders can be changed.'), + 'not_pending_or_paid': _('Only pending or paid orders can be changed.'), 'paid_to_free_exceeded': _('This operation would make the order free and therefore immediately paid, however ' 'no quota is available.'), + 'paid_price_change': _('Currently, paid orders can only be changed in a way that does not change the total ' + 'price of the order as partial payments or refunds are not yet supported.') } ItemOperation = namedtuple('ItemOperation', ('position', 'item', 'variation', 'price')) PriceOperation = namedtuple('PriceOperation', ('position', 'price')) @@ -498,8 +500,7 @@ class OrderChangeManager: def change_item(self, position: OrderPosition, item: Item, variation: Optional[ItemVariation]): if (not variation and item.has_variations) or (variation and variation.item_id != item.pk): raise OrderError(self.error_messages['product_without_variation']) - price = item.default_price if variation is None else ( - variation.default_price if variation.default_price is not None else item.default_price) + price = item.default_price if variation is None else variation.price if not price: raise OrderError(self.error_messages['product_invalid']) self._totaldiff = price - position.price @@ -528,6 +529,10 @@ class OrderChangeManager: if self.order.total == Decimal('0.00') and self._totaldiff > 0: raise OrderError(self.error_messages['free_to_paid']) + def _check_paid_price_change(self): + if self.order.status == Order.STATUS_PAID and self._totaldiff != 0: + raise OrderError(self.error_messages['paid_price_change']) + def _check_paid_to_free(self): if self.order.total == 0: try: @@ -624,9 +629,10 @@ class OrderChangeManager: return with transaction.atomic(): with self.order.event.lock(): - if self.order.status != Order.STATUS_PENDING: - raise OrderError(self.error_messages['not_pending']) + if self.order.status not in (Order.STATUS_PENDING, Order.STATUS_PAID): + raise OrderError(self.error_messages['not_pending_or_paid']) self._check_free_to_paid() + self._check_paid_price_change() self._check_quotas() self._check_complete_cancel() self._perform_operations() diff --git a/src/pretix/control/forms/orders.py b/src/pretix/control/forms/orders.py index 173f7dbe69..313f5a44e7 100644 --- a/src/pretix/control/forms/orders.py +++ b/src/pretix/control/forms/orders.py @@ -1,6 +1,7 @@ from django import forms from django.core.exceptions import ValidationError from django.db import models +from django.utils.formats import localize from django.utils.timezone import now from django.utils.translation import ugettext_lazy as _ @@ -94,9 +95,12 @@ class OrderPositionChangeForm(forms.Form): variations = list(i.variations.all()) if variations: for v in variations: - choices.append(('%d-%d' % (i.pk, v.pk), '%s – %s' % (pname, v.value))) + choices.append(('%d-%d' % (i.pk, v.pk), + '%s – %s (%s %s)' % (pname, v.value, localize(v.price), + instance.order.event.currency))) else: - choices.append((str(i.pk), pname)) + choices.append((str(i.pk), '%s (%s %s)' % (pname, localize(i.default_price), + instance.order.event.currency))) self.fields['itemvar'].choices = choices def clean(self): diff --git a/src/pretix/control/templates/pretixcontrol/order/index.html b/src/pretix/control/templates/pretixcontrol/order/index.html index 0453c4830b..c141504caa 100644 --- a/src/pretix/control/templates/pretixcontrol/order/index.html +++ b/src/pretix/control/templates/pretixcontrol/order/index.html @@ -144,7 +144,7 @@