mirror of
https://github.com/pretix/pretix.git
synced 2026-10-02 19:24:42 +00:00
ItemCreate/ItemUpdate: replace personalized with questionnaires selection
This commit is contained in:
@@ -2052,9 +2052,10 @@ class Questionnaire(LoggedModel):
|
||||
def __lt__(self, other) -> bool:
|
||||
return self.sortkey < other.sortkey
|
||||
|
||||
def check_constraints(self):
|
||||
@staticmethod
|
||||
def check_constraints(event):
|
||||
errors = set()
|
||||
all_questionnaires = Questionnaire.objects.filter(event=self.event).prefetch_related('children', 'items')
|
||||
all_questionnaires = Questionnaire.objects.filter(event=event).prefetch_related('children', 'items')
|
||||
for (type, item_id, sdf, udf), fields in groupby(sorted(
|
||||
(q.type, item.id, c.system_datafield or '', c.user_datafield_id or 0, q, item)
|
||||
for q in all_questionnaires
|
||||
|
||||
@@ -60,7 +60,7 @@ from pretix.base.models import (
|
||||
Item, ItemCategory, ItemProgramTime, ItemVariation, Question,
|
||||
QuestionOption, Quota,
|
||||
)
|
||||
from pretix.base.models.items import ItemAddOn, ItemBundle, ItemMetaValue
|
||||
from pretix.base.models.items import ItemAddOn, ItemBundle, ItemMetaValue, Questionnaire
|
||||
from pretix.base.signals import item_copy_data
|
||||
from pretix.control.forms import (
|
||||
ButtonGroupRadioSelect, ExtFileField, ItemMultipleChoiceField,
|
||||
@@ -392,13 +392,20 @@ class ItemCreateForm(I18nModelForm):
|
||||
help_text=_('Select this option e.g. for t-shirts that come in multiple sizes. '
|
||||
'You can select the variations in the next step.'),
|
||||
required=False)
|
||||
questionnaires = SafeModelMultipleChoiceField(
|
||||
queryset=Questionnaire.objects.none(),
|
||||
required=False,
|
||||
label=_('Questionnaires'),
|
||||
widget=forms.CheckboxSelectMultiple(attrs={
|
||||
'class': 'scrolling-multiple-choice'
|
||||
}),
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.event = kwargs['event']
|
||||
self.user = kwargs.pop('user')
|
||||
kwargs.setdefault('initial', {})
|
||||
kwargs['initial'].setdefault('admission', True)
|
||||
kwargs['initial'].setdefault('personalized', True)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.fields['category'].queryset = self.instance.event.categories.all()
|
||||
@@ -414,6 +421,8 @@ class ItemCreateForm(I18nModelForm):
|
||||
)
|
||||
self.fields['category'].widget.choices = self.fields['category'].choices
|
||||
|
||||
self.fields['questionnaires'].queryset = self.instance.event.questionnaires.all()
|
||||
|
||||
self.fields['tax_rule'].queryset = self.instance.event.tax_rules.all()
|
||||
change_decimal_field(self.fields['default_price'], self.instance.event.currency)
|
||||
self.fields['copy_from'] = forms.ModelChoiceField(
|
||||
@@ -520,10 +529,15 @@ class ItemCreateForm(I18nModelForm):
|
||||
self.instance.picture.save(os.path.basename(src.picture.name), src.picture)
|
||||
|
||||
self.instance.position = (self.event.items.aggregate(p=Max('position'))['p'] or 0) + 1
|
||||
if not self.instance.admission:
|
||||
self.instance.personalized = False
|
||||
instance = super().save(*args, **kwargs)
|
||||
|
||||
instance.questionnaires.clear()
|
||||
instance.questionnaires.add(*self.cleaned_data['questionnaires'])
|
||||
try:
|
||||
Questionnaire.check_constraints(self.event)
|
||||
except ValidationError as exc:
|
||||
raise ValidationError({'questionnaires': exc})
|
||||
|
||||
if not self.event.has_subevents and not self.cleaned_data.get('has_variations'):
|
||||
if self.cleaned_data.get('quota_option') == self.EXISTING and self.cleaned_data.get('quota_add_existing') is not None:
|
||||
quota = self.cleaned_data.get('quota_add_existing')
|
||||
@@ -647,6 +661,14 @@ class ItemUpdateForm(I18nModelForm):
|
||||
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
|
||||
required=False,
|
||||
)
|
||||
questionnaires = SafeModelMultipleChoiceField(
|
||||
queryset=Questionnaire.objects.none(),
|
||||
required=False,
|
||||
label=_('Questionnaires'),
|
||||
widget=forms.CheckboxSelectMultiple(attrs={
|
||||
'class': 'scrolling-multiple-choice'
|
||||
}),
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -742,6 +764,9 @@ class ItemUpdateForm(I18nModelForm):
|
||||
)
|
||||
self.fields['category'].widget.choices = self.fields['category'].choices
|
||||
|
||||
self.fields['questionnaires'].queryset = self.instance.event.questionnaires.all()
|
||||
self.initial['questionnaires'] = self.instance.questionnaires.values_list('pk', flat=True)
|
||||
|
||||
self.fields['free_price_suggestion'].widget.attrs['data-display-dependency'] = '#id_free_price'
|
||||
|
||||
self.fields['validity_dynamic_start_choice'] = forms.TypedChoiceField(
|
||||
@@ -836,6 +861,16 @@ class ItemUpdateForm(I18nModelForm):
|
||||
|
||||
return d
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
instance = super().save(*args, **kwargs)
|
||||
instance.questionnaires.clear()
|
||||
instance.questionnaires.add(*self.cleaned_data['questionnaires'])
|
||||
try:
|
||||
Questionnaire.check_constraints(self.event)
|
||||
except ValidationError as exc:
|
||||
raise ValidationError({'questionnaires': exc})
|
||||
return instance
|
||||
|
||||
class Meta:
|
||||
model = Item
|
||||
localized_fields = '__all__'
|
||||
@@ -847,7 +882,6 @@ class ItemUpdateForm(I18nModelForm):
|
||||
'all_sales_channels',
|
||||
'limit_sales_channels',
|
||||
'admission',
|
||||
'personalized',
|
||||
'description',
|
||||
'picture',
|
||||
'default_price',
|
||||
|
||||
@@ -64,47 +64,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" data-display-dependency="#admission_on">
|
||||
<label class="col-md-3 control-label">{% trans "Personalization" %}</label>
|
||||
<div class="col-md-9">
|
||||
<div class="big-radio radio">
|
||||
<label>
|
||||
<input type="radio" value="on" name="{{ form.personalized.html_name }}" {% if form.personalized.value %}checked{% endif %}>
|
||||
<span class="fa fa-fw fa-id-card-o"></span>
|
||||
<strong>{% trans "Personalized ticket" %}</strong><br>
|
||||
<div class="help-block">
|
||||
{% blocktrans trimmed %}
|
||||
When this ticket is purchased, the system will ask for a name or other details according
|
||||
to your event settings.
|
||||
{% endblocktrans %}
|
||||
{% if not request.event.settings.attendee_names_asked and not request.event.settings.attendee_emails_asked and not request.event.settings.attendee_company_asked and not request.event.settings.attendee_addresses_asked %}
|
||||
<br>
|
||||
<span class="text-warning">
|
||||
<span class="fa fa-warning" aria-hidden="true"></span>
|
||||
{% trans "This will currently have no effect since all data fields are turned off in event settings." %}
|
||||
</span>
|
||||
<a href="{% url "control:event.settings" organizer=request.event.organizer.slug event=request.event.slug %}#tab-0-2-open"
|
||||
class="btn btn-default btn-xs" target="_blank">{% trans "Change settings" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="big-radio radio">
|
||||
<label>
|
||||
<input type="radio" value="" name="{{ form.personalized.html_name }}" {% if not form.personalized.value %}checked{% endif %}>
|
||||
<span class="fa fa-fw fa-circle-o"></span>
|
||||
<strong>{% trans "Non-personalized ticket" %}</strong>
|
||||
<div class="help-block">
|
||||
{% blocktrans trimmed %}
|
||||
The system will not ask for a name or other attendee details. This only affects
|
||||
system-provided fields, you can still add your own questions.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% bootstrap_field form.questionnaires layout="control" %}
|
||||
{% bootstrap_field form.category layout="control" %}
|
||||
|
||||
<div class="form-group">
|
||||
|
||||
@@ -72,51 +72,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" data-display-dependency="#admission_on">
|
||||
<label class="col-md-3 control-label">{% trans "Personalization" %}</label>
|
||||
<div class="col-md-9">
|
||||
{% for e in form.errors.personalized %}
|
||||
<div class="alert alert-danger has-error">
|
||||
{{ e }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="big-radio radio">
|
||||
<label>
|
||||
<input type="radio" value="on" name="{{ form.personalized.html_name }}" {% if form.personalized.value %}checked{% endif %}>
|
||||
<span class="fa fa-fw fa-id-card-o"></span>
|
||||
<strong>{% trans "Personalized ticket" %}</strong><br>
|
||||
<div class="help-block">
|
||||
{% blocktrans trimmed %}
|
||||
When this ticket is purchased, the system will ask for a name or other details according
|
||||
to your event settings.
|
||||
{% endblocktrans %}
|
||||
{% if not request.event.settings.attendee_names_asked and not request.event.settings.attendee_emails_asked and not request.event.settings.attendee_company_asked and not request.event.settings.attendee_addresses_asked %}
|
||||
<br>
|
||||
<span class="text-warning">
|
||||
<span class="fa fa-warning" aria-hidden="true"></span>
|
||||
{% trans "This will currently have no effect since all data fields are turned off in event settings." %}
|
||||
</span>
|
||||
<a href="{% url "control:event.settings" organizer=request.event.organizer.slug event=request.event.slug %}#tab-0-2-open"
|
||||
class="btn btn-default btn-xs" target="_blank">{% trans "Change settings" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="big-radio radio">
|
||||
<label>
|
||||
<input type="radio" value="" name="{{ form.personalized.html_name }}" {% if not form.personalized.value %}checked{% endif %}>
|
||||
<span class="fa fa-fw fa-file-text-o"></span>
|
||||
<strong>{% trans "Non-personalized ticket" %}</strong>
|
||||
<div class="help-block">
|
||||
{% blocktrans trimmed %}
|
||||
The system will not ask for a name or other attendee details. This only affects
|
||||
system-provided fields, you can still add your own questions.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% bootstrap_field form.questionnaires layout="control" %}
|
||||
|
||||
{% bootstrap_field form.description layout="control" %}
|
||||
{% bootstrap_field form.picture layout="control" %}
|
||||
|
||||
@@ -39,7 +39,7 @@ from itertools import groupby
|
||||
from json.decoder import JSONDecodeError
|
||||
|
||||
from django.contrib import messages
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.core.exceptions import PermissionDenied, ValidationError
|
||||
from django.core.files import File
|
||||
from django.db import models, transaction
|
||||
from django.db.models import (
|
||||
@@ -1335,9 +1335,10 @@ class ItemCreate(EventPermissionRequiredMixin, MetaDataEditorMixin, CreateView):
|
||||
initial['tax_rule'] = trs[0]
|
||||
|
||||
if self.copy_from:
|
||||
fields = ('name', 'internal_name', 'category', 'admission', 'personalized', 'default_price', 'tax_rule')
|
||||
fields = ('name', 'internal_name', 'category', 'admission', 'default_price', 'tax_rule')
|
||||
for f in fields:
|
||||
initial[f] = getattr(self.copy_from, f)
|
||||
initial['questionnaires'] = self.copy_from.questionnaires.values_list('pk', flat=True)
|
||||
initial['copy_from'] = self.copy_from
|
||||
initial['has_variations'] = self.copy_from.variations.exists()
|
||||
|
||||
@@ -1371,7 +1372,7 @@ class ItemCreate(EventPermissionRequiredMixin, MetaDataEditorMixin, CreateView):
|
||||
return super().form_invalid(form)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data()
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['meta_forms'] = self.meta_forms
|
||||
return ctx
|
||||
|
||||
@@ -1379,9 +1380,11 @@ class ItemCreate(EventPermissionRequiredMixin, MetaDataEditorMixin, CreateView):
|
||||
self.object = None
|
||||
form = self.get_form()
|
||||
if form.is_valid() and all([f.is_valid() for f in self.meta_forms]):
|
||||
return self.form_valid(form)
|
||||
else:
|
||||
return self.form_invalid(form)
|
||||
try:
|
||||
return self.form_valid(form)
|
||||
except ValidationError as e:
|
||||
form.add_error(None, e)
|
||||
return self.form_invalid(form)
|
||||
|
||||
|
||||
class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, MetaDataEditorMixin, UpdateView):
|
||||
@@ -1445,9 +1448,11 @@ class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, MetaDataE
|
||||
self.get_object()
|
||||
form = self.get_form()
|
||||
if self.is_valid(form) and all([f.is_valid() for f in self.meta_forms]):
|
||||
return self.form_valid(form)
|
||||
else:
|
||||
return self.form_invalid(form)
|
||||
try:
|
||||
return self.form_valid(form)
|
||||
except ValidationError as exc:
|
||||
form.add_error(None, exc)
|
||||
return self.form_invalid(form)
|
||||
|
||||
def save_formset(self, key, log_base, attr='item', order=True, serializer=None,
|
||||
rm_verb='removed'):
|
||||
@@ -1496,7 +1501,6 @@ class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, MetaDataE
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
self.save_meta()
|
||||
messages.success(self.request, _('Your changes have been saved.'))
|
||||
|
||||
change_data = {
|
||||
k: form.cleaned_data.get(k)
|
||||
@@ -1556,7 +1560,9 @@ class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, MetaDataE
|
||||
else:
|
||||
v.save()
|
||||
|
||||
return super().form_valid(form)
|
||||
response = super().form_valid(form)
|
||||
messages.success(self.request, _('Your changes have been saved.'))
|
||||
return response
|
||||
|
||||
def form_invalid(self, form):
|
||||
messages.error(self.request, _('We could not save your changes. See below for details.'))
|
||||
@@ -1569,7 +1575,7 @@ class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, MetaDataE
|
||||
return o
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data()
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['plugin_forms'] = self.plugin_forms
|
||||
ctx['meta_forms'] = self.meta_forms
|
||||
ctx['formsets'] = self.formsets
|
||||
|
||||
Reference in New Issue
Block a user