Separate personalization from admission (#2990)

Co-authored-by: Richard Schreiber <schreiber@rami.io>
This commit is contained in:
Raphael Michel
2023-01-09 14:57:35 +01:00
committed by GitHub
parent e5528f7784
commit 603225d042
30 changed files with 293 additions and 52 deletions

View File

@@ -295,6 +295,7 @@ class ItemCreateForm(I18nModelForm):
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()
@@ -403,6 +404,8 @@ class ItemCreateForm(I18nModelForm):
self.instance.sales_channels = list(get_all_sales_channels().keys())
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)
if not self.event.has_subevents and not self.cleaned_data.get('has_variations'):
@@ -494,6 +497,7 @@ class ItemCreateForm(I18nModelForm):
'internal_name',
'category',
'admission',
'personalized',
'default_price',
'tax_rule',
]
@@ -588,13 +592,14 @@ class ItemUpdateForm(I18nModelForm):
'tax_rule',
_("Gift card products should use a tax rule with a rate of 0 percent since sales tax will be applied when the gift card is redeemed.")
)
if d['admission']:
if d.get('admission'):
self.add_error(
'admission',
_(
"Gift card products should not be admission products at the same time."
)
)
if d.get('require_membership') and not d.get('require_membership_types'):
self.add_error(
'require_membership_types',
@@ -602,6 +607,18 @@ class ItemUpdateForm(I18nModelForm):
"If a valid membership is required, at least one valid membership type needs to be selected."
)
)
if not d.get('admission'):
d['personalized'] = False
if d.get('grant_membership_type'):
if not d['grant_membership_type'].transferable and not d['personalized']:
self.add_error(
'personalized' if d['admission'] else 'admission',
_("Your product grants a non-transferable membership and should therefore be a personalized "
"admission ticket. Otherwise customers might not be able to use the membership later. If you "
"want the membership to be non-personalized, set the membership type to be transferable.")
)
return d
def clean_picture(self):
@@ -622,6 +639,7 @@ class ItemUpdateForm(I18nModelForm):
'active',
'sales_channels',
'admission',
'personalized',
'description',
'picture',
'default_price',

View File

@@ -90,7 +90,7 @@
</div>
</div>
<h4>{% trans "Attendee data (once per admission ticket)" %}</h4>
<h4>{% trans "Attendee data (once per personalized ticket)" %}</h4>
{% bootstrap_field sform.attendee_names_asked_required layout="control" %}
{% bootstrap_field sform.attendee_emails_asked_required layout="control" %}

View File

@@ -20,13 +20,19 @@
<div class="col-md-9">
<div class="big-radio radio">
<label>
<input type="radio" value="on" name="{{ form.admission.html_name }}" {% if form.admission.value %}checked{% endif %}>
<span class="fa fa-user"></span>
<input type="radio" value="on" name="{{ form.admission.html_name }}" {% if form.admission.value %}checked{% endif %} id="admission_on">
<span class="fa fa-fw fa-user"></span>
<strong>{% trans "Admission product" %}</strong><br>
<div class="help-block">
{% blocktrans trimmed %}
Every purchase of this product represents one person who is allowed to enter your event.
By default, pretix will only ask for attendee information and offer ticket downloads for these products.
By default, we will only offer ticket downloads for these products.
{% endblocktrans %}
</div>
<div class="help-block">
{% blocktrans trimmed %}
Only purchases of such products will be considered "attendees" for most statistical
purposes or within some plugins.
{% endblocktrans %}
</div>
<div class="help-block">
@@ -40,12 +46,12 @@
<div class="big-radio radio">
<label>
<input type="radio" value="" name="{{ form.admission.html_name }}" {% if not form.admission.value %}checked{% endif %}>
<span class="fa fa-cube"></span>
<span class="fa fa-fw fa-cube"></span>
<strong>{% trans "Non-admission product" %}</strong>
<div class="help-block">
{% blocktrans trimmed %}
A product that does not represent a person. By default, pretix will not ask for attendee information or offer
ticket downloads.
A product that does not represent a person. By default, we will not offer ticket downloads
(but you can still enable ticket downloads in event settings or product settings).
{% endblocktrans %}
</div>
<div class="help-block">
@@ -58,6 +64,47 @@
</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.category layout="control" %}
<div class="form-group">

View File

