Seat-specific vouchers (#1486)

* Basic functionality

* API

* Do not delete seats with vouchers

* Show seat in list of seats

* Validate availability of seats

* Fix invalid logic in Seat.is_available

* Show voucher name in edit form
This commit is contained in:
Raphael Michel
2019-11-15 10:56:34 +01:00
committed by GitHub
parent f79df47b78
commit a2c1c69d7e
19 changed files with 474 additions and 42 deletions

View File

@@ -38,7 +38,7 @@ class VoucherForm(I18nModelForm):
localized_fields = '__all__'
fields = [
'code', 'valid_until', 'block_quota', 'allow_ignore_quota', 'value', 'tag',
'comment', 'max_usages', 'price_mode', 'subevent', 'show_hidden_items'
'comment', 'max_usages', 'price_mode', 'subevent', 'show_hidden_items',
]
field_classes = {
'valid_until': SplitDateTimeField,
@@ -115,6 +115,16 @@ class VoucherForm(I18nModelForm):
self.fields['itemvar'].widget.choices = self.fields['itemvar'].choices
self.fields['itemvar'].required = True
if self.instance.event.seating_plan or self.instance.event.subevents.filter(seating_plan__isnull=False).exists():
self.fields['seat'] = forms.CharField(
label=_("Specific seat ID"),
max_length=255,
required=False,
widget=forms.TextInput(attrs={'data-seat-guid-field': '1'}),
initial=self.instance.seat.seat_guid if self.instance.seat else '',
help_text=str(self.instance.seat) if self.instance.seat else '',
)
def clean(self):
data = super().clean()
@@ -179,6 +189,8 @@ class VoucherForm(I18nModelForm):
self.instance.quota, self.instance.item, self.instance.variation
)
Voucher.clean_voucher_code(data, self.instance.event, self.instance.pk)
if 'seat' in self.fields and data.get('seat'):
self.instance.seat = Voucher.clean_seat_id(data, self.instance.item, self.instance.event, self.instance.pk)
voucher_form_validation.send(sender=self.instance.event, form=self, data=data)
@@ -271,6 +283,13 @@ class VoucherBulkForm(VoucherForm):
super().__init__(*args, **kwargs)
self._set_field_placeholders('send_subject', ['event', 'name'])
self._set_field_placeholders('send_message', ['event', 'voucher_list', 'name'])
if 'seat' in self.fields:
self.fields['seats'] = forms.CharField(
label=_("Specific seat IDs"),
required=False,
widget=forms.Textarea(attrs={'data-seat-guid-field': '1'}),
initial=self.instance.seat.seat_guid if self.instance.seat else '',
)
def clean_send_recipients(self):
raw = self.cleaned_data['send_recipients']
@@ -331,6 +350,18 @@ class VoucherBulkForm(VoucherForm):
if code_len != recp_len:
raise ValidationError(_('You generated {codes} vouchers, but entered recipients for {recp} vouchers.').format(codes=code_len, recp=recp_len))
if data.get('seats'):
seatids = [s.strip() for s in data.get('seats').strip().split() if s]
print(seatids)
if len(seatids) != len(data.get('codes')):
raise ValidationError(_('You need to specify as many seats as voucher codes.'))
data['seats'] = []
for s in seatids:
data['seat'] = s
data['seats'].append(Voucher.clean_seat_id(data, self.instance.item, self.instance.event, None))
else:
data['seats'] = []
return data
def save(self, event, *args, **kwargs):
@@ -339,6 +370,10 @@ class VoucherBulkForm(VoucherForm):
obj = modelcopy(self.instance)
obj.event = event
obj.code = code
try:
obj.seat = self.cleaned_data['seats'].pop()
except IndexError:
pass
data = dict(self.cleaned_data)
data['code'] = code
data['bulk'] = True

View File

@@ -65,6 +65,9 @@
{% if form.subevent %}
{% bootstrap_field form.subevent layout="control" %}
{% endif %}
{% if "seats" in form.fields %}
{% bootstrap_field form.seats layout="control" %}
{% endif %}
</fieldset>
<fieldset>
<legend>{% trans "Advanced settings" %}</legend>

View File

@@ -67,6 +67,9 @@
{% if form.subevent %}
{% bootstrap_field form.subevent layout="control" %}
{% endif %}
{% if "seat" in form.fields %}
{% bootstrap_field form.seat layout="control" %}
{% endif %}
</fieldset>
<fieldset>
<legend>{% trans "Advanced settings" %}</legend>

View File

@@ -133,6 +133,7 @@
Any product in quota "{{ quota }}"
{% endblocktrans %}
{% endif %}
{% if v.seat %}<br><small class="text-muted">{{ v.seat }}</small>{% endif %}
</td>
{% if request.event.has_subevents %}
<td>{{ v.subevent.name }} {{ v.subevent.get_date_range_display }}</td>

View File

@@ -235,11 +235,11 @@ def seat_select2(request, **kwargs):
if request.event.has_subevents:
try:
qs = request.event.subevents.get(active=True, pk=request.GET.get('subevent', 0)).free_seats
qs = request.event.subevents.get(active=True, pk=request.GET.get('subevent', 0)).free_seats()
except SubEvent.DoesNotExist:
qs = request.event.seats.none()
else:
qs = request.event.free_seats
qs = request.event.free_seats()
qs = qs.filter(
Q(name__icontains=query) | Q(seat_guid__icontains=query)
).order_by('name').select_related('product', 'subevent')

View File

@@ -37,7 +37,9 @@ class VoucherList(PaginationMixin, EventPermissionRequiredMixin, ListView):
permission = 'can_view_vouchers'
def get_queryset(self):
qs = self.request.event.vouchers.filter(waitinglistentries__isnull=True).select_related('item', 'variation')
qs = self.request.event.vouchers.filter(waitinglistentries__isnull=True).select_related(
'item', 'variation', 'seat'
)
if self.filter_form.is_valid():
qs = self.filter_form.filter_qs(qs)