Fix #978 -- Allow to split names (#1049)

- [x] attendee names
- [x] Invoice address names
- [x] Data migration
- [x] API serializers
  - [x] orderposition
  - [x] cartposition
  - [x] invoiceaddress
  - [x] checkinlistposition
- [x] position API search
- [x] invoice API search
- [x] business/individual required toggle
- [x] Split columns in CSV exports
- [x] ticket editor
- [x] shredder
- [x] ticket/invoice sample data
- [x] order search
- [x] Handle changed naming scheme
- [x] tests
- [x] make use in:
  - [x] Boabee
  - [x] Certificate download order
  - [x] Badge download order
  - [x] Ticket download order
- [x] Document new MySQL requirement
- [x] Plugins
This commit is contained in:
Raphael Michel
2018-11-05 15:43:21 +01:00
committed by GitHub
parent 7039374588
commit 94be46ffdb
71 changed files with 1219 additions and 244 deletions

View File

@@ -20,6 +20,7 @@ from pretix.base.forms import I18nModelForm, PlaceholderValidator, SettingsForm
from pretix.base.models import Event, Organizer, TaxRule
from pretix.base.models.event import EventMetaValue, SubEvent
from pretix.base.reldate import RelativeDateField, RelativeDateTimeField
from pretix.base.settings import PERSON_NAME_SCHEMES
from pretix.control.forms import (
ExtFileField, MultipleLanguagesWidget, SingleLanguageWidget, SlugWidget,
SplitDateTimeField, SplitDateTimePickerWidget,
@@ -338,6 +339,12 @@ class EventSettingsForm(SettingsForm):
required=False,
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_settings-attendee_names_asked'}),
)
name_scheme = forms.ChoiceField(
label=_("Name format"),
help_text=_("This defines how pretix will ask for human names. Changing this after you already received "
"orders might lead to unexpected behaviour when sorting or changing names."),
required=True,
)
attendee_emails_asked = forms.BooleanField(
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 "
@@ -419,6 +426,13 @@ class EventSettingsForm(SettingsForm):
'e.g. I hereby confirm that I have read and agree with the event organizer\'s terms of service '
'and agree with them.'
)
self.fields['name_scheme'].choices = (
(k, _('Ask for {fields}, display like {example}').format(
fields=' + '.join(str(vv[1]) for vv in v['fields']),
example=v['concatenation'](v['sample'])
))
for k, v in PERSON_NAME_SCHEMES.items()
)
class PaymentSettingsForm(SettingsForm):

View File

@@ -129,7 +129,7 @@ class OrderFilterForm(FilterForm):
matching_positions = OrderPosition.objects.filter(
Q(order=OuterRef('pk')) & Q(
Q(attendee_name__icontains=u) | Q(attendee_email__icontains=u)
Q(attendee_name_cached__icontains=u) | Q(attendee_email__icontains=u)
| Q(secret__istartswith=u)
)
).values('id')
@@ -137,7 +137,7 @@ class OrderFilterForm(FilterForm):
qs = qs.annotate(has_pos=Exists(matching_positions)).filter(
code
| Q(email__icontains=u)
| Q(invoice_address__name__icontains=u)
| Q(invoice_address__name_cached__icontains=u)
| Q(invoice_address__company__icontains=u)
| Q(pk__in=matching_invoices)
| Q(comment__icontains=u)
@@ -568,9 +568,9 @@ class CheckInFilterForm(FilterForm):
'item': ('item__name', 'variation__value', 'order__code'),
'-item': ('-item__name', '-variation__value', '-order__code'),
'name': {'_order': F('display_name').asc(nulls_first=True),
'display_name': Coalesce('attendee_name', 'addon_to__attendee_name')},
'display_name': Coalesce('attendee_name_cached', 'addon_to__attendee_name_cached')},
'-name': {'_order': F('display_name').desc(nulls_last=True),
'display_name': Coalesce('attendee_name', 'addon_to__attendee_name')},
'display_name': Coalesce('attendee_name_cached', 'addon_to__attendee_name_cached')},
}
user = forms.CharField(
@@ -615,10 +615,10 @@ class CheckInFilterForm(FilterForm):
Q(order__code__istartswith=u)
| Q(secret__istartswith=u)
| Q(order__email__icontains=u)
| Q(attendee_name__icontains=u)
| Q(attendee_name_cached__icontains=u)
| Q(attendee_email__icontains=u)
| Q(voucher__code__istartswith=u)
| Q(order__invoice_address__name__icontains=u)
| Q(order__invoice_address__name_cached__icontains=u)
| Q(order__invoice_address__company__icontains=u)
)