@@ -27,13 +27,19 @@
{% endfor %}
<div class="big-radio radio">
<label>
<input type="radio" value="on" name="{{ form.admission.html_name }}" {% if form.admission.value %}checked{% endif %}>
<input type="radio" value="on" name="{{ form.admission.html_name }}" {% if form.admission.value %}checked{% endif %} id="admission_on">
<span class="fa fa-fw fa-user"></span>
<strong>{% trans "Admission product" %}</strong><br>
<div class="help-block">
{% blocktrans trimmed %}
Every purchase of this product represents one person who is allowed to enter your event.
By default, pretix will only ask for attendee information and offer ticket downloads for these products.
By default, we will only offer ticket downloads for these products.
{% endblocktrans %}
</div>
<div class="help-block">
{% blocktrans trimmed %}
Only purchases of such products will be considered "attendees" for most statistical
purposes or within some plugins.
{% endblocktrans %}
</div>
<div class="help-block">
@@ -51,8 +57,8 @@
<strong>{% trans "Non-admission product" %}</strong>
<div class="help-block">
{% blocktrans trimmed %}
A product that does not represent a person. By default, pretix will not ask for attendee information or offer
ticket downloads.
A product that does not represent a person. By default, we will not offer ticket downloads
(but you can still enable ticket downloads in event settings or product settings).
{% endblocktrans %}
</div>
<div class="help-block">
@@ -65,6 +71,52 @@
</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-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.description layout="control" %}
{% bootstrap_field form.picture layout="control" %}
{% bootstrap_field form.require_approval layout="control" %}

View File

@@ -81,7 +81,11 @@
</td>
<td>
{% if i.admission %}
<span class="fa fa-user fa-fw text-muted" data-toggle="tooltip" title="{% trans "Admission ticket" %}"></span>
{% if i.personalized %}
<span class="fa fa-id-card-o fa-fw text-muted" data-toggle="tooltip" title="{% trans "Personalized admission ticket" %}"></span>
{% else %}
<span class="fa fa-user fa-fw text-muted" data-toggle="tooltip" title="{% trans "Admission ticket without personalization" %}"></span>
{% endif %}
{% elif i.issue_giftcard %}
<span class="fa fa-gift fa-fw text-muted" data-toggle="tooltip" title="{% trans "Gift card" %}"></span>
{% endif %}

View File

@@ -76,7 +76,7 @@
{% endfor %}
</ul>
{% else %}
<small>{% trans "All admission products" %}</small>
<small>{% trans "All personalized products" %}</small>
{% endif %}
</td>
<td class="dnd-container">

View File

@@ -468,12 +468,12 @@
{% endif %}
{% if line.has_questions %}
<dl>
{% if line.item.admission and event.settings.attendee_names_asked %}
{% if line.item.ask_attendee_data and event.settings.attendee_names_asked %}
<dt>{% trans "Attendee name" %}</dt>
<dd>{% if line.attendee_name %}{{ line.attendee_name }}{% else %}
<em>{% trans "not answered" %}</em>{% endif %}</dd>
{% endif %}
{% if line.item.admission and event.settings.attendee_emails_asked %}
{% if line.item.ask_attendee_data and event.settings.attendee_emails_asked %}
<dt>{% trans "Attendee email" %}</dt>
<dd>
{% if line.attendee_email %}
@@ -496,7 +496,7 @@
{% endif %}
</dd>
{% endif %}
{% if line.item.admission and event.settings.attendee_company_asked %}
{% if line.item.ask_attendee_data and event.settings.attendee_company_asked %}
<dt>
{% trans "Attendee company" %}
</dt>
@@ -504,7 +504,7 @@
{% if line.company %}{{ line.company }}{% else %}<em>{% trans "not answered" %}</em>{% endif %}
</dd>
{% endif %}
{% if line.item.admission and event.settings.attendee_addresses_asked %}
{% if line.item.ask_attendee_data and event.settings.attendee_addresses_asked %}
<dt>
{% trans "Attendee address" %}
</dt>

View File

@@ -1204,7 +1204,7 @@ class ItemCreate(EventPermissionRequiredMixin, CreateView):
initial['tax_rule'] = trs[0]
if self.copy_from:
fields = ('name', 'internal_name', 'category', 'admission', 'default_price', 'tax_rule')
fields = ('name', 'internal_name', 'category', 'admission', 'personalized', 'default_price', 'tax_rule')
for f in fields:
initial[f] = getattr(self.copy_from, f)
initial['copy_from'] = self.copy_from

View File

@@ -383,8 +383,8 @@ class OrderDetail(OrderView):
p.has_questions = (
p.additional_fields or
(p.item.admission and self.request.event.settings.attendee_names_asked) or
(p.item.admission and self.request.event.settings.attendee_emails_asked) or
(p.item.ask_attendee_data and self.request.event.settings.attendee_names_asked) or
(p.item.ask_attendee_data and self.request.event.settings.attendee_emails_asked) or
p.item.questions.all()
)
p.cache_answers()