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

@@ -73,6 +73,7 @@ class ItemDataExporter(ListExporter):
_("Free price input"),
_("Sales tax"),
_("Is an admission ticket"),
_("Personalized ticket"),
_("Generate tickets"),
_("Waiting list"),
_("Available from"),
@@ -144,6 +145,7 @@ class ItemDataExporter(ListExporter):
_("Yes") if i.free_price else "",
str(i.tax_rule) if i.tax_rule else "",
_("Yes") if i.admission else "",
_("Yes") if i.personalized else "",
_("Yes") if i.generate_tickets else (_("Default") if i.generate_tickets is None else ""),
_("Yes") if i.allow_waitinglist else "",
date_format(_max(i.available_from, v.available_from).astimezone(self.timezone),

View File

@@ -78,6 +78,7 @@ class JSONExporter(BaseExporter):
'tax_rate': item.tax_rule.rate if item.tax_rule else Decimal('0.00'),
'tax_name': str(item.tax_rule.name) if item.tax_rule else None,
'admission': item.admission,
'personalized': item.personalized,
'active': item.active,
'sales_channels': item.sales_channels,
'description': str(item.description),

View File

@@ -575,7 +575,7 @@ class BaseQuestionsForm(forms.Form):
add_fields = {}
if item.admission and event.settings.attendee_names_asked:
if item.ask_attendee_data and event.settings.attendee_names_asked:
add_fields['attendee_name_parts'] = NamePartsFormField(
max_length=255,
required=event.settings.attendee_names_required and not self.all_optional,
@@ -584,7 +584,7 @@ class BaseQuestionsForm(forms.Form):
label=_('Attendee name'),
initial=(cartpos.attendee_name_parts if cartpos else orderpos.attendee_name_parts),
)
if item.admission and event.settings.attendee_emails_asked:
if item.ask_attendee_data and event.settings.attendee_emails_asked:
add_fields['attendee_email'] = forms.EmailField(
required=event.settings.attendee_emails_required and not self.all_optional,
label=_('Attendee email'),
@@ -595,7 +595,7 @@ class BaseQuestionsForm(forms.Form):
}
)
)
if item.admission and event.settings.attendee_company_asked:
if item.ask_attendee_data and event.settings.attendee_company_asked:
add_fields['company'] = forms.CharField(
required=event.settings.attendee_company_required and not self.all_optional,
label=_('Company'),
@@ -603,7 +603,7 @@ class BaseQuestionsForm(forms.Form):
initial=(cartpos.company if cartpos else orderpos.company),
)
if item.admission and event.settings.attendee_addresses_asked:
if item.ask_attendee_data and event.settings.attendee_addresses_asked:
add_fields['street'] = forms.CharField(
required=event.settings.attendee_addresses_required and not self.all_optional,
label=_('Address'),

View File

@@ -0,0 +1,27 @@
# Generated by Django 3.2.16 on 2022-12-21 08:59
from django.db import migrations, models
def item_set_personalized(apps, schema_editor):
# We cannot really know if a position was bundled or an add-on, but we can at least guess
Item = apps.get_model("pretixbase", "Item")
Item.objects.filter(admission=True).update(personalized=True)
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0226_itemvariationmetavalue'),
]
operations = [
migrations.AddField(
model_name='item',
name='personalized',
field=models.BooleanField(default=False),
),
migrations.RunPython(
item_set_personalized,
migrations.RunPython.noop,
),
]

View File

@@ -310,6 +310,8 @@ class Item(LoggedModel):
:type tax_rate: decimal.Decimal
:param admission: ``True``, if this item allows persons to enter the event (as opposed to e.g. merchandise)
:type admission: bool
:param personalized: ``True``, if attendee information should be collected for this ticket
:type personalized: bool
:param picture: A product picture to be shown next to the product description
:type picture: File
:param available_from: The date this product goes on sale
@@ -396,8 +398,14 @@ class Item(LoggedModel):
admission = models.BooleanField(
verbose_name=_("Is an admission ticket"),
help_text=_(
'Whether or not buying this product allows a person to enter '
'your event'
'Whether or not buying this product allows a person to enter your event'
),
default=False
)
personalized = models.BooleanField(
verbose_name=_("Is a personalized ticket"),
help_text=_(
'Whether or not buying this product allows to enter attendee information'
),
default=False
)
@@ -578,6 +586,10 @@ class Item(LoggedModel):
return self.event.settings.show_quota_left
return self.show_quota_left
@property
def ask_attendee_data(self):
return self.admission and self.personalized
def tax(self, price=None, base_price_is='auto', currency=None, invoice_address=None, override_tax_rate=None, include_bundled=False):
price = price if price is not None else self.default_price

View File

@@ -808,7 +808,7 @@ class Order(LockModel, LoggedModel):
return True
ask_names = self.event.settings.get('attendee_names_asked', as_type=bool)
for cp in positions:
if (cp.item.admission and ask_names) or cp.item.questions.all():
if (cp.item.ask_attendee_data and ask_names) or cp.item.questions.all():
return True
return False # nothing there to modify

View File

@@ -206,7 +206,7 @@ DEFAULTS = {
'serializer_class': serializers.BooleanField,
'form_kwargs': dict(
label=_("Ask for attendee names"),
help_text=_("Ask for a name for all tickets which include admission to the event."),
help_text=_("Ask for a name for all personalized tickets."),
)
},
'attendee_names_required': {
@@ -229,10 +229,10 @@ DEFAULTS = {
label=_("Ask for email addresses per ticket"),
help_text=_("Normally, pretix asks for one email address per order and the order confirmation will be sent "
"only to that email address. If you enable this option, the system will additionally ask for "
"individual email addresses for every admission ticket. This might be useful if you want to "
"individual email addresses for every personalized ticket. This might be useful if you want to "
"obtain individual addresses for every attendee even in case of group orders. However, "
"pretix will send the order confirmation by default only to the one primary email address, not to "
"the per-attendee addresses. You can however enable this in the E-mail settings."),
"the per-attendee addresses. You can however enable this in the email settings."),
)
},
'attendee_emails_required': {
@@ -242,7 +242,7 @@ DEFAULTS = {
'serializer_class': serializers.BooleanField,
'form_kwargs': dict(
label=_("Require email addresses per ticket"),
help_text=_("Require customers to fill in individual e-mail addresses for all admission tickets. See the "
help_text=_("Require customers to fill in individual email addresses for all personalized tickets. See the "
"above option for more details. One email address for the order confirmation will always be "
"required regardless of this setting."),
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_settings-attendee_emails_asked'}),
@@ -2574,7 +2574,7 @@ Your {organizer} team"""))
label=_("Attendee data explanation"),
widget=I18nTextarea,
widget_kwargs={'attrs': {'rows': '2'}},
help_text=_("This text will be shown above the questions asked for every admission product. You can use it e.g. to explain "
help_text=_("This text will be shown above the questions asked for every personalized product. You can use it e.g. to explain "
"why you need information from them.")
)
},

View File

@@ -78,7 +78,7 @@ class BaseQuestionsViewMixin:
form.pos = cartpos or orderpos
form.show_copy_answers_to_addon_button = form.pos.addon_to and (
set(form.pos.addon_to.item.questions.all()) & set(form.pos.item.questions.all()) or
(form.pos.addon_to.item.admission and form.pos.item.admission and (
(form.pos.addon_to.item.ask_attendee_data and form.pos.item.ask_attendee_data and (
self.request.event.settings.attendee_names_asked or
self.request.event.settings.attendee_emails_asked or
self.request.event.settings.attendee_company_asked or