diff --git a/src/pretix/_base_settings.py b/src/pretix/_base_settings.py index 75daedb6f..c77e2060f 100644 --- a/src/pretix/_base_settings.py +++ b/src/pretix/_base_settings.py @@ -235,7 +235,12 @@ COMPRESS_FILTERS = { ) } -CURRENCIES = list(currencies) +CURRENCIES = [ + c for c in currencies + if c.alpha_3 not in { + 'XAG', 'XAU', 'XBA', 'XBB', 'XBC', 'XBD', 'XDR', 'XPD', 'XPT', 'XSU', 'XTS', 'XUA', + } +] CURRENCY_PLACES = { # default is 2 'BIF': 0, diff --git a/src/pretix/api/serializers/order.py b/src/pretix/api/serializers/order.py index 345c9d717..5c98c908b 100644 --- a/src/pretix/api/serializers/order.py +++ b/src/pretix/api/serializers/order.py @@ -1586,6 +1586,9 @@ class OrderCreateSerializer(I18nAwareModelSerializer): if order.total == Decimal('0.00') and validated_data.get('status') == Order.STATUS_PAID and not payment_provider: payment_provider = 'free' + if order.total != Decimal('0.00') and order.event.currency == "XXX": + raise ValidationError('Paid products not supported without a valid currency.') + if order.total == Decimal('0.00') and validated_data.get('status') != Order.STATUS_PAID and not validated_data.get('require_approval'): order.status = Order.STATUS_PAID order.save() diff --git a/src/pretix/base/migrations/0263_auto_20240409_0732.py b/src/pretix/base/migrations/0263_auto_20240409_0732.py new file mode 100644 index 000000000..5d4c7ef68 --- /dev/null +++ b/src/pretix/base/migrations/0263_auto_20240409_0732.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2.10 on 2024-04-09 07:32 + +from django.db import migrations + + +def change_currencies(apps, schema_editor): + Event = apps.get_model("pretixbase", "Event") + Event.objects.filter( + currency__in={ + 'XAG', 'XAU', 'XBA', 'XBB', 'XBC', 'XBD', 'XDR', 'XPD', 'XPT', 'XSU', 'XTS', 'XUA', + } + ).update(currency='XXX') + + + +class Migration(migrations.Migration): + + dependencies = [ + ("pretixbase", "0262_subevent_comment"), + ] + + operations = [ + migrations.RunPython( + change_currencies, migrations.RunPython.noop + ) + ] diff --git a/src/pretix/base/models/event.py b/src/pretix/base/models/event.py index aac35cb74..a37941380 100644 --- a/src/pretix/base/models/event.py +++ b/src/pretix/base/models/event.py @@ -1237,6 +1237,9 @@ class Event(EventMixin, LoggedModel): if self.has_paid_things and not self.has_payment_provider: issues.append(_('You have configured at least one paid product but have not enabled any payment methods.')) + if self.has_paid_things and self.currency == "XXX": + issues.append(_('You have configured at least one paid product but have not configured a currency.')) + if not self.quotas.exists(): issues.append(_('You need to configure at least one quota to sell anything.')) diff --git a/src/pretix/base/services/orders.py b/src/pretix/base/services/orders.py index 8e5a24d93..a7a24f5ae 100644 --- a/src/pretix/base/services/orders.py +++ b/src/pretix/base/services/orders.py @@ -199,6 +199,7 @@ error_messages = { ), 'addon_no_multi': gettext_lazy('You can select every add-on from the category %(cat)s for the product %(base)s at most once.'), 'addon_already_checked_in': gettext_lazy('You cannot remove the position %(addon)s since it has already been checked in.'), + 'currency_XXX': gettext_lazy('Paid products not supported without a valid currency.'), } logger = logging.getLogger(__name__) @@ -1129,6 +1130,9 @@ def _perform_order(event: Event, payment_requests: List[dict], position_ids: Lis id__in=position_ids, event=event ) + if shown_total is not None and Decimal(shown_total) > Decimal("0.00") and event.currency == "XXX": + raise OrderError(error_messages['currency_XXX']) + validate_order.send( event, payment_provider=payment_requests[0]['provider'] if payment_requests else None, # only for backwards compatibility @@ -2125,6 +2129,9 @@ class OrderChangeManager: ) def _check_paid_to_free(self): + if self.event.currency == 'XXX' and self.order.total + self._totaldiff > Decimal("0.00"): + raise OrderError(error_messages['currency_XXX']) + if self.order.total == 0 and (self._totaldiff < 0 or (self.split_order and self.split_order.total > 0)) and not self.order.require_approval: if not self.order.fees.exists() and not self.order.positions.exists(): # The order is completely empty now, so we cancel it.