From 35b6968a7bd51b5bdb030e1e455600eca78cfb07 Mon Sep 17 00:00:00 2001 From: lcduong Date: Tue, 12 Nov 2024 15:41:27 +0700 Subject: [PATCH 1/6] Implement voucher for invoise create/update pages --- ...icket_id_alter_cachedticket_id_and_more.py | 72 ++++++++++++++++ src/pretix/base/models/vouchers.py | 77 +++++++++++++++++ src/pretix/control/forms/admin/__init__.py | 0 src/pretix/control/forms/admin/vouchers.py | 69 +++++++++++++++ src/pretix/control/navigation.py | 6 ++ .../pretixcontrol/admin/vouchers/detail.html | 43 ++++++++++ .../pretixcontrol/admin/vouchers/index.html | 76 +++++++++++++++++ src/pretix/control/urls.py | 3 + src/pretix/control/views/admin.py | 84 ++++++++++++++++++- src/pretix/eventyay_common/context.py | 1 - .../static/eventyay-common/scss/_voucher.scss | 28 +++++++ .../static/pretixcontrol/scss/main.scss | 1 + 12 files changed, 458 insertions(+), 2 deletions(-) create mode 100644 src/pretix/base/migrations/0004_alter_cachedcombinedticket_id_alter_cachedticket_id_and_more.py create mode 100644 src/pretix/control/forms/admin/__init__.py create mode 100644 src/pretix/control/forms/admin/vouchers.py create mode 100644 src/pretix/control/templates/pretixcontrol/admin/vouchers/detail.html create mode 100644 src/pretix/control/templates/pretixcontrol/admin/vouchers/index.html create mode 100644 src/pretix/static/eventyay-common/scss/_voucher.scss diff --git a/src/pretix/base/migrations/0004_alter_cachedcombinedticket_id_alter_cachedticket_id_and_more.py b/src/pretix/base/migrations/0004_alter_cachedcombinedticket_id_alter_cachedticket_id_and_more.py new file mode 100644 index 0000000000..eb0bd1cad5 --- /dev/null +++ b/src/pretix/base/migrations/0004_alter_cachedcombinedticket_id_alter_cachedticket_id_and_more.py @@ -0,0 +1,72 @@ +# Generated by Django 5.1.3 on 2024-11-12 08:04 + +import pretix.base.models.base +import pretix.base.models.vouchers +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("pretixbase", "0003_event_is_video_creation_and_more"), + ] + + operations = [ + migrations.CreateModel( + name="InvoiceVoucher", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False + ), + ), + ( + "code", + models.CharField( + db_index=True, + default=pretix.base.models.vouchers.generate_code, + max_length=255, + unique=True, + ), + ), + ("max_usages", models.PositiveIntegerField(default=1)), + ("redeemed", models.PositiveIntegerField(default=0)), + ( + "budget", + models.DecimalField(decimal_places=2, max_digits=10, null=True), + ), + ( + "valid_until", + models.DateTimeField(blank=True, db_index=True, null=True), + ), + ("price_mode", models.CharField(default="none", max_length=100)), + ( + "value", + models.DecimalField(decimal_places=2, max_digits=10, null=True), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("created_by", models.CharField(default="system", max_length=50)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("updated_by", models.CharField(default="system", max_length=50)), + ( + "limit_events", + models.ManyToManyField( + related_name="invoice_vouchers", to="pretixbase.event" + ), + ), + ( + "limit_organizer", + models.ManyToManyField( + related_name="invoice_vouchers", to="pretixbase.organizer" + ), + ), + ], + options={ + "verbose_name": "Invoice Voucher", + "verbose_name_plural": "Invoice Vouchers", + "ordering": ("code",), + }, + bases=(models.Model, pretix.base.models.base.LoggingMixin), + ), + ] diff --git a/src/pretix/base/models/vouchers.py b/src/pretix/base/models/vouchers.py index 0e458054be..dbda8b3eeb 100644 --- a/src/pretix/base/models/vouchers.py +++ b/src/pretix/base/models/vouchers.py @@ -503,3 +503,80 @@ def budget_used(self): ] ).aggregate(s=Sum(F('price_before_voucher') - F('price')))['s'] or Decimal('0.00') return ops + + +class InvoiceVoucher(LoggedModel): + PRICE_MODES = ( + ('none', _('No effect')), + ('set', _('Set product price to')), + ('subtract', _('Subtract from product price')), + ('percent', _('Reduce product price by (%)')), + ) + code = models.CharField( + verbose_name=_("Voucher code"), + max_length=255, default=generate_code, + db_index=True, + validators=[MinLengthValidator(5)], + unique=True + ) + max_usages = models.PositiveIntegerField( + verbose_name=_("Maximum usages"), + help_text=_("Number of times this voucher can be redeemed."), + default=1 + ) + redeemed = models.PositiveIntegerField( + verbose_name=_("Redeemed"), + default=0 + ) + budget = models.DecimalField( + verbose_name=_("Maximum discount budget"), + help_text=_("This is the maximum monetary amount that will be " + "discounted using this voucher across all usages."), + decimal_places=2, max_digits=10, + null=True, blank=True + ) + valid_until = models.DateTimeField( + blank=True, null=True, db_index=True, + verbose_name=_("Valid until") + ) + price_mode = models.CharField( + verbose_name=_("Price mode"), + max_length=100, + choices=PRICE_MODES, + default='none' + ) + value = models.DecimalField( + verbose_name=_("Voucher value"), + decimal_places=2, max_digits=10, null=True, blank=True, + ) + + limit_events = models.ManyToManyField( + 'Event', + verbose_name=_("Limit to events"), + blank=True, + related_name='invoice_vouchers' + ) + + limit_organizer = models.ManyToManyField( + 'Organizer', + verbose_name=_("Limit to Organizer"), + blank=True, + related_name='invoice_vouchers' + ) + + created_at = models.DateTimeField(auto_now_add=True) + created_by = models.CharField(max_length=50, default="system") + updated_at = models.DateTimeField(auto_now=True) + updated_by = models.CharField(max_length=50, default="system") + + class Meta: + verbose_name = _("Invoice Voucher") + verbose_name_plural = _("Invoice Vouchers") + ordering = ('code', ) + + def is_active(self): + if self.redeemed >= self.max_usages: + return False + if self.valid_until and self.valid_until < now(): + return False + return True diff --git a/src/pretix/control/forms/admin/__init__.py b/src/pretix/control/forms/admin/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/pretix/control/forms/admin/vouchers.py b/src/pretix/control/forms/admin/vouchers.py new file mode 100644 index 0000000000..4ab0acc7c4 --- /dev/null +++ b/src/pretix/control/forms/admin/vouchers.py @@ -0,0 +1,69 @@ +from django import forms +from django_scopes import scopes_disabled +from django.utils.translation import gettext_lazy as _ + +from pretix.base.forms import I18nModelForm +from pretix.base.forms.widgets import SplitDateTimePickerWidget +from pretix.base.models import Event, Organizer +from pretix.base.models.vouchers import InvoiceVoucher +from pretix.control.forms import SplitDateTimeField +from pretix.helpers.models import modelcopy + + +class InvoiceVoucherForm(I18nModelForm): + event_effect = forms.ModelMultipleChoiceField( + queryset=Event.objects.none(), + widget=forms.CheckboxSelectMultiple, + required=False, + label=_("Event effect"), + help_text=_("The voucher will only be valid for the selected events.") + ) + organizer_effect = forms.ModelMultipleChoiceField( + queryset=Organizer.objects.none(), + widget=forms.CheckboxSelectMultiple, + required=False, + label=_("Organizer effect"), + help_text=_("The voucher will be valid for all events under the selected organizers.") + ) + + class Meta: + model = InvoiceVoucher + localized_fields = '__all__' + fields = [ + 'code', 'valid_until', 'value', 'max_usages', 'price_mode', 'budget', 'event_effect', 'organizer_effect' + ] + field_classes = { + 'valid_until': SplitDateTimeField, + } + widgets = { + 'valid_until': SplitDateTimePickerWidget(), + } + + def __init__(self, *args, **kwargs): + instance = kwargs.get('instance') + super().__init__(*args, **kwargs) + if instance: + self.fields['event_effect'].initial = instance.limit_events.all() + self.fields['organizer_effect'].initial = instance.limit_organizer.all() + with scopes_disabled(): + self.fields['event_effect'].queryset = Event.objects.all() + self.fields['organizer_effect'].queryset = Organizer.objects.all() + + def clean(self): + data = super().clean() + return data + + def save(self, commit=True): + instance = super().save(commit=False) + + if commit: + instance.save() + + instance.limit_events.set(self.cleaned_data.get('event_effect', [])) + instance.limit_organizer.set(self.cleaned_data.get('organizer_effect', [])) + + if commit: + self.save_m2m() + + return instance + diff --git a/src/pretix/control/navigation.py b/src/pretix/control/navigation.py index a442457496..bfce91901a 100644 --- a/src/pretix/control/navigation.py +++ b/src/pretix/control/navigation.py @@ -523,6 +523,12 @@ def get_admin_navigation(request): }, ] }, + { + 'label': _('Vouchers'), + 'url': reverse('control:admin.vouchers'), + 'active': 'vouchers' in url.url_name, + 'icon': 'tags', + }, { 'label': _('Global settings'), 'url': reverse('control:admin.global.settings'), diff --git a/src/pretix/control/templates/pretixcontrol/admin/vouchers/detail.html b/src/pretix/control/templates/pretixcontrol/admin/vouchers/detail.html new file mode 100644 index 0000000000..887dde6eb4 --- /dev/null +++ b/src/pretix/control/templates/pretixcontrol/admin/vouchers/detail.html @@ -0,0 +1,43 @@ +{% extends "pretixcontrol/admin/base.html" %} +{% load i18n %} +{% load bootstrap3 %} +{% block title %}{% trans "Voucher" %}{% endblock %} +{% block content %} +

