forked from CGM_Public/pretix_original
Sendmail plugin: Allow filtering for check-ins (#1382)
* Allow filtering mass-emails for Checkin Lists * Allow filtering mass emails for not checked in * Fix email filtering logic issue * Use Select2 for checkin lists selection * sendmail plugin: Make checkin list filtering optional * Remove unused constant * Re-size panel to only fit the right column * Revert incorrect JavaScript change * Change semantics of not_checked_in * Introduce a subquery to filter on position properties
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
from django import forms
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import pgettext_lazy, ugettext_lazy as _
|
||||
from django_scopes.forms import SafeModelMultipleChoiceField
|
||||
from i18nfield.forms import I18nFormField, I18nTextarea, I18nTextInput
|
||||
|
||||
from pretix.base.email import get_available_placeholders
|
||||
from pretix.base.forms import PlaceholderValidator
|
||||
from pretix.base.models import Item, Order, SubEvent
|
||||
from pretix.control.forms.widgets import Select2
|
||||
from pretix.base.models import CheckinList, Item, Order, SubEvent
|
||||
from pretix.control.forms.widgets import Select2, Select2Multiple
|
||||
|
||||
|
||||
class MailForm(forms.Form):
|
||||
@@ -27,6 +28,12 @@ class MailForm(forms.Form):
|
||||
required=True,
|
||||
queryset=Item.objects.none()
|
||||
)
|
||||
filter_checkins = forms.BooleanField(
|
||||
label=_('Filter check-in status'),
|
||||
required=False
|
||||
)
|
||||
checkin_lists = SafeModelMultipleChoiceField(queryset=CheckinList.objects.none(), required=False) # overridden later
|
||||
not_checked_in = forms.BooleanField(label=_("Send to customers not checked in"), required=False)
|
||||
subevent = forms.ModelChoiceField(
|
||||
SubEvent.objects.none(),
|
||||
label=_('Only send to customers of'),
|
||||
@@ -96,6 +103,21 @@ class MailForm(forms.Form):
|
||||
if not self.initial.get('items'):
|
||||
self.initial['items'] = event.items.all()
|
||||
|
||||
self.fields['checkin_lists'].queryset = event.checkin_lists.all()
|
||||
self.fields['checkin_lists'].widget = Select2Multiple(
|
||||
attrs={
|
||||
'data-model-select2': 'generic',
|
||||
'data-select2-url': reverse('control:event.orders.checkinlists.select2', kwargs={
|
||||
'event': event.slug,
|
||||
'organizer': event.organizer.slug,
|
||||
}),
|
||||
'data-placeholder': _('Send to customers checked in on list'),
|
||||
'data-inverse-dependency': '#id_not_checked_in'
|
||||
}
|
||||
)
|
||||
self.fields['checkin_lists'].widget.choices = self.fields['checkin_lists'].choices
|
||||
self.fields['checkin_lists'].label = _('Send to customers checked in on list')
|
||||
|
||||
if event.has_subevents:
|
||||
self.fields['subevent'].queryset = event.subevents.all()
|
||||
self.fields['subevent'].widget = Select2(
|
||||
|
||||
@@ -9,7 +9,8 @@ from pretix.celery_app import app
|
||||
|
||||
|
||||
@app.task(base=ProfiledEventTask)
|
||||
def send_mails(event: Event, user: int, subject: dict, message: dict, orders: list, items: list, recipients: str) -> None:
|
||||
def send_mails(event: Event, user: int, subject: dict, message: dict, orders: list, items: list,
|
||||
recipients: str, filter_checkins: bool, not_checked_in: bool, checkin_lists: list) -> None:
|
||||
failures = []
|
||||
user = User.objects.get(pk=user) if user else None
|
||||
orders = Order.objects.filter(pk__in=orders, event=event)
|
||||
@@ -32,6 +33,15 @@ def send_mails(event: Event, user: int, subject: dict, message: dict, orders: li
|
||||
if p.item_id not in items and not any(a.item_id in items for a in p.addons.all()):
|
||||
continue
|
||||
|
||||
if filter_checkins:
|
||||
checkins = list(p.checkins.all())
|
||||
allowed = (
|
||||
(not_checked_in and not checkins)
|
||||
or (any(c.list_id in checkin_lists for c in checkins))
|
||||
)
|
||||
if not allowed:
|
||||
continue
|
||||
|
||||
if not p.attendee_email:
|
||||
if recipients == 'attendees':
|
||||
send_to_order = True
|
||||
|
||||
@@ -23,6 +23,14 @@
|
||||
{% if log.pdata.items %}
|
||||
<br/><span class="fa fa-shopping-cart fa-fw"></span> {{ log.pdata.items|join:", " }}
|
||||
{% endif %}
|
||||
{% if log.pdata.filter_checkins %}
|
||||
{% if log.pdata.not_checked_in %}
|
||||
<br/><span class="fa fa-check-square-o fa-fw"></span> {% trans "All customers not checked in" %}
|
||||
{% endif %}
|
||||
{% if log.pdata.checkin_lists %}
|
||||
<br/><span class="fa fa-check-square-o fa-fw"></span> {{ log.pdata.checkin_lists|join:", " }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if log.pdata.subevent_obj %}
|
||||
<br/><span class="fa fa-calendar fa-fw"></span> {{ log.pdata.subevent_obj }}
|
||||
{% endif %}
|
||||
|
||||
@@ -13,6 +13,23 @@
|
||||
{% bootstrap_field form.subevent layout='horizontal' %}
|
||||
{% endif %}
|
||||
{% bootstrap_field form.items layout='horizontal' %}
|
||||
<div class="row">
|
||||
<div class="col-md-9 col-md-offset-3">
|
||||
<div class="panel-group">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<label data-toggle="collapse" data-target="#checkin_filter">
|
||||
{{ form.filter_checkins }} {{ form.filter_checkins.label }}
|
||||
</label>
|
||||
</div>
|
||||
<div id="checkin_filter" class="panel-body panel-collapse collapse {% if form.filter_checkins.value %} in {% else %} out {% endif %}">
|
||||
{% bootstrap_field form.not_checked_in layout='horizontal' %}
|
||||
{% bootstrap_field form.checkin_lists layout='horizontal' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% bootstrap_field form.subject layout='horizontal' %}
|
||||
{% bootstrap_field form.message layout='horizontal' %}
|
||||
{% if request.method == "POST" %}
|
||||
|
||||
@@ -2,7 +2,7 @@ import logging
|
||||
|
||||
import bleach
|
||||
from django.contrib import messages
|
||||
from django.db.models import Q
|
||||
from django.db.models import Exists, OuterRef, Q
|
||||
from django.http import Http404
|
||||
from django.shortcuts import redirect
|
||||
from django.utils.timezone import now
|
||||
@@ -11,7 +11,7 @@ from django.views.generic import FormView, ListView
|
||||
|
||||
from pretix.base.email import get_available_placeholders
|
||||
from pretix.base.i18n import LazyI18nString, language
|
||||
from pretix.base.models import LogEntry, Order
|
||||
from pretix.base.models import LogEntry, Order, OrderPosition
|
||||
from pretix.base.models.event import SubEvent
|
||||
from pretix.base.services.mail import TolerantDict
|
||||
from pretix.base.templatetags.rich_text import markdown_compile_email
|
||||
@@ -53,6 +53,9 @@ class SenderView(EventPermissionRequiredMixin, FormView):
|
||||
kwargs['initial']['items'] = self.request.event.items.filter(
|
||||
id=logentry.parsed_data['item']['id']
|
||||
)
|
||||
if 'checkin_lists' in logentry.parsed_data:
|
||||
kwargs['initial']['checkin_lists'] = logentry.parsed_data['checkin_lists']
|
||||
kwargs['initial']['filter_checkins'] = logentry.parsed_data.get('filter_checkins', False)
|
||||
if logentry.parsed_data.get('subevent'):
|
||||
try:
|
||||
kwargs['initial']['subevent'] = self.request.event.subevents.get(
|
||||
@@ -74,12 +77,30 @@ class SenderView(EventPermissionRequiredMixin, FormView):
|
||||
if 'overdue' in form.cleaned_data['sendto']:
|
||||
statusq |= Q(status=Order.STATUS_PENDING, expires__lt=now())
|
||||
orders = qs.filter(statusq)
|
||||
orders = orders.filter(all_positions__item_id__in=[i.pk for i in form.cleaned_data.get('items')],
|
||||
all_positions__canceled=False)
|
||||
|
||||
opq = OrderPosition.objects.filter(
|
||||
order=OuterRef('pk'),
|
||||
canceled=False,
|
||||
item_id__in=[i.pk for i in form.cleaned_data.get('items')],
|
||||
)
|
||||
|
||||
if form.cleaned_data.get('filter_checkins'):
|
||||
ql = []
|
||||
if form.cleaned_data.get('not_checked_in'):
|
||||
ql.append(Q(checkins__list_id=None))
|
||||
if form.cleaned_data.get('checkin_lists'):
|
||||
ql.append(Q(
|
||||
checkins__list_id__in=[i.pk for i in form.cleaned_data.get('checkin_lists', [])],
|
||||
))
|
||||
if len(ql) == 2:
|
||||
opq = opq.filter(ql[0] | ql[1])
|
||||
else:
|
||||
opq = opq.filter(ql[0])
|
||||
|
||||
if form.cleaned_data.get('subevent'):
|
||||
orders = orders.filter(all_positions__subevent__in=(form.cleaned_data.get('subevent'),),
|
||||
all_positions__canceled=False)
|
||||
orders = orders.distinct()
|
||||
opq = opq.filter(subevent=form.cleaned_data.get('subevent'))
|
||||
|
||||
orders = orders.annotate(match_pos=Exists(opq)).filter(match_pos=True).distinct()
|
||||
|
||||
self.output = {}
|
||||
if not orders:
|
||||
@@ -118,7 +139,10 @@ class SenderView(EventPermissionRequiredMixin, FormView):
|
||||
'subject': form.cleaned_data['subject'].data,
|
||||
'message': form.cleaned_data['message'].data,
|
||||
'orders': [o.pk for o in orders],
|
||||
'items': [i.pk for i in form.cleaned_data.get('items')]
|
||||
'items': [i.pk for i in form.cleaned_data.get('items')],
|
||||
'not_checked_in': form.cleaned_data.get('not_checked_in'),
|
||||
'checkin_lists': [i.pk for i in form.cleaned_data.get('checkin_lists')],
|
||||
'filter_checkins': form.cleaned_data.get('filter_checkins'),
|
||||
}
|
||||
)
|
||||
self.request.event.log_action('pretix.plugins.sendmail.sent',
|
||||
@@ -159,6 +183,9 @@ class EmailHistoryView(EventPermissionRequiredMixin, ListView):
|
||||
itemcache = {
|
||||
i.pk: str(i) for i in self.request.event.items.all()
|
||||
}
|
||||
checkin_list_cache = {
|
||||
i.pk: str(i) for i in self.request.event.checkin_lists.all()
|
||||
}
|
||||
status = dict(Order.STATUS_CHOICE)
|
||||
status['overdue'] = _('pending with payment overdue')
|
||||
status['r'] = status['c']
|
||||
@@ -176,6 +203,9 @@ class EmailHistoryView(EventPermissionRequiredMixin, ListView):
|
||||
log.pdata['items'] = [
|
||||
itemcache[i['id']] for i in log.pdata.get('items', [])
|
||||
]
|
||||
log.pdata['checkin_lists'] = [
|
||||
checkin_list_cache[i] for i in log.pdata.get('checkin_lists', []) if i in checkin_list_cache
|
||||
]
|
||||
if log.pdata.get('subevent'):
|
||||
try:
|
||||
log.pdata['subevent_obj'] = self.request.event.subevents.get(pk=log.pdata['subevent']['id'])
|
||||
|
||||
Reference in New Issue
Block a user