diff --git a/src/pretix/base/models/orders.py b/src/pretix/base/models/orders.py index 01844c82f8..506aef269c 100644 --- a/src/pretix/base/models/orders.py +++ b/src/pretix/base/models/orders.py @@ -628,9 +628,14 @@ class Order(LockModel, LoggedModel): def set_expires(self, now_dt=None, subevents=None): now_dt = now_dt or now() tz = ZoneInfo(self.event.settings.timezone) - mode = self.event.settings.get('payment_term_mode') + + sales_channel_suffix = "_" + self.sales_channel.identifier.replace(".", "_") + if not (mode := self.event.settings.get(f'payment_term_mode{sales_channel_suffix}')): + mode = self.event.settings.get('payment_term_mode') + sales_channel_suffix = "" + if mode == 'days': - exp_by_date = now_dt.astimezone(tz) + timedelta(days=self.event.settings.get('payment_term_days', as_type=int)) + exp_by_date = now_dt.astimezone(tz) + timedelta(days=self.event.settings.get(f'payment_term_days{sales_channel_suffix}', as_type=int)) exp_by_date = exp_by_date.astimezone(tz).replace(hour=23, minute=59, second=59, microsecond=0) if self.event.settings.get('payment_term_weekdays'): if exp_by_date.weekday() == 5: @@ -638,7 +643,7 @@ class Order(LockModel, LoggedModel): elif exp_by_date.weekday() == 6: exp_by_date += timedelta(days=1) elif mode == 'minutes': - exp_by_date = now_dt.astimezone(tz) + timedelta(minutes=self.event.settings.get('payment_term_minutes', as_type=int)) + exp_by_date = now_dt.astimezone(tz) + timedelta(minutes=self.event.settings.get(f'payment_term_minutes{sales_channel_suffix}', as_type=int)) else: raise ValueError("'payment_term_mode' has an invalid value '{}'.".format(mode)) diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py index ae679d186c..d9dcfbb65e 100644 --- a/src/pretix/base/settings.py +++ b/src/pretix/base/settings.py @@ -979,12 +979,12 @@ DEFAULTS = { 'form_class': forms.IntegerField, 'serializer_class': serializers.IntegerField, 'write_permission': 'event.settings.payment:write', - 'form_kwargs': dict( + 'form_kwargs': lambda suffix="", parent=0: dict( label=_('Payment term in days'), widget=forms.NumberInput( attrs={ - 'data-display-dependency': '#id_payment_term_mode_0', - 'data-required-if': '#id_payment_term_mode_0' + 'data-display-dependency': f'#id_payment_term_mode{suffix}_{parent}', + 'data-required-if': f'#id_payment_term_mode{suffix}_{parent}' }, ), help_text=_("The number of days after placing an order the user has to pay to preserve their reservation. If " @@ -1023,7 +1023,7 @@ DEFAULTS = { 'form_class': forms.IntegerField, 'serializer_class': serializers.IntegerField, 'write_permission': 'event.settings.payment:write', - 'form_kwargs': dict( + 'form_kwargs': lambda suffix="", parent=0: dict( label=_('Payment term in minutes'), help_text=_("The number of minutes after placing an order the user has to pay to preserve their reservation. " "Only use this if you exclusively offer real-time payment methods. Please note that for technical reasons, " @@ -1032,8 +1032,8 @@ DEFAULTS = { MaxValueValidator(1440)], widget=forms.NumberInput( attrs={ - 'data-display-dependency': '#id_payment_term_mode_1', - 'data-required-if': '#id_payment_term_mode_1' + 'data-display-dependency': f'#id_payment_term_mode{suffix}_{parent}', + 'data-required-if': f'#id_payment_term_mode{suffix}_{parent}' }, ), ), diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py index 82087bbe67..9ec45c9876 100644 --- a/src/pretix/control/forms/event.py +++ b/src/pretix/control/forms/event.py @@ -856,6 +856,50 @@ class PaymentSettingsForm(EventSettingsValidationMixin, SettingsForm): 'tax_rule_payment', ] + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.term_channel_fields = {} + for c in self.obj.organizer.sales_channels.all(): + if c.type_instance.payment_restrictions_supported and c.identifier != "web": + # At the moment, it seems sufficient to allow this for the same channel types as other payment settings + # We can always introduce more flags later if needed + suffix = '_' + c.identifier.replace(".", "_") + self.term_channel_fields[c] = [ + 'payment_term_mode' + suffix, + 'payment_term_days' + suffix, + 'payment_term_minutes' + suffix, + ] + self.fields['payment_term_mode' + suffix] = DEFAULTS['payment_term_mode']['form_class']( + label=_("Payment term"), + widget=forms.RadioSelect, + required=False, + choices=( + ('', _("same as above")), + ('days', _("different payment term in days")), + ('minutes', _("different payment term in minutes")) + ), + ) + self.fields['payment_term_days' + suffix] = DEFAULTS['payment_term_days']['form_class']( + required=False, + **DEFAULTS['payment_term_days']['form_kwargs'](suffix, 1), + ) + self.fields['payment_term_minutes' + suffix] = DEFAULTS['payment_term_minutes']['form_class']( + required=False, + **DEFAULTS['payment_term_minutes']['form_kwargs'](suffix, 2), + ) + + def clean(self): + data = super().clean() + for c in self.term_channel_fields.keys(): + suffix = '_' + c.identifier.replace(".", "_") + mode = self.cleaned_data.get(f'payment_term_mode{suffix}') + if mode == 'days' and self.cleaned_data.get(f'payment_term_days{suffix}') is None: + raise ValidationError({f'payment_term_days{suffix}': _("This field is required.")}) + if mode == 'minutes' and self.cleaned_data.get(f'payment_term_minutes{suffix}') is None: + raise ValidationError({f'payment_term_minutes{suffix}': _("This field is required.")}) + return data + def clean_payment_term_days(self): value = self.cleaned_data.get('payment_term_days') if self.cleaned_data.get('payment_term_mode') == 'days' and value is None: diff --git a/src/pretix/control/templates/pretixcontrol/event/payment.html b/src/pretix/control/templates/pretixcontrol/event/payment.html index fef1ae3593..55a53f35f8 100644 --- a/src/pretix/control/templates/pretixcontrol/event/payment.html +++ b/src/pretix/control/templates/pretixcontrol/event/payment.html @@ -2,9 +2,10 @@ {% load i18n %} {% load static %} {% load bootstrap3 %} +{% load getitem %} {% block inside %}