{% trans "Voucher" %}

+ {% if voucher.redeemed %} +
+ {% trans "This voucher already has been used. It is not recommended to modify it." %} +
+ {% endif %} +
+ {% csrf_token %} + {% bootstrap_form_errors form %} +
+
+
+ {% trans "Voucher details" %} + {% bootstrap_field form.code layout="control" %} + {% bootstrap_field form.max_usages layout="control" %} + {% bootstrap_field form.valid_until layout="control" %} +
+ +
+ {% bootstrap_field form.price_mode show_label=False form_group_class="" %} +
+
+ {% bootstrap_field form.value show_label=False form_group_class="" %} +
+
+ {% bootstrap_field form.budget addon_after=currency layout="control" %} + {% bootstrap_field form.event_effect layout="control" %} + {% bootstrap_field form.organizer_effect layout="control" %} +
+
+
+
+ +
+
+{% endblock %} diff --git a/src/pretix/control/templates/pretixcontrol/admin/vouchers/index.html b/src/pretix/control/templates/pretixcontrol/admin/vouchers/index.html new file mode 100644 index 0000000000..6fc6c483e6 --- /dev/null +++ b/src/pretix/control/templates/pretixcontrol/admin/vouchers/index.html @@ -0,0 +1,76 @@ +{% extends "pretixcontrol/admin/base.html" %} +{% load i18n %} +{% load bootstrap3 %} +{% load urlreplace %} +{% load money %} +{% block title %}{% trans "Vouchers" %}{% endblock %} +{% block content %} +

