mirror of
https://github.com/pretix/pretix.git
synced 2026-04-26 23:52:35 +00:00
Add filter for date for multiple exporters
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
from collections import OrderedDict
|
||||
from decimal import Decimal
|
||||
|
||||
import dateutil
|
||||
import pytz
|
||||
from django import forms
|
||||
from django.db.models import (
|
||||
CharField, Count, DateTimeField, IntegerField, Max, OuterRef, Q, Subquery,
|
||||
Sum,
|
||||
Case, CharField, Count, DateTimeField, F, IntegerField, Max, Min, OuterRef,
|
||||
Q, Subquery, Sum, When,
|
||||
)
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.db.models.functions import Coalesce, TruncDate
|
||||
from django.dispatch import receiver
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.timezone import get_current_timezone, now
|
||||
@@ -48,28 +49,61 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
|
||||
@property
|
||||
def additional_form_fields(self):
|
||||
return OrderedDict(
|
||||
[
|
||||
('paid_only',
|
||||
forms.BooleanField(
|
||||
label=_('Only paid orders'),
|
||||
initial=True,
|
||||
required=False
|
||||
)),
|
||||
('include_payment_amounts',
|
||||
forms.BooleanField(
|
||||
label=_('Include payment amounts'),
|
||||
initial=False,
|
||||
required=False
|
||||
)),
|
||||
('group_multiple_choice',
|
||||
forms.BooleanField(
|
||||
label=_('Show multiple choice answers grouped in one column'),
|
||||
initial=False,
|
||||
required=False
|
||||
)),
|
||||
]
|
||||
)
|
||||
d = [
|
||||
('paid_only',
|
||||
forms.BooleanField(
|
||||
label=_('Only paid orders'),
|
||||
initial=True,
|
||||
required=False
|
||||
)),
|
||||
('include_payment_amounts',
|
||||
forms.BooleanField(
|
||||
label=_('Include payment amounts'),
|
||||
initial=False,
|
||||
required=False
|
||||
)),
|
||||
('group_multiple_choice',
|
||||
forms.BooleanField(
|
||||
label=_('Show multiple choice answers grouped in one column'),
|
||||
initial=False,
|
||||
required=False
|
||||
)),
|
||||
('date_from',
|
||||
forms.DateField(
|
||||
label=_('Start date'),
|
||||
widget=forms.DateInput(attrs={'class': 'datepickerfield'}),
|
||||
required=False,
|
||||
help_text=_('Only include orders created on or after this date.')
|
||||
)),
|
||||
('date_to',
|
||||
forms.DateField(
|
||||
label=_('End date'),
|
||||
widget=forms.DateInput(attrs={'class': 'datepickerfield'}),
|
||||
required=False,
|
||||
help_text=_('Only include orders issued on or before this date.')
|
||||
)),
|
||||
('event_date_from',
|
||||
forms.DateField(
|
||||
label=_('Start event date'),
|
||||
widget=forms.DateInput(attrs={'class': 'datepickerfield'}),
|
||||
required=False,
|
||||
help_text=_('Only include orders including at least one ticket for a date on or after this date. '
|
||||
'Will also include other dates in case of mixed orders!')
|
||||
)),
|
||||
('event_date_to',
|
||||
forms.DateField(
|
||||
label=_('End event date'),
|
||||
widget=forms.DateInput(attrs={'class': 'datepickerfield'}),
|
||||
required=False,
|
||||
help_text=_('Only include orders including at least one ticket for a date on or after this date. '
|
||||
'Will also include other dates in case of mixed orders!')
|
||||
)),
|
||||
]
|
||||
d = OrderedDict(d)
|
||||
if not self.is_multievent and not self.event.has_subevents:
|
||||
del d['event_date_from']
|
||||
del d['event_date_to']
|
||||
return d
|
||||
|
||||
def _get_all_payment_methods(self, qs):
|
||||
pps = dict(get_all_payment_providers())
|
||||
@@ -107,6 +141,51 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
def event_object_cache(self):
|
||||
return {e.pk: e for e in self.events}
|
||||
|
||||
def _date_filter(self, qs, form_data, rel):
|
||||
annotations = {}
|
||||
filters = {}
|
||||
|
||||
if form_data.get('date_from'):
|
||||
date_value = form_data.get('date_from')
|
||||
if isinstance(date_value, str):
|
||||
date_value = dateutil.parser.parse(date_value).date()
|
||||
|
||||
annotations['date'] = TruncDate(f'{rel}datetime')
|
||||
filters['date__gte'] = date_value
|
||||
|
||||
if form_data.get('date_to'):
|
||||
date_value = form_data.get('date_to')
|
||||
if isinstance(date_value, str):
|
||||
date_value = dateutil.parser.parse(date_value).date()
|
||||
|
||||
annotations['date'] = TruncDate(f'{rel}datetime')
|
||||
filters['date__lte'] = date_value
|
||||
|
||||
if form_data.get('event_date_from'):
|
||||
date_value = form_data.get('event_date_from')
|
||||
if isinstance(date_value, str):
|
||||
date_value = dateutil.parser.parse(date_value).date()
|
||||
|
||||
annotations['event_date_max'] = Case(
|
||||
When(**{f'{rel}event__has_subevents': True}, then=Max(f'{rel}all_positions__subevent__date_from')),
|
||||
default=F(f'{rel}event__date_from'),
|
||||
)
|
||||
filters['event_date_max__gte'] = date_value
|
||||
|
||||
if form_data.get('event_date_to'):
|
||||
date_value = form_data.get('event_date_to')
|
||||
if isinstance(date_value, str):
|
||||
date_value = dateutil.parser.parse(date_value).date()
|
||||
|
||||
annotations['event_date_min'] = Case(
|
||||
When(**{f'{rel}event__has_subevents': True}, then=Min(f'{rel}all_positions__subevent__date_from')),
|
||||
default=F(f'{rel}event__date_from'),
|
||||
)
|
||||
filters['event_date_min__lte'] = date_value
|
||||
|
||||
if filters:
|
||||
return qs.annotate(**annotations).filter(**filters)
|
||||
|
||||
def iterate_orders(self, form_data: dict):
|
||||
p_date = OrderPayment.objects.filter(
|
||||
order=OuterRef('pk'),
|
||||
@@ -143,6 +222,9 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
invoice_numbers=Subquery(i_numbers, output_field=CharField()),
|
||||
pcnt=Subquery(s, output_field=IntegerField())
|
||||
).select_related('invoice_address')
|
||||
|
||||
qs = self._date_filter(qs, form_data, rel='')
|
||||
|
||||
if form_data['paid_only']:
|
||||
qs = qs.filter(status=Order.STATUS_PAID)
|
||||
tax_rates = self._get_all_tax_rates(qs)
|
||||
@@ -308,6 +390,8 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
if form_data['paid_only']:
|
||||
qs = qs.filter(order__status=Order.STATUS_PAID)
|
||||
|
||||
qs = self._date_filter(qs, form_data, rel='order__')
|
||||
|
||||
headers = [
|
||||
_('Event slug'),
|
||||
_('Order code'),
|
||||
@@ -406,6 +490,8 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
if form_data['paid_only']:
|
||||
qs = qs.filter(order__status=Order.STATUS_PAID)
|
||||
|
||||
qs = self._date_filter(qs, form_data, rel='order__')
|
||||
|
||||
has_subevents = self.events.filter(has_subevents=True).exists()
|
||||
|
||||
headers = [
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import copy
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime, time, timedelta
|
||||
from io import BytesIO
|
||||
from typing import Tuple
|
||||
|
||||
import dateutil.parser
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.contrib.staticfiles import finders
|
||||
from django.core.files import File
|
||||
from django.core.files.storage import default_storage
|
||||
from django.db.models import Exists, OuterRef
|
||||
from django.db.models import Exists, OuterRef, Q
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.utils.timezone import make_aware
|
||||
from django.utils.translation import gettext as _, gettext_lazy
|
||||
from jsonfallback.functions import JSONExtract
|
||||
from reportlab.lib import pagesizes
|
||||
@@ -236,12 +239,27 @@ class BadgeExporter(BaseExporter):
|
||||
'want to print to a sheet of stickers with a regular office printer. Please note '
|
||||
'that your individual badge layouts must already be in the correct size.')
|
||||
)),
|
||||
('date_from',
|
||||
forms.DateField(
|
||||
label=_('Start date'),
|
||||
widget=forms.DateInput(attrs={'class': 'datepickerfield'}),
|
||||
required=False,
|
||||
help_text=_('Only include tickets for dates on or after this date.')
|
||||
)),
|
||||
('date_to',
|
||||
forms.DateField(
|
||||
label=_('End date'),
|
||||
widget=forms.DateInput(attrs={'class': 'datepickerfield'}),
|
||||
required=False,
|
||||
help_text=_('Only include tickets for dates on or before this date.')
|
||||
)),
|
||||
('order_by',
|
||||
forms.ChoiceField(
|
||||
label=_('Sort by'),
|
||||
choices=[
|
||||
('name', _('Attendee name')),
|
||||
('code', _('Order code')),
|
||||
('date', _('Event date')),
|
||||
] + ([
|
||||
('name:{}'.format(k), _('Attendee name: {part}').format(part=label))
|
||||
for k, label, w in name_scheme['fields']
|
||||
@@ -266,10 +284,26 @@ class BadgeExporter(BaseExporter):
|
||||
else:
|
||||
qs = qs.filter(order__status__in=[Order.STATUS_PAID])
|
||||
|
||||
if form_data.get('date_from'):
|
||||
dt = make_aware(datetime.combine(
|
||||
dateutil.parser.parse(form_data['date_from']).date(),
|
||||
time(hour=0, minute=0, second=0)
|
||||
), self.event.timezone)
|
||||
qs = qs.filter(Q(subevent__date_from__gte=dt) | Q(subevent__isnull=True, order__event__date_from__gte=dt))
|
||||
|
||||
if form_data.get('date_to'):
|
||||
dt = make_aware(datetime.combine(
|
||||
dateutil.parser.parse(form_data['date_to']).date() + timedelta(days=1),
|
||||
time(hour=0, minute=0, second=0)
|
||||
), self.event.timezone)
|
||||
qs = qs.filter(Q(subevent__date_from__lt=dt) | Q(subevent__isnull=True, order__event__date_from__lt=dt))
|
||||
|
||||
if form_data.get('order_by') == 'name':
|
||||
qs = qs.order_by('attendee_name_cached', 'order__code')
|
||||
elif form_data.get('order_by') == 'code':
|
||||
qs = qs.order_by('order__code')
|
||||
elif form_data.get('order_by') == 'date':
|
||||
qs = qs.annotate(ed=Coalesce('subevent__date_from', 'order__event__date_from')).order_by('ed', 'order__code')
|
||||
elif form_data.get('order_by', '').startswith('name:'):
|
||||
part = form_data['order_by'][5:]
|
||||
qs = qs.annotate(
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime, time, timedelta
|
||||
from io import BytesIO
|
||||
|
||||
import dateutil.parser
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.core.files.base import ContentFile
|
||||
from django.db.models import Q
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.utils.timezone import make_aware
|
||||
from django.utils.translation import gettext as _, gettext_lazy
|
||||
from jsonfallback.functions import JSONExtract
|
||||
from PyPDF2.merger import PdfFileMerger
|
||||
@@ -32,12 +36,27 @@ class AllTicketsPDF(BaseExporter):
|
||||
label=_('Include pending orders'),
|
||||
required=False
|
||||
)),
|
||||
('date_from',
|
||||
forms.DateField(
|
||||
label=_('Start date'),
|
||||
widget=forms.DateInput(attrs={'class': 'datepickerfield'}),
|
||||
required=False,
|
||||
help_text=_('Only include tickets for dates on or after this date.')
|
||||
)),
|
||||
('date_to',
|
||||
forms.DateField(
|
||||
label=_('End date'),
|
||||
widget=forms.DateInput(attrs={'class': 'datepickerfield'}),
|
||||
required=False,
|
||||
help_text=_('Only include tickets for dates on or before this date.')
|
||||
)),
|
||||
('order_by',
|
||||
forms.ChoiceField(
|
||||
label=_('Sort by'),
|
||||
choices=[
|
||||
('name', _('Attendee name')),
|
||||
('code', _('Order code')),
|
||||
('date', _('Event date')),
|
||||
] + ([
|
||||
('name:{}'.format(k), _('Attendee name: {part}').format(part=label))
|
||||
for k, label, w in name_scheme['fields']
|
||||
@@ -45,6 +64,11 @@ class AllTicketsPDF(BaseExporter):
|
||||
)),
|
||||
]
|
||||
)
|
||||
|
||||
if not self.is_multievent and not self.event.has_subevents:
|
||||
del d['date_from']
|
||||
del d['date_to']
|
||||
|
||||
return d
|
||||
|
||||
def render(self, form_data):
|
||||
@@ -60,10 +84,26 @@ class AllTicketsPDF(BaseExporter):
|
||||
else:
|
||||
qs = qs.filter(order__status__in=[Order.STATUS_PAID])
|
||||
|
||||
if form_data.get('date_from'):
|
||||
dt = make_aware(datetime.combine(
|
||||
dateutil.parser.parse(form_data['date_from']).date(),
|
||||
time(hour=0, minute=0, second=0)
|
||||
), self.event.timezone)
|
||||
qs = qs.filter(Q(subevent__date_from__gte=dt) | Q(subevent__isnull=True, order__event__date_from__gte=dt))
|
||||
|
||||
if form_data.get('date_to'):
|
||||
dt = make_aware(datetime.combine(
|
||||
dateutil.parser.parse(form_data['date_to']).date() + timedelta(days=1),
|
||||
time(hour=0, minute=0, second=0)
|
||||
), self.event.timezone)
|
||||
qs = qs.filter(Q(subevent__date_from__lt=dt) | Q(subevent__isnull=True, order__event__date_from__lt=dt))
|
||||
|
||||
if form_data.get('order_by') == 'name':
|
||||
qs = qs.order_by('attendee_name_cached', 'order__code')
|
||||
elif form_data.get('order_by') == 'code':
|
||||
qs = qs.order_by('order__code')
|
||||
elif form_data.get('order_by') == 'date':
|
||||
qs = qs.annotate(ed=Coalesce('subevent__date_from', 'order__event__date_from')).order_by('ed', 'order__code')
|
||||
elif form_data.get('order_by', '').startswith('name:'):
|
||||
part = form_data['order_by'][5:]
|
||||
qs = qs.annotate(
|
||||
|
||||
@@ -23,7 +23,9 @@ from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from pretix.base.channels import get_all_sales_channels
|
||||
from pretix.base.models import ItemVariation, Quota, SeatCategoryMapping, Voucher
|
||||
from pretix.base.models import (
|
||||
ItemVariation, Quota, SeatCategoryMapping, Voucher,
|
||||
)
|
||||
from pretix.base.models.event import SubEvent
|
||||
from pretix.base.models.items import (
|
||||
ItemBundle, SubEventItem, SubEventItemVariation,
|
||||
|
||||
@@ -19,7 +19,7 @@ from pytz import UTC
|
||||
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import (
|
||||
Event, EventMetaValue, SubEvent, SubEventMetaValue, Quota,
|
||||
Event, EventMetaValue, Quota, SubEvent, SubEventMetaValue,
|
||||
)
|
||||
from pretix.base.services.quotas import QuotaAvailability
|
||||
from pretix.helpers.compat import date_fromisocalendar
|
||||
|
||||
Reference in New Issue
Block a user