From fe9a7eaa2484258c3b4f955dddeaaa71f6e4108a Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Fri, 22 Aug 2025 09:30:34 +0200 Subject: [PATCH] Order overview: Try to make linked filters behave as expected for line-level cancellations (Z#23203500) --- src/pretix/base/services/stats.py | 1 + src/pretix/control/forms/filter.py | 32 +++++++++++++++++-- .../pretixcontrol/orders/overview.html | 25 +++++++-------- src/pretix/control/views/orders.py | 5 +++ 4 files changed, 46 insertions(+), 17 deletions(-) diff --git a/src/pretix/base/services/stats.py b/src/pretix/base/services/stats.py index fd27e8735b..79e86afe0e 100644 --- a/src/pretix/base/services/stats.py +++ b/src/pretix/base/services/stats.py @@ -197,6 +197,7 @@ def order_overview( item.all_variations = list(item.variations.all()) item.has_variations = (len(item.all_variations) > 0) item.num = {} + item.subevent = subevent if item.has_variations: for var in item.all_variations: variid = var.id diff --git a/src/pretix/control/forms/filter.py b/src/pretix/control/forms/filter.py index abb66f03f1..51be2d05dc 100644 --- a/src/pretix/control/forms/filter.py +++ b/src/pretix/control/forms/filter.py @@ -220,6 +220,7 @@ class OrderFilterForm(FilterForm): (_('Cancellations'), ( (Order.STATUS_CANCELED, _('Canceled (fully)')), ('cp', _('Canceled (fully or with paid fee)')), + ('cany', _('Canceled (at least one position)')), ('rc', _('Cancellation requested')), ('cni', _('Fully canceled but invoice not canceled')), )), @@ -396,6 +397,16 @@ class OrderFilterForm(FilterForm): ).filter( Q(status=Order.STATUS_PAID, has_pc=False) | Q(status=Order.STATUS_CANCELED) ) + elif s == 'cany': + s = OrderPosition.all.filter( + order=OuterRef('pk'), + canceled=True, + ) + qs = qs.annotate( + has_pc_c=Exists(s) + ).filter( + Q(has_pc_c=True) | Q(status=Order.STATUS_CANCELED) + ) if fdata.get('ordering'): qs = qs.order_by(*get_deterministic_ordering(Order, self.get_order_by())) @@ -474,16 +485,31 @@ class EventOrderFilterForm(OrderFilterForm): fdata = self.cleaned_data qs = super().filter_qs(qs) + # This is a little magic, but there's no option that does not confuse people and let's hope this confuses less + # people. + only_match_noncanceled_products = fdata.get('status') in ( + Order.STATUS_PAID, + Order.STATUS_PAID + 'v', + Order.STATUS_PENDING, + Order.STATUS_PENDING + Order.STATUS_PAID, + ) + if only_match_noncanceled_products: + canceled_filter = Q(all_positions__canceled=False) + elif fdata.get('status') in ('cp', 'cany'): + canceled_filter = Q(all_positions__canceled=True) | Q(status=Order.STATUS_CANCELED) + else: + canceled_filter = Q() + item = fdata.get('item') if item: if '-' in item: var = item.split('-')[1] - qs = qs.filter(all_positions__variation_id=var, all_positions__canceled=False).distinct() + qs = qs.filter(canceled_filter, all_positions__variation_id=var).distinct() else: - qs = qs.filter(all_positions__item_id=fdata.get('item'), all_positions__canceled=False).distinct() + qs = qs.filter(canceled_filter, all_positions__item_id=fdata.get('item')).distinct() if fdata.get('subevent'): - qs = qs.filter(all_positions__subevent=fdata.get('subevent'), all_positions__canceled=False).distinct() + qs = qs.filter(canceled_filter, all_positions__subevent=fdata.get('subevent')).distinct() if fdata.get('question') and fdata.get('answer') is not None: q = fdata.get('question') diff --git a/src/pretix/control/templates/pretixcontrol/orders/overview.html b/src/pretix/control/templates/pretixcontrol/orders/overview.html index 3685840427..dd1f2e94d0 100644 --- a/src/pretix/control/templates/pretixcontrol/orders/overview.html +++ b/src/pretix/control/templates/pretixcontrol/orders/overview.html @@ -74,7 +74,7 @@ {% trans "Product" %} - {% trans "Canceled" %}¹ + {% trans "Canceled" %} {% trans "Expired" %} {% trans "Approval pending" %} {% trans "Purchased" %} @@ -106,27 +106,27 @@ {{ item }} - + {{ item.num.canceled|togglesum:request.event.currency }} - + {{ item.num.expired|togglesum:request.event.currency }} - + {{ item.num.unapproved|togglesum:request.event.currency }} - + {{ item.num.pending|togglesum:request.event.currency }} - + {{ item.num.paid|togglesum:request.event.currency }} @@ -139,27 +139,27 @@ {{ var }} - + {{ var.num.canceled|togglesum:request.event.currency }} - + {{ var.num.expired|togglesum:request.event.currency }} - + {{ var.num.unapproved|togglesum:request.event.currency }} - + {{ var.num.pending|togglesum:request.event.currency }} - + {{ var.num.paid|togglesum:request.event.currency }} @@ -185,7 +185,4 @@ -

- ¹ {% trans "If you click links in this column, you will only find orders that are canceled completely, while the numbers also include single canceled positions within valid orders." %} -

{% endblock %} diff --git a/src/pretix/control/views/orders.py b/src/pretix/control/views/orders.py index 8785f5bd7e..377150bd1a 100644 --- a/src/pretix/control/views/orders.py +++ b/src/pretix/control/views/orders.py @@ -2575,6 +2575,11 @@ class OverView(EventPermissionRequiredMixin, TemplateView): self.request.event, fees=True ) + ctx['subevent'] = ( + self.request.event.has_subevents and + self.filter_form.is_valid() and + self.filter_form.cleaned_data.get('subevent') + ) ctx['subevent_warning'] = ( self.request.event.has_subevents and self.filter_form.is_valid() and