forked from CGM_Public/pretix_original
436 lines
17 KiB
Python
436 lines
17 KiB
Python
import copy
|
|
|
|
from django.contrib import messages
|
|
from django.core.urlresolvers import reverse
|
|
from django.db import transaction
|
|
from django.db.models import F, IntegerField, OuterRef, Prefetch, Subquery, Sum
|
|
from django.db.models.functions import Coalesce
|
|
from django.forms import inlineformset_factory
|
|
from django.http import Http404, HttpResponseRedirect
|
|
from django.utils.functional import cached_property
|
|
from django.utils.translation import pgettext_lazy, ugettext_lazy as _
|
|
from django.views.generic import CreateView, DeleteView, ListView, UpdateView
|
|
|
|
from pretix.base.models.checkin import CheckinList
|
|
from pretix.base.models.event import SubEvent, SubEventMetaValue
|
|
from pretix.base.models.items import Quota, SubEventItem, SubEventItemVariation
|
|
from pretix.control.forms.checkin import CheckinListForm
|
|
from pretix.control.forms.filter import SubEventFilterForm
|
|
from pretix.control.forms.item import QuotaForm
|
|
from pretix.control.forms.subevents import (
|
|
CheckinListFormSet, QuotaFormSet, SubEventForm, SubEventItemForm,
|
|
SubEventItemVariationForm, SubEventMetaValueForm,
|
|
)
|
|
from pretix.control.permissions import EventPermissionRequiredMixin
|
|
from pretix.control.views import PaginationMixin
|
|
from pretix.control.views.event import MetaDataEditorMixin
|
|
|
|
|
|
class SubEventList(EventPermissionRequiredMixin, PaginationMixin, ListView):
|
|
model = SubEvent
|
|
context_object_name = 'subevents'
|
|
template_name = 'pretixcontrol/subevents/index.html'
|
|
permission = 'can_change_settings'
|
|
|
|
def get_queryset(self):
|
|
sum_tickets_paid = Quota.objects.filter(
|
|
subevent=OuterRef('pk')
|
|
).order_by().values('subevent').annotate(
|
|
s=Sum('cached_availability_paid_orders')
|
|
).values(
|
|
's'
|
|
)
|
|
|
|
qs = self.request.event.subevents.annotate(
|
|
sum_tickets_paid=Subquery(sum_tickets_paid, output_field=IntegerField())
|
|
).prefetch_related(
|
|
Prefetch('quotas',
|
|
queryset=Quota.objects.annotate(s=Coalesce(F('size'), 0)).order_by('-s'),
|
|
to_attr='first_quotas')
|
|
)
|
|
if self.filter_form.is_valid():
|
|
qs = self.filter_form.filter_qs(qs)
|
|
return qs
|
|
|
|
def get_context_data(self, **kwargs):
|
|
ctx = super().get_context_data(**kwargs)
|
|
ctx['filter_form'] = self.filter_form
|
|
for s in ctx['subevents']:
|
|
s.first_quotas = s.first_quotas[:4]
|
|
for q in s.first_quotas:
|
|
q.cached_avail = (
|
|
(q.cached_availability_state, q.cached_availability_number)
|
|
if q.cached_availability_time is not None
|
|
else q.availability(allow_cache=True)
|
|
)
|
|
if q.size is not None:
|
|
q.percent_paid = min(
|
|
100,
|
|
round(q.cached_availability_paid_orders / q.size * 100) if q.size > 0 else 100
|
|
)
|
|
return ctx
|
|
|
|
@cached_property
|
|
def filter_form(self):
|
|
return SubEventFilterForm(data=self.request.GET)
|
|
|
|
|
|
class SubEventDelete(EventPermissionRequiredMixin, DeleteView):
|
|
model = SubEvent
|
|
template_name = 'pretixcontrol/subevents/delete.html'
|
|
permission = 'can_change_settings'
|
|
context_object_name = 'subevents'
|
|
|
|
def get_object(self, queryset=None) -> SubEvent:
|
|
try:
|
|
return self.request.event.subevents.get(
|
|
id=self.kwargs['subevent']
|
|
)
|
|
except SubEvent.DoesNotExist:
|
|
raise Http404(pgettext_lazy("subevent", "The requested date does not exist."))
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
if self.get_object().orderposition_set.count() > 0:
|
|
messages.error(request, pgettext_lazy('subevent', 'A date can not be deleted if orders already have been '
|
|
'placed.'))
|
|
return HttpResponseRedirect(self.get_success_url())
|
|
return super().get(request, *args, **kwargs)
|
|
|
|
@transaction.atomic
|
|
def delete(self, request, *args, **kwargs):
|
|
self.object = self.get_object()
|
|
success_url = self.get_success_url()
|
|
|
|
if self.get_object().orderposition_set.count() > 0:
|
|
messages.error(request, pgettext_lazy('subevent', 'A date can not be deleted if orders already have been '
|
|
'placed.'))
|
|
return HttpResponseRedirect(self.get_success_url())
|
|
else:
|
|
self.object.log_action('pretix.subevent.deleted', user=self.request.user)
|
|
self.object.delete()
|
|
messages.success(request, pgettext_lazy('subevent', 'The selected date has been deleted.'))
|
|
return HttpResponseRedirect(success_url)
|
|
|
|
def get_success_url(self) -> str:
|
|
return reverse('control:event.subevents', kwargs={
|
|
'organizer': self.request.event.organizer.slug,
|
|
'event': self.request.event.slug,
|
|
})
|
|
|
|
|
|
class SubEventEditorMixin(MetaDataEditorMixin):
|
|
meta_form = SubEventMetaValueForm
|
|
meta_model = SubEventMetaValue
|
|
|
|
def _make_meta_form(self, p, val_instances):
|
|
if not hasattr(self, '_default_meta'):
|
|
self._default_meta = self.request.event.meta_data
|
|
|
|
return self.meta_form(
|
|
prefix='prop-{}'.format(p.pk),
|
|
property=p,
|
|
default=self._default_meta.get(p.name, ''),
|
|
instance=val_instances.get(p.pk, self.meta_model(property=p, subevent=self.object)),
|
|
data=(self.request.POST if self.request.method == "POST" else None)
|
|
)
|
|
|
|
@cached_property
|
|
def cl_formset(self):
|
|
extra = 0
|
|
kwargs = {}
|
|
|
|
if self.copy_from:
|
|
kwargs['initial'] = [
|
|
{
|
|
'name': cl.name,
|
|
'all_products': cl.all_products,
|
|
'limit_products': cl.limit_products.all(),
|
|
} for cl in self.copy_from.checkinlist_set.prefetch_related('limit_products')
|
|
]
|
|
extra = len(kwargs['initial'])
|
|
elif not self.object:
|
|
kwargs['initial'] = [
|
|
{
|
|
'name': '',
|
|
'all_products': True,
|
|
}
|
|
]
|
|
extra = 1
|
|
|
|
formsetclass = inlineformset_factory(
|
|
SubEvent, CheckinList,
|
|
form=CheckinListForm, formset=CheckinListFormSet,
|
|
can_order=False, can_delete=True, extra=extra,
|
|
)
|
|
if self.object:
|
|
kwargs['queryset'] = self.object.checkinlist_set.prefetch_related('limit_products')
|
|
|
|
return formsetclass(self.request.POST if self.request.method == "POST" else None,
|
|
instance=self.object,
|
|
event=self.request.event, **kwargs)
|
|
|
|
@cached_property
|
|
def formset(self):
|
|
extra = 0
|
|
kwargs = {}
|
|
|
|
if self.copy_from:
|
|
kwargs['initial'] = [
|
|
{
|
|
'size': q.size,
|
|
'name': q.name,
|
|
'itemvars': [str(i.pk) for i in q.items.all()] + [
|
|
'{}-{}'.format(v.item_id, v.pk) for v in q.variations.all()
|
|
]
|
|
} for q in self.copy_from.quotas.prefetch_related('items', 'variations')
|
|
]
|
|
extra = len(kwargs['initial'])
|
|
|
|
formsetclass = inlineformset_factory(
|
|
SubEvent, Quota,
|
|
form=QuotaForm, formset=QuotaFormSet,
|
|
can_order=False, can_delete=True, extra=extra,
|
|
)
|
|
if self.object:
|
|
kwargs['queryset'] = self.object.quotas.prefetch_related('items', 'variations')
|
|
|
|
return formsetclass(self.request.POST if self.request.method == "POST" else None,
|
|
instance=self.object,
|
|
event=self.request.event, **kwargs)
|
|
|
|
def save_cl_formset(self, obj):
|
|
for form in self.cl_formset.initial_forms:
|
|
if form in self.cl_formset.deleted_forms:
|
|
if not form.instance.pk:
|
|
continue
|
|
form.instance.log_action(action='pretix.event.checkinlist.deleted', user=self.request.user)
|
|
form.instance.delete()
|
|
form.instance.pk = None
|
|
elif form.has_changed():
|
|
form.instance.subevent = obj
|
|
form.instance.event = obj.event
|
|
form.save()
|
|
change_data = {k: form.cleaned_data.get(k) for k in form.changed_data}
|
|
change_data['id'] = form.instance.pk
|
|
form.instance.log_action(
|
|
'pretix.event.checkinlist.changed', user=self.request.user, data={
|
|
k: form.cleaned_data.get(k) for k in form.changed_data
|
|
}
|
|
)
|
|
|
|
for form in self.cl_formset.extra_forms:
|
|
if not form.has_changed():
|
|
continue
|
|
if self.formset._should_delete_form(form):
|
|
continue
|
|
form.instance.subevent = obj
|
|
form.instance.event = obj.event
|
|
form.save()
|
|
change_data = {k: form.cleaned_data.get(k) for k in form.changed_data}
|
|
change_data['id'] = form.instance.pk
|
|
form.instance.log_action(action='pretix.event.checkinlist.added', user=self.request.user, data=change_data)
|
|
|
|
def save_formset(self, obj):
|
|
for form in self.formset.initial_forms:
|
|
if form in self.formset.deleted_forms:
|
|
if not form.instance.pk:
|
|
continue
|
|
form.instance.log_action(action='pretix.event.quota.deleted', user=self.request.user)
|
|
obj.log_action('pretix.subevent.quota.deleted', user=self.request.user, data={
|
|
'id': form.instance.pk
|
|
})
|
|
form.instance.delete()
|
|
form.instance.pk = None
|
|
elif form.has_changed():
|
|
form.instance.question = obj
|
|
form.save()
|
|
change_data = {k: form.cleaned_data.get(k) for k in form.changed_data}
|
|
change_data['id'] = form.instance.pk
|
|
obj.log_action(
|
|
'pretix.subevent.quota.changed', user=self.request.user, data={
|
|
k: form.cleaned_data.get(k) for k in form.changed_data
|
|
}
|
|
)
|
|
form.instance.log_action(
|
|
'pretix.event.quota.changed', user=self.request.user, data={
|
|
k: form.cleaned_data.get(k) for k in form.changed_data
|
|
}
|
|
)
|
|
|
|
for form in self.formset.extra_forms:
|
|
if not form.has_changed():
|
|
continue
|
|
if self.formset._should_delete_form(form):
|
|
continue
|
|
form.instance.subevent = obj
|
|
form.instance.event = obj.event
|
|
form.save()
|
|
change_data = {k: form.cleaned_data.get(k) for k in form.changed_data}
|
|
change_data['id'] = form.instance.pk
|
|
form.instance.log_action(action='pretix.event.quota.added', user=self.request.user, data=change_data)
|
|
obj.log_action('pretix.subevent.quota.added', user=self.request.user, data=change_data)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
ctx = super().get_context_data(**kwargs)
|
|
ctx['formset'] = self.formset
|
|
ctx['cl_formset'] = self.cl_formset
|
|
ctx['itemvar_forms'] = self.itemvar_forms
|
|
ctx['meta_forms'] = self.meta_forms
|
|
return ctx
|
|
|
|
@cached_property
|
|
def copy_from(self):
|
|
if self.request.GET.get("copy_from") and not getattr(self, 'object'):
|
|
try:
|
|
return self.request.event.subevents.get(pk=self.request.GET.get("copy_from"))
|
|
except SubEvent.DoesNotExist:
|
|
pass
|
|
|
|
@cached_property
|
|
def itemvar_forms(self):
|
|
se_item_instances = {
|
|
sei.item_id: sei for sei in SubEventItem.objects.filter(subevent=self.object)
|
|
}
|
|
se_var_instances = {
|
|
sei.variation_id: sei for sei in SubEventItemVariation.objects.filter(subevent=self.object)
|
|
}
|
|
|
|
if self.copy_from:
|
|
se_item_instances = {
|
|
sei.item_id: SubEventItem(item=sei.item, price=sei.price)
|
|
for sei in SubEventItem.objects.filter(subevent=self.copy_from).select_related('item')
|
|
}
|
|
se_var_instances = {
|
|
sei.variation_id: SubEventItemVariation(variation=sei.variation, price=sei.price)
|
|
for sei in SubEventItemVariation.objects.filter(subevent=self.copy_from).select_related('variation')
|
|
}
|
|
|
|
formlist = []
|
|
for i in self.request.event.items.filter(active=True).prefetch_related('variations'):
|
|
if i.has_variations:
|
|
for v in i.variations.all():
|
|
inst = se_var_instances.get(v.pk) or SubEventItemVariation(subevent=self.object, variation=v)
|
|
formlist.append(SubEventItemVariationForm(
|
|
prefix='itemvar-{}'.format(v.pk),
|
|
item=i, variation=v,
|
|
instance=inst,
|
|
data=(self.request.POST if self.request.method == "POST" else None)
|
|
))
|
|
else:
|
|
inst = se_item_instances.get(i.pk) or SubEventItem(subevent=self.object, item=i)
|
|
formlist.append(SubEventItemForm(
|
|
prefix='item-{}'.format(i.pk),
|
|
item=i,
|
|
instance=inst,
|
|
data=(self.request.POST if self.request.method == "POST" else None)
|
|
))
|
|
return formlist
|
|
|
|
def is_valid(self, form):
|
|
return form.is_valid() and all([f.is_valid() for f in self.itemvar_forms]) and self.formset.is_valid() and (
|
|
all([f.is_valid() for f in self.meta_forms])
|
|
) and self.cl_formset.is_valid()
|
|
|
|
|
|
class SubEventUpdate(EventPermissionRequiredMixin, SubEventEditorMixin, UpdateView):
|
|
model = SubEvent
|
|
template_name = 'pretixcontrol/subevents/detail.html'
|
|
permission = 'can_change_settings'
|
|
context_object_name = 'subevent'
|
|
form_class = SubEventForm
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
self.object = self.get_object()
|
|
form = self.get_form()
|
|
if self.is_valid(form):
|
|
return self.form_valid(form)
|
|
else:
|
|
return self.form_invalid(form)
|
|
|
|
def get_object(self, queryset=None) -> SubEvent:
|
|
try:
|
|
return self.request.event.subevents.get(
|
|
id=self.kwargs['subevent']
|
|
)
|
|
except SubEvent.DoesNotExist:
|
|
raise Http404(pgettext_lazy("subevent", "The requested date does not exist."))
|
|
|
|
@transaction.atomic
|
|
def form_valid(self, form):
|
|
self.save_formset(self.object)
|
|
self.save_cl_formset(self.object)
|
|
self.save_meta()
|
|
|
|
for f in self.itemvar_forms:
|
|
f.save()
|
|
# TODO: LogEntry?
|
|
|
|
messages.success(self.request, _('Your changes have been saved.'))
|
|
if form.has_changed():
|
|
self.object.log_action(
|
|
'pretix.subevent.changed', user=self.request.user, data={
|
|
k: form.cleaned_data.get(k) for k in form.changed_data
|
|
}
|
|
)
|
|
return super().form_valid(form)
|
|
|
|
def get_success_url(self) -> str:
|
|
return reverse('control:event.subevents', kwargs={
|
|
'organizer': self.request.event.organizer.slug,
|
|
'event': self.request.event.slug,
|
|
})
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super().get_form_kwargs()
|
|
kwargs['event'] = self.request.event
|
|
return kwargs
|
|
|
|
|
|
class SubEventCreate(SubEventEditorMixin, EventPermissionRequiredMixin, CreateView):
|
|
model = SubEvent
|
|
template_name = 'pretixcontrol/subevents/detail.html'
|
|
permission = 'can_change_settings'
|
|
context_object_name = 'subevent'
|
|
form_class = SubEventForm
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
self.object = SubEvent(event=self.request.event)
|
|
form = self.get_form()
|
|
if self.is_valid(form):
|
|
return self.form_valid(form)
|
|
else:
|
|
return self.form_invalid(form)
|
|
|
|
def get_success_url(self) -> str:
|
|
return reverse('control:event.subevents', kwargs={
|
|
'organizer': self.request.event.organizer.slug,
|
|
'event': self.request.event.slug,
|
|
})
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super().get_form_kwargs()
|
|
kwargs['event'] = self.request.event
|
|
if self.copy_from:
|
|
i = copy.copy(self.copy_from)
|
|
i.pk = None
|
|
kwargs['instance'] = i
|
|
else:
|
|
kwargs['instance'] = SubEvent(event=self.request.event)
|
|
return kwargs
|
|
|
|
@transaction.atomic
|
|
def form_valid(self, form):
|
|
form.instance.event = self.request.event
|
|
messages.success(self.request, pgettext_lazy('subevent', 'The new date has been created.'))
|
|
ret = super().form_valid(form)
|
|
form.instance.log_action('pretix.subevent.added', data=dict(form.cleaned_data), user=self.request.user)
|
|
|
|
self.save_formset(form.instance)
|
|
self.save_cl_formset(form.instance)
|
|
for f in self.itemvar_forms:
|
|
f.instance.subevent = form.instance
|
|
f.save()
|
|
self.object = form.instance
|
|
self.save_meta()
|
|
return ret
|