{% trans "Vouchers" %}

+ {% if vouchers|length == 0 %} +
+

+ {% blocktrans trimmed %} + You haven't created any vouchers yet. + {% endblocktrans %} +

+ + {% trans "Create a new voucher" %} +
+ {% else %} +

+ {% trans "Create a new voucher" %} +

+
+ {% csrf_token %} +
+ + + + + + + + + + + {% for v in vouchers %} + + + + + + + {% endfor %} + +
+ {% trans "Voucher code" %} + + + + {% trans "Redemptions" %} + + + + {% trans "Expiry" %} + + +
+ {% if not v.is_active %} + + {% endif %} + {{ v.code }} + {% if not v.is_active %} + + {% endif %} + + {{ v.redeemed }} / {{ v.max_usages }} + {{ v.valid_until|date }} + +
+
+
+ {% include "pretixcontrol/pagination.html" %} + {% endif %} +{% endblock %} diff --git a/src/pretix/control/urls.py b/src/pretix/control/urls.py index a75a1791d2..fb2018b558 100644 --- a/src/pretix/control/urls.py +++ b/src/pretix/control/urls.py @@ -330,6 +330,9 @@ url(r'^global/settings/$', global_settings.GlobalSettingsView.as_view(), name='admin.global.settings'), url(r'^global/update/$', global_settings.UpdateCheckView.as_view(), name='admin.global.update'), url(r'^global/message/$', global_settings.MessageView.as_view(), name='admin.global.message'), + url(r'^vouchers/$', admin.VoucherList.as_view(), name='admin.vouchers'), + url(r'^vouchers/add$', admin.VoucherCreate.as_view(), name='admin.vouchers.add'), + url(r'^vouchers/(?P\d+)/$', admin.VoucherUpdate.as_view(), name='admin.voucher'), ])), url(r'^event/(?P[^/]+)/$', RedirectView.as_view(pattern_name='control:organizer'), name='event.organizerredirect'), ] diff --git a/src/pretix/control/views/admin.py b/src/pretix/control/views/admin.py index 8630e4df2c..da41b149c1 100644 --- a/src/pretix/control/views/admin.py +++ b/src/pretix/control/views/admin.py @@ -1,10 +1,18 @@ +from django.conf import settings +from django.contrib import messages +from django.db import transaction +from django.http import Http404 +from django.urls import reverse, resolve from django.utils.functional import cached_property -from django.views.generic import ListView, TemplateView +from django.views.generic import ListView, TemplateView, CreateView, UpdateView from pretix.base.models import Organizer +from pretix.base.models.vouchers import InvoiceVoucher +from pretix.control.forms.admin.vouchers import InvoiceVoucherForm from pretix.control.forms.filter import OrganizerFilterForm from pretix.control.permissions import AdministratorPermissionRequiredMixin from pretix.control.views import PaginationMixin +from django.utils.translation import gettext_lazy as _ class AdminDashboard(AdministratorPermissionRequiredMixin, TemplateView): @@ -37,3 +45,77 @@ def get_context_data(self, **kwargs): @cached_property def filter_form(self): return OrganizerFilterForm(data=self.request.GET, request=self.request) + + +class VoucherList(PaginationMixin, AdministratorPermissionRequiredMixin, ListView): + model = InvoiceVoucher + context_object_name = 'vouchers' + template_name = 'pretixcontrol/admin/vouchers/index.html' + + def get_queryset(self): + qs = InvoiceVoucher.objects.all() + return qs + + def get_context_data(self, **kwargs): + ctx = super().get_context_data(**kwargs) + return ctx + + def get(self, request, *args, **kwargs): + return super().get(request, *args, **kwargs) + + +class VoucherCreate(AdministratorPermissionRequiredMixin, CreateView): + model = InvoiceVoucher + template_name = 'pretixcontrol/admin/vouchers/detail.html' + context_object_name = 'voucher' + + def get_form_class(self): + form_class = InvoiceVoucherForm + return form_class + + def get_context_data(self, **kwargs): + ctx = super().get_context_data(**kwargs) + ctx['currency'] = settings.DEFAULT_CURRENCY + return ctx + + def get_success_url(self) -> str: + return reverse('control:admin.vouchers') + + def get_form_kwargs(self): + kwargs = super().get_form_kwargs() + return kwargs + + @transaction.atomic + def form_valid(self, form): + req = super().form_valid(form) + return req + + def post(self, request, *args, **kwargs): + return super().post(request, *args, **kwargs) + + +class VoucherUpdate(AdministratorPermissionRequiredMixin, UpdateView): + model = InvoiceVoucher + template_name = 'pretixcontrol/admin/vouchers/detail.html' + context_object_name = 'voucher' + + def get_form_class(self): + form_class = InvoiceVoucherForm + return form_class + + def get_object(self, queryset=None) -> InvoiceVoucherForm: + url = resolve(self.request.path_info) + try: + return InvoiceVoucher.objects.get( + id=url.kwargs['voucher'] + ) + except InvoiceVoucher.DoesNotExist: + raise Http404(_("The requested voucher does not exist.")) + + @transaction.atomic + def form_valid(self, form): + messages.success(self.request, _('Your changes have been saved.')) + return super().form_valid(form) + + def get_success_url(self) -> str: + return reverse('control:admin.vouchers') diff --git a/src/pretix/eventyay_common/context.py b/src/pretix/eventyay_common/context.py index 44c15de827..09d5bddae1 100644 --- a/src/pretix/eventyay_common/context.py +++ b/src/pretix/eventyay_common/context.py @@ -61,7 +61,6 @@ def get_global_navigation(request): url = request.resolver_match if not url: return [] - request.user.has_active_staff_session(request.session.session_key) nav = [ { 'label': _('Dashboard'), diff --git a/src/pretix/static/eventyay-common/scss/_voucher.scss b/src/pretix/static/eventyay-common/scss/_voucher.scss new file mode 100644 index 0000000000..97e8143504 --- /dev/null +++ b/src/pretix/static/eventyay-common/scss/_voucher.scss @@ -0,0 +1,28 @@ +#id_event_effect, #id_organizer_effect { + width: 100%; + max-height: 200px; + border: 1px solid #ccc; + border-radius: 5px; + padding: 8px; + + &::-webkit-scrollbar { + width: 8px; + } + + &::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 5px; + } + + &::-webkit-scrollbar-thumb { + background: #888; + border-radius: 5px; + } + + &::-webkit-scrollbar-thumb:hover { + background: #555; + } + + overflow-y: auto; + overflow-x: hidden; +} diff --git a/src/pretix/static/pretixcontrol/scss/main.scss b/src/pretix/static/pretixcontrol/scss/main.scss index 677e549866..c159463ebb 100644 --- a/src/pretix/static/pretixcontrol/scss/main.scss +++ b/src/pretix/static/pretixcontrol/scss/main.scss @@ -16,6 +16,7 @@ @import "../../select2/select2.scss"; @import "../../select2/select2_bootstrap.scss"; @import "../../colorpicker/bootstrap-colorpicker.scss"; +@import "../../eventyay-common/scss/_voucher.scss"; /* See https://github.com/pretix/pretix/pull/761 */ .bootstrap-datetimepicker-widget table td span { From c7b246b85cd206290957231ccddb06ab5a872b0c Mon Sep 17 00:00:00 2001 From: lcduong Date: Tue, 12 Nov 2024 16:34:26 +0700 Subject: [PATCH 2/6] Implement invoice voucher delete view --- src/pretix/base/models/vouchers.py | 3 ++ .../pretixcontrol/admin/vouchers/delete.html | 19 +++++++++ .../pretixcontrol/admin/vouchers/index.html | 2 +- src/pretix/control/urls.py | 2 + src/pretix/control/views/admin.py | 42 +++++++++++++++++-- 5 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 src/pretix/control/templates/pretixcontrol/admin/vouchers/delete.html diff --git a/src/pretix/base/models/vouchers.py b/src/pretix/base/models/vouchers.py index dbda8b3eeb..1be8f7f062 100644 --- a/src/pretix/base/models/vouchers.py +++ b/src/pretix/base/models/vouchers.py @@ -574,6 +574,9 @@ class Meta: verbose_name_plural = _("Invoice Vouchers") ordering = ('code', ) + def __str__(self): + return self.code + def is_active(self): if self.redeemed >= self.max_usages: return False diff --git a/src/pretix/control/templates/pretixcontrol/admin/vouchers/delete.html b/src/pretix/control/templates/pretixcontrol/admin/vouchers/delete.html new file mode 100644 index 0000000000..bc0e0d8d49 --- /dev/null +++ b/src/pretix/control/templates/pretixcontrol/admin/vouchers/delete.html @@ -0,0 +1,19 @@ +{% extends "pretixcontrol/admin/base.html" %} +{% load i18n %} +{% load bootstrap3 %} +{% block title %}{% trans "Delete voucher" %}{% endblock %} +{% block content %} +

{% trans "Delete voucher" %}

+
+ {% csrf_token %} +

{% blocktrans %}Are you sure you want to delete the voucher {{ invoice_voucher }}?{% endblocktrans %}

+
+ + {% trans "Cancel" %} + + +
+
+{% endblock %} diff --git a/src/pretix/control/templates/pretixcontrol/admin/vouchers/index.html b/src/pretix/control/templates/pretixcontrol/admin/vouchers/index.html index 6fc6c483e6..9d2779d476 100644 --- a/src/pretix/control/templates/pretixcontrol/admin/vouchers/index.html +++ b/src/pretix/control/templates/pretixcontrol/admin/vouchers/index.html @@ -63,7 +63,7 @@

{% trans "Vouchers" %}

{{ v.valid_until|date }} - + {% endfor %} diff --git a/src/pretix/control/urls.py b/src/pretix/control/urls.py index fb2018b558..b34766d721 100644 --- a/src/pretix/control/urls.py +++ b/src/pretix/control/urls.py @@ -333,6 +333,8 @@ url(r'^vouchers/$', admin.VoucherList.as_view(), name='admin.vouchers'), url(r'^vouchers/add$', admin.VoucherCreate.as_view(), name='admin.vouchers.add'), url(r'^vouchers/(?P\d+)/$', admin.VoucherUpdate.as_view(), name='admin.voucher'), + url(r'^vouchers/(?P\d+)/delete$', admin.VoucherDelete.as_view(), + name='admin.voucher.delete'), ])), url(r'^event/(?P[^/]+)/$', RedirectView.as_view(pattern_name='control:organizer'), name='event.organizerredirect'), ] diff --git a/src/pretix/control/views/admin.py b/src/pretix/control/views/admin.py index da41b149c1..8185b32315 100644 --- a/src/pretix/control/views/admin.py +++ b/src/pretix/control/views/admin.py @@ -1,10 +1,10 @@ from django.conf import settings from django.contrib import messages from django.db import transaction -from django.http import Http404 +from django.http import Http404, HttpResponseRedirect from django.urls import reverse, resolve from django.utils.functional import cached_property -from django.views.generic import ListView, TemplateView, CreateView, UpdateView +from django.views.generic import ListView, TemplateView, CreateView, UpdateView, DeleteView from pretix.base.models import Organizer from pretix.base.models.vouchers import InvoiceVoucher @@ -104,10 +104,9 @@ def get_form_class(self): return form_class def get_object(self, queryset=None) -> InvoiceVoucherForm: - url = resolve(self.request.path_info) try: return InvoiceVoucher.objects.get( - id=url.kwargs['voucher'] + id=self.kwargs['voucher'] ) except InvoiceVoucher.DoesNotExist: raise Http404(_("The requested voucher does not exist.")) @@ -119,3 +118,38 @@ def form_valid(self, form): def get_success_url(self) -> str: return reverse('control:admin.vouchers') + + +class VoucherDelete(AdministratorPermissionRequiredMixin, DeleteView): + model = InvoiceVoucher + template_name = 'pretixcontrol/admin/vouchers/delete.html' + context_object_name = 'invoice_voucher' + + def get_object(self, queryset=None) -> InvoiceVoucher: + try: + return InvoiceVoucher.objects.get( + id=self.kwargs['voucher'] + ) + except InvoiceVoucher.DoesNotExist: + raise Http404(_("The requested voucher does not exist.")) + + def get(self, request, *args, **kwargs): + if self.get_object().redeemed > 0: + messages.error(request, _('A voucher can not be deleted if it already has been redeemed.')) + return HttpResponseRedirect(self.get_success_url()) + return super().get(request, *args, **kwargs) + + @transaction.atomic + def form_valid(self, form): + self.object = self.get_object() + success_url = self.get_success_url() + + if self.object.redeemed > 0: + messages.error(self.request, _('A voucher can not be deleted if it already has been redeemed.')) + else: + self.object.delete() + messages.success(self.request, _('The selected voucher has been deleted.')) + return HttpResponseRedirect(success_url) + + def get_success_url(self) -> str: + return reverse('control:admin.vouchers') From bb8fcfe64df2e1d7aa30fdb326c229cc978db051 Mon Sep 17 00:00:00 2001 From: lcduong Date: Wed, 13 Nov 2024 17:44:33 +0700 Subject: [PATCH 3/6] show currency in invoice voucher page update --- src/pretix/control/views/admin.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/pretix/control/views/admin.py b/src/pretix/control/views/admin.py index 8185b32315..2d97ccedf6 100644 --- a/src/pretix/control/views/admin.py +++ b/src/pretix/control/views/admin.py @@ -111,6 +111,11 @@ def get_object(self, queryset=None) -> InvoiceVoucherForm: except InvoiceVoucher.DoesNotExist: raise Http404(_("The requested voucher does not exist.")) + def get_context_data(self, **kwargs): + ctx = super().get_context_data(**kwargs) + ctx['currency'] = settings.DEFAULT_CURRENCY + return ctx + @transaction.atomic def form_valid(self, form): messages.success(self.request, _('Your changes have been saved.')) From 6a2cdf7dbb3c200140eb333689cdcfdf244e64ab Mon Sep 17 00:00:00 2001 From: lcduong Date: Thu, 12 Dec 2024 09:31:31 +0700 Subject: [PATCH 4/6] optimize import --- src/pretix/control/views/admin.py | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/pretix/control/views/admin.py b/src/pretix/control/views/admin.py index b9def0f5ba..2071b45cd1 100644 --- a/src/pretix/control/views/admin.py +++ b/src/pretix/control/views/admin.py @@ -1,32 +1,22 @@ -from django.conf import settings -from django.contrib import messages -from django.db import transaction -from django.http import Http404, HttpResponseRedirect -from django.urls import reverse, resolve -from django.utils.functional import cached_property -from django.views.generic import ListView, TemplateView, CreateView, UpdateView, DeleteView - -from pretix.base.models import Organizer -from pretix.base.models.vouchers import InvoiceVoucher -from pretix.control.forms.admin.vouchers import InvoiceVoucherForm -from pretix.control.forms.filter import OrganizerFilterForm -from pretix.control.permissions import AdministratorPermissionRequiredMixin -from pretix.control.views import PaginationMixin -from django.utils.translation import gettext_lazy as _ from zoneinfo import ZoneInfo from cron_descriptor import Options, get_description from django.conf import settings from django.contrib import messages +from django.http import Http404 from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404 from django.urls import reverse from django.utils.formats import date_format from django.utils.functional import cached_property +from django.utils.translation import gettext_lazy as _ +from django.views.generic import CreateView, UpdateView, DeleteView from django.views.generic import ListView, TemplateView from django_celery_beat.models import PeriodicTask, PeriodicTasks from pretix.base.models import Organizer +from pretix.base.models.vouchers import InvoiceVoucher +from pretix.control.forms.admin.vouchers import InvoiceVoucherForm from pretix.control.forms.filter import OrganizerFilterForm, TaskFilterForm from pretix.control.permissions import AdministratorPermissionRequiredMixin from pretix.control.views import PaginationMixin From 1bed56b49213710f3b023ef596afa6e3383df735 Mon Sep 17 00:00:00 2001 From: lcduong Date: Thu, 12 Dec 2024 09:38:14 +0700 Subject: [PATCH 5/6] change migration file name --- ...chedticket_id_and_more.py => 0005_create_invoice_voucher.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/pretix/base/migrations/{0004_alter_cachedcombinedticket_id_alter_cachedticket_id_and_more.py => 0005_create_invoice_voucher.py} (97%) diff --git a/src/pretix/base/migrations/0004_alter_cachedcombinedticket_id_alter_cachedticket_id_and_more.py b/src/pretix/base/migrations/0005_create_invoice_voucher.py similarity index 97% rename from src/pretix/base/migrations/0004_alter_cachedcombinedticket_id_alter_cachedticket_id_and_more.py rename to src/pretix/base/migrations/0005_create_invoice_voucher.py index eb0bd1cad5..33abf48829 100644 --- a/src/pretix/base/migrations/0004_alter_cachedcombinedticket_id_alter_cachedticket_id_and_more.py +++ b/src/pretix/base/migrations/0005_create_invoice_voucher.py @@ -8,7 +8,7 @@ class Migration(migrations.Migration): dependencies = [ - ("pretixbase", "0003_event_is_video_creation_and_more"), + ("pretixbase", "0004_create_billing_invoice"), ] operations = [ From 6e1ec95fc7d88f01e71a9a3e33a6ba49a310f167 Mon Sep 17 00:00:00 2001 From: odkhang Date: Tue, 17 Dec 2024 17:40:39 +0700 Subject: [PATCH 6/6] fix isort, flake8 --- ..._invoice_voucher.py => 0006_create_invoice_voucher.py} | 5 +++-- src/pretix/control/forms/admin/vouchers.py | 4 +--- src/pretix/control/views/admin.py | 8 ++++---- 3 files changed, 8 insertions(+), 9 deletions(-) rename src/pretix/base/migrations/{0005_create_invoice_voucher.py => 0006_create_invoice_voucher.py} (97%) diff --git a/src/pretix/base/migrations/0005_create_invoice_voucher.py b/src/pretix/base/migrations/0006_create_invoice_voucher.py similarity index 97% rename from src/pretix/base/migrations/0005_create_invoice_voucher.py rename to src/pretix/base/migrations/0006_create_invoice_voucher.py index 33abf48829..9fb93c033f 100644 --- a/src/pretix/base/migrations/0005_create_invoice_voucher.py +++ b/src/pretix/base/migrations/0006_create_invoice_voucher.py @@ -1,14 +1,15 @@ # Generated by Django 5.1.3 on 2024-11-12 08:04 +from django.db import migrations, models + import pretix.base.models.base import pretix.base.models.vouchers -from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("pretixbase", "0004_create_billing_invoice"), + ("pretixbase", "0005_page_alter_cachedcombinedticket_id_and_more"), ] operations = [ diff --git a/src/pretix/control/forms/admin/vouchers.py b/src/pretix/control/forms/admin/vouchers.py index 4ab0acc7c4..3b02d49f7f 100644 --- a/src/pretix/control/forms/admin/vouchers.py +++ b/src/pretix/control/forms/admin/vouchers.py @@ -1,13 +1,12 @@ from django import forms -from django_scopes import scopes_disabled from django.utils.translation import gettext_lazy as _ +from django_scopes import scopes_disabled from pretix.base.forms import I18nModelForm from pretix.base.forms.widgets import SplitDateTimePickerWidget from pretix.base.models import Event, Organizer from pretix.base.models.vouchers import InvoiceVoucher from pretix.control.forms import SplitDateTimeField -from pretix.helpers.models import modelcopy class InvoiceVoucherForm(I18nModelForm): @@ -66,4 +65,3 @@ def save(self, commit=True): self.save_m2m() return instance - diff --git a/src/pretix/control/views/admin.py b/src/pretix/control/views/admin.py index 2071b45cd1..ff4b204a50 100644 --- a/src/pretix/control/views/admin.py +++ b/src/pretix/control/views/admin.py @@ -3,15 +3,15 @@ from cron_descriptor import Options, get_description from django.conf import settings from django.contrib import messages -from django.http import Http404 -from django.http import HttpResponseRedirect +from django.http import Http404, HttpResponseRedirect from django.shortcuts import get_object_or_404 from django.urls import reverse from django.utils.formats import date_format from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ -from django.views.generic import CreateView, UpdateView, DeleteView -from django.views.generic import ListView, TemplateView +from django.views.generic import ( + CreateView, DeleteView, ListView, TemplateView, UpdateView, +) from django_celery_beat.models import PeriodicTask, PeriodicTasks from pretix.base.models import Organizer