mirror of
https://github.com/pretix/pretix.git
synced 2026-05-13 16:33:59 +00:00
[DRAFT] voucher bulk update
This commit is contained in:
@@ -40,6 +40,7 @@ from django import forms
|
|||||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||||
from django.core.validators import EmailValidator
|
from django.core.validators import EmailValidator
|
||||||
from django.db.models.functions import Upper
|
from django.db.models.functions import Upper
|
||||||
|
from django.forms.utils import ErrorDict
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django_scopes.forms import SafeModelChoiceField
|
from django_scopes.forms import SafeModelChoiceField
|
||||||
@@ -265,6 +266,49 @@ class VoucherForm(I18nModelForm):
|
|||||||
return super().save(commit)
|
return super().save(commit)
|
||||||
|
|
||||||
|
|
||||||
|
class VoucherBulkEditForm(VoucherForm):
|
||||||
|
# TODO: clean quota changes!
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.mixed_values = kwargs.pop('mixed_values')
|
||||||
|
self.queryset = kwargs.pop('queryset')
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
def save(self, commit=True):
|
||||||
|
objs = list(self.queryset)
|
||||||
|
fields = set()
|
||||||
|
|
||||||
|
for k in self.fields:
|
||||||
|
cb_val = self.prefix + k
|
||||||
|
if cb_val not in self.data.getlist('_bulk'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
fields.add(k)
|
||||||
|
for obj in objs:
|
||||||
|
if k == 'itemvar':
|
||||||
|
selected_items = set(list(self.event.items.filter(id__in=[
|
||||||
|
i.split('-')[0] for i in self.cleaned_data['itemvars']
|
||||||
|
])))
|
||||||
|
selected_variations = list(ItemVariation.objects.filter(item__event=self.event, id__in=[
|
||||||
|
i.split('-')[1] for i in self.cleaned_data['itemvars'] if '-' in i
|
||||||
|
]))
|
||||||
|
obj.items.set(selected_items)
|
||||||
|
obj.variations.set(selected_variations)
|
||||||
|
else:
|
||||||
|
setattr(obj, k, self.cleaned_data[k])
|
||||||
|
|
||||||
|
fields = [f for f in fields if f != 'itemvars']
|
||||||
|
if fields:
|
||||||
|
Voucher.objects.bulk_update(objs, fields, 200)
|
||||||
|
|
||||||
|
def full_clean(self):
|
||||||
|
if len(self.data) == 0:
|
||||||
|
# form wasn't submitted
|
||||||
|
self._errors = ErrorDict()
|
||||||
|
return
|
||||||
|
super().full_clean()
|
||||||
|
|
||||||
|
|
||||||
class VoucherBulkForm(VoucherForm):
|
class VoucherBulkForm(VoucherForm):
|
||||||
codes = forms.CharField(
|
codes = forms.CharField(
|
||||||
widget=forms.Textarea,
|
widget=forms.Textarea,
|
||||||
|
|||||||
@@ -223,6 +223,10 @@
|
|||||||
<i class="fa fa-trash" aria-hidden="true"></i>
|
<i class="fa fa-trash" aria-hidden="true"></i>
|
||||||
{% trans "Delete selected" %}
|
{% trans "Delete selected" %}
|
||||||
</button>
|
</button>
|
||||||
|
<button type="submit" class="btn btn-primary btn-save" name="action" value="edit"
|
||||||
|
formaction="{% url "control:event.subevents.bulkedit" organizer=request.event.organizer.slug event=request.event.slug %}">
|
||||||
|
<i class="fa fa-edit"></i>{% trans "Edit selected" %}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -379,6 +379,7 @@ urlpatterns = [
|
|||||||
re_path(r'^vouchers/bulk_add$', vouchers.VoucherBulkCreate.as_view(), name='event.vouchers.bulk'),
|
re_path(r'^vouchers/bulk_add$', vouchers.VoucherBulkCreate.as_view(), name='event.vouchers.bulk'),
|
||||||
re_path(r'^vouchers/bulk_add/mail_preview$', vouchers.VoucherBulkMailPreview.as_view(), name='event.vouchers.bulk.mail_preview'),
|
re_path(r'^vouchers/bulk_add/mail_preview$', vouchers.VoucherBulkMailPreview.as_view(), name='event.vouchers.bulk.mail_preview'),
|
||||||
re_path(r'^vouchers/bulk_action$', vouchers.VoucherBulkAction.as_view(), name='event.vouchers.bulkaction'),
|
re_path(r'^vouchers/bulk_action$', vouchers.VoucherBulkAction.as_view(), name='event.vouchers.bulkaction'),
|
||||||
|
#re_path(r'^vouchers/bulk_edit$', vouchers.VoucherBulkUpdateView.as_view(), name='event.vouchers.bulkedit'),
|
||||||
re_path(r'^vouchers/import/$', modelimport.VoucherImportView.as_view(), name='event.vouchers.import'),
|
re_path(r'^vouchers/import/$', modelimport.VoucherImportView.as_view(), name='event.vouchers.import'),
|
||||||
re_path(r'^vouchers/import/(?P<file>[^/]+)/$', modelimport.VoucherProcessView.as_view(), name='event.vouchers.import.process'),
|
re_path(r'^vouchers/import/(?P<file>[^/]+)/$', modelimport.VoucherProcessView.as_view(), name='event.vouchers.import.process'),
|
||||||
re_path(r'^orders/(?P<code>[0-9A-Z]+)/transition$', orders.OrderTransition.as_view(),
|
re_path(r'^orders/(?P<code>[0-9A-Z]+)/transition$', orders.OrderTransition.as_view(),
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ from django.conf import settings
|
|||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.core.exceptions import PermissionDenied, ValidationError
|
from django.core.exceptions import PermissionDenied, ValidationError
|
||||||
from django.db import connection, transaction
|
from django.db import connection, transaction
|
||||||
from django.db.models import Exists, OuterRef, Sum
|
from django.db.models import Exists, OuterRef, Sum, Subquery
|
||||||
from django.http import (
|
from django.http import (
|
||||||
Http404, HttpResponse, HttpResponseBadRequest, HttpResponseRedirect,
|
Http404, HttpResponse, HttpResponseBadRequest, HttpResponseRedirect,
|
||||||
JsonResponse,
|
JsonResponse,
|
||||||
@@ -55,7 +55,7 @@ from django.utils.safestring import mark_safe
|
|||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views.generic import (
|
from django.views.generic import (
|
||||||
CreateView, ListView, TemplateView, UpdateView, View,
|
CreateView, ListView, TemplateView, UpdateView, View, FormView,
|
||||||
)
|
)
|
||||||
from django_scopes import scopes_disabled
|
from django_scopes import scopes_disabled
|
||||||
|
|
||||||
@@ -663,3 +663,110 @@ class VoucherBulkAction(VoucherQueryMixin, EventPermissionRequiredMixin, View):
|
|||||||
'organizer': self.request.event.organizer.slug,
|
'organizer': self.request.event.organizer.slug,
|
||||||
'event': self.request.event.slug,
|
'event': self.request.event.slug,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class VoucherBulkUpdateView(VoucherQueryMixin, EventPermissionRequiredMixin, FormView):
|
||||||
|
template_name = 'pretixcontrol/vouchers/bulk_edit.html'
|
||||||
|
permission = 'event.vouchers:write'
|
||||||
|
context_object_name = 'voucher'
|
||||||
|
form_class = VoucherBulkEditForm
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return super().get_queryset().prefetch_related(None).order_by()
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
return HttpResponse(status=405)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def is_submitted(self):
|
||||||
|
# Usually, django considers a form "bound" / "submitted" on every POST request. However, this view is always
|
||||||
|
# called with POST method, even if just to pass the selection of objects to work on, so we want to modify
|
||||||
|
# that behaviour
|
||||||
|
return '_bulk' in self.request.POST
|
||||||
|
|
||||||
|
def get_form_kwargs(self):
|
||||||
|
initial = {}
|
||||||
|
mixed_values = set()
|
||||||
|
qs = self.get_queryset().annotate()
|
||||||
|
|
||||||
|
fields = {
|
||||||
|
'name': 'name',
|
||||||
|
'size': 'size',
|
||||||
|
'subevent': 'subevent',
|
||||||
|
'close_when_sold_out': 'close_when_sold_out',
|
||||||
|
'release_after_exit': 'release_after_exit',
|
||||||
|
'ignore_for_event_availability': 'ignore_for_event_availability',
|
||||||
|
}
|
||||||
|
for k, f in fields.items():
|
||||||
|
existing_values = list(qs.order_by(f).values(f).annotate(c=Count('*')))
|
||||||
|
if len(existing_values) == 1:
|
||||||
|
initial[k] = existing_values[0][f]
|
||||||
|
elif len(existing_values) > 1:
|
||||||
|
mixed_values.add(k)
|
||||||
|
initial[k] = None
|
||||||
|
|
||||||
|
item_values = list(qs.order_by("items_list").values("items_list").annotate(c=Count('*')))
|
||||||
|
var_values = list(qs.order_by("vars_list").values("vars_list").annotate(c=Count('*')))
|
||||||
|
if len(item_values) > 1 or len(var_values) > 1:
|
||||||
|
mixed_values.add("itemvars")
|
||||||
|
else:
|
||||||
|
initial["itemvars"] = [iv for iv in (item_values[0]["items_list"] or "").split(",") + (var_values[0]["vars_list"] or "").split(",") if iv]
|
||||||
|
|
||||||
|
kwargs = super().get_form_kwargs()
|
||||||
|
kwargs['event'] = self.request.event
|
||||||
|
kwargs['prefix'] = 'bulkedit'
|
||||||
|
kwargs['initial'] = initial
|
||||||
|
kwargs['queryset'] = self.get_queryset()
|
||||||
|
kwargs['mixed_values'] = mixed_values
|
||||||
|
if not self.is_submitted:
|
||||||
|
kwargs['data'] = None
|
||||||
|
kwargs['files'] = None
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse('control:event.items.quotas', kwargs={
|
||||||
|
'organizer': self.request.event.organizer.slug,
|
||||||
|
'event': self.request.event.slug,
|
||||||
|
})
|
||||||
|
|
||||||
|
@transaction.atomic()
|
||||||
|
def form_valid(self, form):
|
||||||
|
log_entries = []
|
||||||
|
|
||||||
|
# Main form
|
||||||
|
form.save()
|
||||||
|
data = {
|
||||||
|
k: v
|
||||||
|
for k, v in form.cleaned_data.items()
|
||||||
|
if k in form.changed_data
|
||||||
|
}
|
||||||
|
data['_raw_bulk_data'] = self.request.POST.dict()
|
||||||
|
for obj in self.get_queryset():
|
||||||
|
log_entries.append(
|
||||||
|
obj.log_action('pretix.event.quota.changed', data=data, user=self.request.user, save=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
LogEntry.bulk_create_and_postprocess(log_entries)
|
||||||
|
|
||||||
|
messages.success(self.request, _('Your changes have been saved.'))
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
ctx = super().get_context_data(**kwargs)
|
||||||
|
ctx['quotas'] = self.get_queryset()
|
||||||
|
ctx['bulk_selected'] = self.request.POST.getlist("_bulk")
|
||||||
|
return ctx
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
form = self.get_form()
|
||||||
|
is_valid = (
|
||||||
|
self.is_submitted and
|
||||||
|
form.is_valid()
|
||||||
|
)
|
||||||
|
if is_valid:
|
||||||
|
return self.form_valid(form)
|
||||||
|
else:
|
||||||
|
if self.is_submitted:
|
||||||
|
messages.error(self.request, _('We could not save your changes. See below for details.'))
|
||||||
|
return self.form_invalid(form)
|
||||||
|
|||||||
Reference in New Issue
Block a user