Order data export: Allow to filter by product (Z#23212618) (#5826)

* Order data export: Allow to filter by product (Z#23212618)

* Fix tests
This commit is contained in:
Raphael Michel
2026-01-26 09:29:41 +01:00
committed by GitHub
parent 5c8e785a6f
commit 5e97f668a5
2 changed files with 47 additions and 6 deletions

View File

@@ -39,8 +39,8 @@ from zoneinfo import ZoneInfo
from django import forms from django import forms
from django.conf import settings from django.conf import settings
from django.db.models import ( from django.db.models import (
Case, CharField, Count, DateTimeField, F, IntegerField, Max, Min, OuterRef, Case, CharField, Count, DateTimeField, Exists, F, IntegerField, Max, Min,
Q, Subquery, Sum, When, OuterRef, Q, Subquery, Sum, When,
) )
from django.db.models.functions import Coalesce from django.db.models.functions import Coalesce
from django.dispatch import receiver from django.dispatch import receiver
@@ -144,6 +144,18 @@ class OrderListExporter(MultiSheetListExporter):
d = OrderedDict(d) d = OrderedDict(d)
if not self.is_multievent and not self.event.has_subevents: if not self.is_multievent and not self.event.has_subevents:
del d['event_date_range'] del d['event_date_range']
if not self.is_multievent:
d["items"] = forms.ModelMultipleChoiceField(
label=_("Products"),
queryset=self.event.items.all(),
widget=forms.CheckboxSelectMultiple(
attrs={"class": "scrolling-multiple-choice"}
),
help_text=_("If none are selected, all products are included. Orders are included if they contain "
"at least one position of this product. The order totals etc. still include all products "
"contained in the order."),
required=False,
)
return d return d
def _get_all_payment_methods(self, qs): def _get_all_payment_methods(self, qs):
@@ -249,6 +261,14 @@ class OrderListExporter(MultiSheetListExporter):
pcnt=Subquery(s, output_field=IntegerField()) pcnt=Subquery(s, output_field=IntegerField())
).select_related('invoice_address', 'customer') ).select_related('invoice_address', 'customer')
if form_data.get('items'):
qs = qs.filter(
Exists(OrderPosition.all.filter(
order=OuterRef('pk'),
item__in=form_data["items"]
))
)
qs = self._date_filter(qs, form_data, rel='') qs = self._date_filter(qs, form_data, rel='')
if form_data['paid_only']: if form_data['paid_only']:
@@ -440,6 +460,14 @@ class OrderListExporter(MultiSheetListExporter):
if form_data['paid_only']: if form_data['paid_only']:
qs = qs.filter(order__status=Order.STATUS_PAID, canceled=False) qs = qs.filter(order__status=Order.STATUS_PAID, canceled=False)
if form_data.get('items'):
qs = qs.filter(
Exists(OrderPosition.all.filter(
order=OuterRef('order'),
item__in=form_data["items"]
))
)
qs = self._date_filter(qs, form_data, rel='order__') qs = self._date_filter(qs, form_data, rel='order__')
return qs return qs
@@ -535,6 +563,11 @@ class OrderListExporter(MultiSheetListExporter):
if form_data['paid_only']: if form_data['paid_only']:
qs = qs.filter(order__status=Order.STATUS_PAID, canceled=False) qs = qs.filter(order__status=Order.STATUS_PAID, canceled=False)
if form_data.get('items'):
qs = qs.filter(
item__in=form_data["items"]
)
qs = self._date_filter(qs, form_data, rel='order__') qs = self._date_filter(qs, form_data, rel='order__')
return qs return qs

View File

@@ -82,6 +82,10 @@ SAMPLE_EXPORTER_CONFIG = {
"name": "event_date_range", "name": "event_date_range",
"required": False "required": False
}, },
{
"name": "items",
"required": False
},
] ]
} }
@@ -107,6 +111,10 @@ def test_org_list(token_client, organizer, event):
"name": "events", "name": "events",
"required": False "required": False
}) })
c['input_parameters'].remove({
"name": "items",
"required": False
})
resp = token_client.get('/api/v1/organizers/{}/exporters/'.format(organizer.slug)) resp = token_client.get('/api/v1/organizers/{}/exporters/'.format(organizer.slug))
assert resp.status_code == 200 assert resp.status_code == 200
assert c in resp.data['results'] assert c in resp.data['results']
@@ -389,7 +397,7 @@ def test_event_scheduled_export_create(user_client, organizer, event, user):
'/api/v1/organizers/{}/events/{}/scheduled_exports/'.format(organizer.slug, event.slug), '/api/v1/organizers/{}/events/{}/scheduled_exports/'.format(organizer.slug, event.slug),
data={ data={
"export_identifier": "orderlist", "export_identifier": "orderlist",
"export_form_data": {"_format": "xlsx", "date_range": "year_this"}, "export_form_data": {"_format": "xlsx", "date_range": "year_this", "items": []},
"locale": "en", "locale": "en",
"mail_additional_recipients": "foo@example.org", "mail_additional_recipients": "foo@example.org",
"mail_additional_recipients_cc": "", "mail_additional_recipients_cc": "",
@@ -403,7 +411,7 @@ def test_event_scheduled_export_create(user_client, organizer, event, user):
) )
assert resp.status_code == 201 assert resp.status_code == 201
created = event.scheduled_exports.get(id=resp.data["id"]) created = event.scheduled_exports.get(id=resp.data["id"])
assert created.export_form_data == {"_format": "xlsx", "date_range": "year_this"} assert created.export_form_data == {"_format": "xlsx", "date_range": "year_this", "items": []}
assert created.owner == user assert created.owner == user
assert created.schedule_next_run > now() assert created.schedule_next_run > now()
@@ -414,7 +422,7 @@ def test_event_scheduled_export_create_requires_user(token_client, organizer, ev
'/api/v1/organizers/{}/events/{}/scheduled_exports/'.format(organizer.slug, event.slug), '/api/v1/organizers/{}/events/{}/scheduled_exports/'.format(organizer.slug, event.slug),
data={ data={
"export_identifier": "orderlist", "export_identifier": "orderlist",
"export_form_data": {"_format": "xlsx", "date_range": "year_this"}, "export_form_data": {"_format": "xlsx", "date_range": "year_this", "items": []},
"locale": "en", "locale": "en",
"mail_additional_recipients": "foo@example.org", "mail_additional_recipients": "foo@example.org",
"mail_additional_recipients_cc": "", "mail_additional_recipients_cc": "",
@@ -453,7 +461,7 @@ def test_event_scheduled_export_update_token(token_client, organizer, event, use
) )
assert resp.status_code == 200 assert resp.status_code == 200
created = event.scheduled_exports.get(id=resp.data["id"]) created = event.scheduled_exports.get(id=resp.data["id"])
assert created.export_form_data == {"_format": "xlsx", "date_range": "month_this"} assert created.export_form_data == {"_format": "xlsx", "date_range": "month_this", "items": []}
@pytest.fixture @pytest.fixture