View File

@@ -63,6 +63,7 @@
{% bootstrap_field sform.max_items_per_order layout="control" %}
{% bootstrap_field sform.attendee_names_asked layout="control" %}
{% bootstrap_field sform.attendee_names_required layout="control" %}
{% bootstrap_field sform.name_scheme layout="control" %}
{% bootstrap_field sform.order_email_asked_twice layout="control" %}
{% bootstrap_field sform.attendee_emails_asked layout="control" %}
{% bootstrap_field sform.attendee_emails_required layout="control" %}

View File

@@ -644,7 +644,7 @@ class MailSettingsRendererPreview(MailSettingsPreview):
expires=now(), code="PREVIEW", total=119)
item = request.event.items.create(name=ugettext("Sample product"), default_price=42.23,
description=ugettext("Sample product description"))
order.positions.create(item=item, attendee_name=ugettext("John Doe"), price=item.default_price)
order.positions.create(item=item, attendee_name_parts={'full_name': ugettext("John Doe")}, price=item.default_price)
v = renderers[request.GET.get('renderer')].render(
v,
str(request.event.settings.mail_text_signature),

View File

@@ -18,6 +18,7 @@ from django.views.generic import TemplateView
from pretix.base.i18n import language
from pretix.base.models import CachedFile, InvoiceAddress, OrderPosition
from pretix.base.pdf import get_variables
from pretix.base.settings import PERSON_NAME_SCHEMES
from pretix.control.permissions import EventPermissionRequiredMixin
from pretix.helpers.database import rolledback_transaction
from pretix.presale.style import get_fonts
@@ -65,11 +66,13 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
locale=self.request.event.settings.locale,
expires=now(), code="PREVIEW1234", total=119)
p = order.positions.create(item=item, attendee_name=_("John Doe"), price=item.default_price)
order.positions.create(item=item2, attendee_name=_("John Doe"), price=item.default_price, addon_to=p)
order.positions.create(item=item2, attendee_name=_("John Doe"), price=item.default_price, addon_to=p)
scheme = PERSON_NAME_SCHEMES[self.request.event.settings.name_scheme]
sample = {k: str(v) for k, v in scheme['sample'].items()}
p = order.positions.create(item=item, attendee_name_parts=sample, price=item.default_price)
order.positions.create(item=item2, attendee_name_parts=sample, price=item.default_price, addon_to=p)
order.positions.create(item=item2, attendee_name_parts=sample, price=item.default_price, addon_to=p)
InvoiceAddress.objects.create(order=order, name=_("John Doe"), company=_("Sample company"))
InvoiceAddress.objects.create(order=order, name_parts=sample, company=_("Sample company"))
return p
def generate(self, p: OrderPosition, override_layout=None, override_background=None):

View File

@@ -36,7 +36,8 @@ class OrderSearch(PaginationMixin, ListView):
qs = self.filter_form.filter_qs(qs)
return qs.only(
'id', 'invoice_address__name', 'code', 'event', 'email', 'datetime', 'total', 'status'
'id', 'invoice_address__name_cached', 'invoice_address__name_parts', 'code', 'event', 'email',
'datetime', 'total', 'status'
).prefetch_related(
'event', 'event__organizer'
)