forked from CGM_Public/pretix_original
Exports: Add predefined timeframes (#3027)
Co-authored-by: Richard Schreiber <schreiber@rami.io>
This commit is contained in:
@@ -33,7 +33,6 @@
|
||||
# License for the specific language governing permissions and limitations under the License.
|
||||
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime, time, timedelta
|
||||
|
||||
import bleach
|
||||
import dateutil.parser
|
||||
@@ -44,7 +43,7 @@ from django.db.models import (
|
||||
from django.db.models.functions import Coalesce, NullIf
|
||||
from django.urls import reverse
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.timezone import is_aware, make_aware
|
||||
from django.utils.timezone import is_aware, make_aware, now
|
||||
from django.utils.translation import (
|
||||
gettext as _, gettext_lazy, pgettext, pgettext_lazy,
|
||||
)
|
||||
@@ -58,6 +57,10 @@ from pretix.base.models import (
|
||||
)
|
||||
from pretix.base.settings import PERSON_NAME_SCHEMES
|
||||
from pretix.base.templatetags.money import money_filter
|
||||
from pretix.base.timeframes import (
|
||||
DateFrameField,
|
||||
resolve_timeframe_to_datetime_start_inclusive_end_exclusive,
|
||||
)
|
||||
from pretix.control.forms.widgets import Select2
|
||||
from pretix.helpers.templatetags.jsonfield import JSONExtract
|
||||
from pretix.plugins.reports.exporters import ReportlabExportMixin
|
||||
@@ -78,19 +81,12 @@ class CheckInListMixin(BaseExporter):
|
||||
),
|
||||
initial=self.event.checkin_lists.first()
|
||||
)),
|
||||
('date_from',
|
||||
forms.DateField(
|
||||
label=_('Start date'),
|
||||
widget=forms.DateInput(attrs={'class': 'datepickerfield'}),
|
||||
('date_range',
|
||||
DateFrameField(
|
||||
label=_('Date range'),
|
||||
include_future_frames=True,
|
||||
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.')
|
||||
help_text=_('Only include tickets for dates within this range.')
|
||||
)),
|
||||
('secrets',
|
||||
forms.BooleanField(
|
||||
@@ -129,8 +125,7 @@ class CheckInListMixin(BaseExporter):
|
||||
)
|
||||
|
||||
if not self.event.has_subevents:
|
||||
del d['date_from']
|
||||
del d['date_to']
|
||||
del d['date_range']
|
||||
|
||||
d['list'].queryset = self.event.checkin_lists.all()
|
||||
d['list'].widget = Select2(
|
||||
@@ -181,19 +176,12 @@ class CheckInListMixin(BaseExporter):
|
||||
if cl.subevent:
|
||||
qs = qs.filter(subevent=cl.subevent)
|
||||
|
||||
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(subevent__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(subevent__date_from__lt=dt)
|
||||
if form_data.get('date_range'):
|
||||
dt_start, dt_end = resolve_timeframe_to_datetime_start_inclusive_end_exclusive(now(), form_data['date_range'], self.timezone)
|
||||
if dt_start:
|
||||
qs = qs.filter(subevent__date_from__gte=dt_start)
|
||||
if dt_end:
|
||||
qs = qs.filter(subevent__date_to__lt=dt_end)
|
||||
|
||||
o = ()
|
||||
if self.event.has_subevents and not cl.subevent:
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
import copy
|
||||
import tempfile
|
||||
from collections import OrderedDict, defaultdict
|
||||
from datetime import date, datetime, time, timedelta
|
||||
from decimal import Decimal
|
||||
|
||||
import pytz
|
||||
@@ -47,7 +46,7 @@ from django.db import models
|
||||
from django.db.models import DateTimeField, Max, OuterRef, Subquery, Sum
|
||||
from django.template.defaultfilters import floatformat
|
||||
from django.utils.formats import date_format, localize
|
||||
from django.utils.timezone import get_current_timezone, make_aware, now
|
||||
from django.utils.timezone import get_current_timezone, now
|
||||
from django.utils.translation import (
|
||||
gettext as _, gettext_lazy, pgettext, pgettext_lazy,
|
||||
)
|
||||
@@ -59,11 +58,14 @@ from reportlab.platypus import PageBreak, Paragraph, Spacer, Table, TableStyle
|
||||
|
||||
from pretix.base.decimal import round_decimal
|
||||
from pretix.base.exporter import BaseExporter, MultiSheetListExporter
|
||||
from pretix.base.forms.widgets import DatePickerWidget
|
||||
from pretix.base.models import Order, OrderPosition
|
||||
from pretix.base.models.event import SubEvent
|
||||
from pretix.base.models.orders import OrderFee, OrderPayment
|
||||
from pretix.base.services.stats import order_overview
|
||||
from pretix.base.timeframes import (
|
||||
DateFrameField, resolve_timeframe_to_dates_inclusive,
|
||||
resolve_timeframe_to_datetime_start_inclusive_end_exclusive,
|
||||
)
|
||||
from pretix.control.forms.filter import OverviewFilterForm
|
||||
|
||||
|
||||
@@ -236,12 +238,13 @@ class OverviewReport(Report):
|
||||
|
||||
def _filter_story(self, doc, form_data, net=False):
|
||||
story = []
|
||||
if form_data.get('date_axis') and (form_data.get('date_from') or form_data.get('date_until')):
|
||||
if form_data.get('date_axis') and form_data.get('date_range'):
|
||||
d_start, d_end = resolve_timeframe_to_dates_inclusive(now(), form_data['date_range'], self.timezone)
|
||||
story += [
|
||||
Paragraph(_('{axis} between {start} and {end}').format(
|
||||
axis=dict(OverviewFilterForm(event=self.event).fields['date_axis'].choices)[form_data.get('date_axis')],
|
||||
start=date_format(form_data.get('date_from'), 'SHORT_DATE_FORMAT') if form_data.get('date_from') else '–',
|
||||
end=date_format(form_data.get('date_until'), 'SHORT_DATE_FORMAT') if form_data.get('date_until') else '–',
|
||||
start=date_format(d_start, 'SHORT_DATE_FORMAT') if d_start else '–',
|
||||
end=date_format(d_end, 'SHORT_DATE_FORMAT') if d_end else '–',
|
||||
), self.get_style()),
|
||||
Spacer(1, 5 * mm)
|
||||
]
|
||||
@@ -256,12 +259,16 @@ class OverviewReport(Report):
|
||||
return story
|
||||
|
||||
def _get_data(self, form_data):
|
||||
if form_data.get('date_range'):
|
||||
d_start, d_end = resolve_timeframe_to_dates_inclusive(now(), form_data['date_range'], self.timezone)
|
||||
else:
|
||||
d_start, d_end = None, None
|
||||
return order_overview(
|
||||
self.event,
|
||||
subevent=form_data.get('subevent'),
|
||||
date_filter=form_data.get('date_axis'),
|
||||
date_from=form_data.get('date_from'),
|
||||
date_until=form_data.get('date_until'),
|
||||
date_from=d_start,
|
||||
date_until=d_end,
|
||||
fees=True
|
||||
)
|
||||
|
||||
@@ -381,6 +388,13 @@ class OverviewReport(Report):
|
||||
def export_form_fields(self) -> dict:
|
||||
f = OverviewFilterForm(event=self.event)
|
||||
del f.fields['ordering']
|
||||
del f.fields['date_from']
|
||||
del f.fields['date_until']
|
||||
f.fields['date_range'] = DateFrameField(
|
||||
label=_('Date range'),
|
||||
include_future_frames=False,
|
||||
required=False,
|
||||
)
|
||||
return f.fields
|
||||
|
||||
|
||||
@@ -606,48 +620,30 @@ class OrderTaxListReport(MultiSheetListExporter):
|
||||
),
|
||||
required=False,
|
||||
)),
|
||||
('date_from', forms.DateField(
|
||||
label=_('Date from'),
|
||||
required=False,
|
||||
widget=DatePickerWidget,
|
||||
)),
|
||||
('date_until', forms.DateField(
|
||||
label=_('Date until'),
|
||||
required=False,
|
||||
widget=DatePickerWidget,
|
||||
))
|
||||
('date_range',
|
||||
DateFrameField(
|
||||
label=_('Date range'),
|
||||
include_future_frames=False,
|
||||
required=False,
|
||||
help_text=_('Only include orders created within this date range.')
|
||||
)),
|
||||
]
|
||||
))
|
||||
return f
|
||||
|
||||
def filter_qs(self, qs, form_data):
|
||||
date_from = form_data.get('date_from')
|
||||
date_until = form_data.get('date_until')
|
||||
date_range = form_data.get('date_range')
|
||||
date_filter = form_data.get('date_axis')
|
||||
if date_from:
|
||||
if isinstance(date_from, str):
|
||||
date_from = parse(date_from).date()
|
||||
if isinstance(date_from, date):
|
||||
date_from = make_aware(datetime.combine(
|
||||
date_from,
|
||||
time(hour=0, minute=0, second=0, microsecond=0)
|
||||
), self.event.timezone)
|
||||
|
||||
if date_until:
|
||||
if isinstance(date_until, str):
|
||||
date_until = parse(date_until).date()
|
||||
if isinstance(date_until, date):
|
||||
date_until = make_aware(datetime.combine(
|
||||
date_until + timedelta(days=1),
|
||||
time(hour=0, minute=0, second=0, microsecond=0)
|
||||
), self.event.timezone)
|
||||
if date_range:
|
||||
dt_start, dt_end = resolve_timeframe_to_datetime_start_inclusive_end_exclusive(now(), date_range, self.timezone)
|
||||
|
||||
if date_filter == 'order_date':
|
||||
if date_from:
|
||||
qs = qs.filter(order__datetime__gte=date_from)
|
||||
if date_until:
|
||||
qs = qs.filter(order__datetime__lt=date_until)
|
||||
elif date_filter == 'last_payment_date':
|
||||
if date_filter == 'order_date' and date_range:
|
||||
if dt_start:
|
||||
qs = qs.filter(order__datetime__gte=dt_start)
|
||||
if dt_end:
|
||||
qs = qs.filter(order__datetime__lt=dt_end)
|
||||
elif date_filter == 'last_payment_date' and date_range:
|
||||
p_date = OrderPayment.objects.filter(
|
||||
order=OuterRef('order'),
|
||||
state__in=[OrderPayment.PAYMENT_STATE_CONFIRMED, OrderPayment.PAYMENT_STATE_REFUNDED],
|
||||
@@ -656,10 +652,10 @@ class OrderTaxListReport(MultiSheetListExporter):
|
||||
m=Max('payment_date')
|
||||
).values('m').order_by()
|
||||
qs = qs.annotate(payment_date=Subquery(p_date, output_field=DateTimeField()))
|
||||
if date_from:
|
||||
qs = qs.filter(payment_date__gte=date_from)
|
||||
if date_until:
|
||||
qs = qs.filter(payment_date__lt=date_until)
|
||||
if dt_start:
|
||||
qs = qs.filter(payment_date__gte=dt_start)
|
||||
if dt_end:
|
||||
qs = qs.filter(payment_date__lt=dt_end)
|
||||
return qs
|
||||
|
||||
def iterate_sheet(self, form_data, sheet):
|
||||
|
||||
@@ -34,16 +34,14 @@
|
||||
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime, time, timedelta
|
||||
from io import BytesIO
|
||||
|
||||
import dateutil.parser
|
||||
from django import forms
|
||||
from django.core.files.base import ContentFile
|
||||
from django.db import DataError, models
|
||||
from django.db.models import OuterRef, Q, Subquery
|
||||
from django.db.models.functions import Cast, Coalesce
|
||||
from django.utils.timezone import make_aware
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext as _, gettext_lazy, pgettext_lazy
|
||||
from PyPDF2 import PdfMerger
|
||||
|
||||
@@ -55,6 +53,10 @@ from pretix.base.models import (
|
||||
from pretix.base.settings import PERSON_NAME_SCHEMES
|
||||
|
||||
from ...base.services.export import ExportError
|
||||
from ...base.timeframes import (
|
||||
DateFrameField,
|
||||
resolve_timeframe_to_datetime_start_inclusive_end_exclusive,
|
||||
)
|
||||
from ...helpers.templatetags.jsonfield import JSONExtract
|
||||
from .ticketoutput import PdfTicketOutput
|
||||
|
||||
@@ -78,19 +80,12 @@ class AllTicketsPDF(BaseExporter):
|
||||
label=_('Include pending orders'),
|
||||
required=False
|
||||
)),
|
||||
('date_from',
|
||||
forms.DateField(
|
||||
label=_('Start date'),
|
||||
widget=forms.DateInput(attrs={'class': 'datepickerfield'}),
|
||||
('date_range',
|
||||
DateFrameField(
|
||||
label=_('Date range'),
|
||||
include_future_frames=True,
|
||||
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.')
|
||||
help_text=_('Only include tickets for dates within this range.')
|
||||
)),
|
||||
('order_by',
|
||||
forms.ChoiceField(
|
||||
@@ -117,8 +112,7 @@ class AllTicketsPDF(BaseExporter):
|
||||
)
|
||||
|
||||
if not self.is_multievent and not self.event.has_subevents:
|
||||
del d['date_from']
|
||||
del d['date_to']
|
||||
del d['date_range']
|
||||
|
||||
return d
|
||||
|
||||
@@ -135,19 +129,12 @@ 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.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.timezone)
|
||||
qs = qs.filter(Q(subevent__date_from__lt=dt) | Q(subevent__isnull=True, order__event__date_from__lt=dt))
|
||||
if form_data.get('date_range'):
|
||||
dt_start, dt_end = resolve_timeframe_to_datetime_start_inclusive_end_exclusive(now(), form_data['date_range'], self.timezone)
|
||||
if dt_start:
|
||||
qs = qs.filter(Q(subevent__date_from__gte=dt_start) | Q(subevent__isnull=True, order__event__date_from__gte=dt_start))
|
||||
if dt_end:
|
||||
qs = qs.filter(Q(subevent__date_from__lt=dt_end) | Q(subevent__isnull=True, order__event__date_from__lt=dt_end))
|
||||
|
||||
if form_data.get('order_by') == 'name':
|
||||
qs = qs.order_by('attendee_name_cached', 'order__code')
|
||||
|
||||
Reference in New Issue
Block a user