From 4b01c42f3193f8083d106139c56e20798b7bef1b Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Wed, 6 May 2020 13:12:17 +0200 Subject: [PATCH 1/2] Improve global order filters --- src/pretix/control/forms/filter.py | 63 +++++++++++++++++++ .../pretixcontrol/search/orders.html | 19 +++++- src/pretix/control/views/search.py | 17 +++-- 3 files changed, 94 insertions(+), 5 deletions(-) diff --git a/src/pretix/control/forms/filter.py b/src/pretix/control/forms/filter.py index 158fbafc9e..0b226e8565 100644 --- a/src/pretix/control/forms/filter.py +++ b/src/pretix/control/forms/filter.py @@ -363,6 +363,28 @@ class OrderSearchFilterForm(OrderFilterForm): ) ) + status = forms.ChoiceField( + label=_('Order status'), + choices=( + ('', _('All orders')), + (Order.STATUS_PAID, _('Paid (or canceled with paid fee)')), + (Order.STATUS_PENDING, _('Pending')), + ('o', _('Pending (overdue)')), + (Order.STATUS_PENDING + Order.STATUS_PAID, _('Pending or paid')), + (Order.STATUS_EXPIRED, _('Expired')), + (Order.STATUS_PENDING + Order.STATUS_EXPIRED, _('Pending or expired')), + (Order.STATUS_CANCELED, _('Canceled')), + ('cp', _('Canceled (or with paid fee)')), + ('pa', _('Approval pending')), + ('overpaid', _('Overpaid')), + ('underpaid', _('Underpaid')), + ('pendingpaid', _('Pending (but fully paid)')), + ('testmode', _('Test mode')), + ('rc', _('Cancellation requested')), + ), + required=False, + ) + def __init__(self, *args, **kwargs): request = kwargs.pop('request') super().__init__(*args, **kwargs) @@ -381,6 +403,47 @@ class OrderSearchFilterForm(OrderFilterForm): if fdata.get('organizer'): qs = qs.filter(event__organizer=fdata.get('organizer')) + if fdata.get('status') == 'overpaid': + qs = Order.annotate_overpayments(qs, refunds=False, results=False, sums=True) + qs = qs.filter( + Q(~Q(status=Order.STATUS_CANCELED) & Q(pending_sum_t__lt=0)) + | Q(Q(status=Order.STATUS_CANCELED) & Q(pending_sum_rc__lt=0)) + ) + elif fdata.get('status') == 'rc': + qs = qs.filter( + cancellation_requests__isnull=False + ) + elif fdata.get('status') == 'pendingpaid': + qs = Order.annotate_overpayments(qs, refunds=False, results=False, sums=True) + qs = qs.filter( + Q(status__in=(Order.STATUS_EXPIRED, Order.STATUS_PENDING)) & Q(pending_sum_t__lte=0) + & Q(require_approval=False) + ) + elif fdata.get('status') == 'underpaid': + qs = Order.annotate_overpayments(qs, refunds=False, results=False, sums=True) + qs = qs.filter( + status=Order.STATUS_PAID, + pending_sum_t__gt=0 + ) + elif fdata.get('status') == 'pa': + qs = qs.filter( + status=Order.STATUS_PENDING, + require_approval=True + ) + elif fdata.get('status') == 'testmode': + qs = qs.filter( + testmode=True + ) + elif fdata.get('status') == 'cp': + s = OrderPosition.objects.filter( + order=OuterRef('pk') + ) + qs = qs.annotate( + has_pc=Exists(s) + ).filter( + Q(status=Order.STATUS_PAID, has_pc=False) | Q(status=Order.STATUS_CANCELED) + ) + return qs diff --git a/src/pretix/control/templates/pretixcontrol/search/orders.html b/src/pretix/control/templates/pretixcontrol/search/orders.html index 8af8098148..52e0a14f1e 100644 --- a/src/pretix/control/templates/pretixcontrol/search/orders.html +++ b/src/pretix/control/templates/pretixcontrol/search/orders.html @@ -73,7 +73,24 @@ {% endif %} {{ o.datetime|date:"SHORT_DATETIME_FORMAT" }} - {{ o.total|money:o.event.currency }} + + {% if o.has_cancellation_request %} + {% trans "CANCELLATION REQUESTED" %} + {% endif %} + {% if o.has_external_refund or o.has_pending_refund %} + {% trans "REFUND PENDING" %} + {% elif o.has_pending_refund %} + {% trans "REFUND PENDING" %} + {% endif %} + {% if o.is_overpaid %} + {% trans "OVERPAID" %} + {% elif o.is_underpaid %} + {% trans "UNDERPAID" %} + {% elif o.is_pending_with_full_payment %} + {% trans "FULLY PAID" %} + {% endif %} + {{ o.total|money:o.event.currency }} + {% include "pretixcontrol/orders/fragment_order_status.html" with order=o %} {% empty %} diff --git a/src/pretix/control/views/search.py b/src/pretix/control/views/search.py index 1f9ae35299..8e89ce5c38 100644 --- a/src/pretix/control/views/search.py +++ b/src/pretix/control/views/search.py @@ -1,9 +1,10 @@ from django.conf import settings -from django.db.models import Count, IntegerField, OuterRef, Q, Subquery +from django.db.models import Count, Exists, IntegerField, OuterRef, Q, Subquery from django.utils.functional import cached_property from django.views.generic import ListView from pretix.base.models import Order, OrderPosition +from pretix.base.models.orders import CancellationRequest from pretix.control.forms.filter import OrderSearchFilterForm from pretix.control.views import LargeResultSetPaginator, PaginationMixin @@ -29,12 +30,14 @@ class OrderSearch(PaginationMixin, ListView): annotated = { o['pk']: o for o in - Order.objects.using(settings.DATABASE_REPLICA).filter( + Order.annotate_overpayments(Order.objects).using(settings.DATABASE_REPLICA).filter( pk__in=[o.pk for o in ctx['orders']] ).annotate( - pcnt=Subquery(s, output_field=IntegerField()) + pcnt=Subquery(s, output_field=IntegerField()), + has_cancellation_request=Exists(CancellationRequest.objects.filter(order=OuterRef('pk'))) ).values( - 'pk', 'pcnt', + 'pk', 'pcnt', 'is_overpaid', 'is_underpaid', 'is_pending_with_full_payment', 'has_external_refund', + 'has_pending_refund', 'has_cancellation_request' ) } @@ -42,6 +45,12 @@ class OrderSearch(PaginationMixin, ListView): if o.pk not in annotated: continue o.pcnt = annotated.get(o.pk)['pcnt'] + o.is_overpaid = annotated.get(o.pk)['is_overpaid'] + o.is_underpaid = annotated.get(o.pk)['is_underpaid'] + o.is_pending_with_full_payment = annotated.get(o.pk)['is_pending_with_full_payment'] + o.has_external_refund = annotated.get(o.pk)['has_external_refund'] + o.has_pending_refund = annotated.get(o.pk)['has_pending_refund'] + o.has_cancellation_request = annotated.get(o.pk)['has_cancellation_request'] return ctx From 66d3c4516ffbcfcf78c50cc2552bc6796136e65d Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Wed, 6 May 2020 13:48:45 +0200 Subject: [PATCH 2/2] Move stuff to OrderFilterForm --- src/pretix/control/forms/filter.py | 172 ++++++++--------------------- 1 file changed, 47 insertions(+), 125 deletions(-) diff --git a/src/pretix/control/forms/filter.py b/src/pretix/control/forms/filter.py index 0b226e8565..75c46a3a83 100644 --- a/src/pretix/control/forms/filter.py +++ b/src/pretix/control/forms/filter.py @@ -111,6 +111,13 @@ class OrderFilterForm(FilterForm): (Order.STATUS_EXPIRED, _('Expired')), (Order.STATUS_PENDING + Order.STATUS_EXPIRED, _('Pending or expired')), (Order.STATUS_CANCELED, _('Canceled')), + ('cp', _('Canceled (or with paid fee)')), + ('pa', _('Approval pending')), + ('overpaid', _('Overpaid')), + ('underpaid', _('Underpaid')), + ('pendingpaid', _('Pending (but fully paid)')), + ('testmode', _('Test mode')), + ('rc', _('Cancellation requested')), ), required=False, ) @@ -165,6 +172,46 @@ class OrderFilterForm(FilterForm): qs = qs.filter(status__in=[Order.STATUS_PENDING, Order.STATUS_EXPIRED]) elif s in ('p', 'n', 'e', 'c', 'r'): qs = qs.filter(status=s) + elif s == 'overpaid': + qs = Order.annotate_overpayments(qs, refunds=False, results=False, sums=True) + qs = qs.filter( + Q(~Q(status=Order.STATUS_CANCELED) & Q(pending_sum_t__lt=0)) + | Q(Q(status=Order.STATUS_CANCELED) & Q(pending_sum_rc__lt=0)) + ) + elif s == 'rc': + qs = qs.filter( + cancellation_requests__isnull=False + ) + elif s == 'pendingpaid': + qs = Order.annotate_overpayments(qs, refunds=False, results=False, sums=True) + qs = qs.filter( + Q(status__in=(Order.STATUS_EXPIRED, Order.STATUS_PENDING)) & Q(pending_sum_t__lte=0) + & Q(require_approval=False) + ) + elif s == 'underpaid': + qs = Order.annotate_overpayments(qs, refunds=False, results=False, sums=True) + qs = qs.filter( + status=Order.STATUS_PAID, + pending_sum_t__gt=0 + ) + elif s == 'pa': + qs = qs.filter( + status=Order.STATUS_PENDING, + require_approval=True + ) + elif s == 'testmode': + qs = qs.filter( + testmode=True + ) + elif s == 'cp': + s = OrderPosition.objects.filter( + order=OuterRef('pk') + ) + qs = qs.annotate( + has_pc=Exists(s) + ).filter( + Q(status=Order.STATUS_PAID, has_pc=False) | Q(status=Order.STATUS_CANCELED) + ) if fdata.get('ordering'): qs = qs.order_by(self.get_order_by()) @@ -203,27 +250,6 @@ class EventOrderFilterForm(OrderFilterForm): answer = forms.CharField( required=False ) - status = forms.ChoiceField( - label=_('Order status'), - choices=( - ('', _('All orders')), - (Order.STATUS_PAID, _('Paid (or canceled with paid fee)')), - (Order.STATUS_PENDING, _('Pending')), - ('o', _('Pending (overdue)')), - (Order.STATUS_PENDING + Order.STATUS_PAID, _('Pending or paid')), - (Order.STATUS_EXPIRED, _('Expired')), - (Order.STATUS_PENDING + Order.STATUS_EXPIRED, _('Pending or expired')), - (Order.STATUS_CANCELED, _('Canceled')), - ('cp', _('Canceled (or with paid fee)')), - ('pa', _('Approval pending')), - ('overpaid', _('Overpaid')), - ('underpaid', _('Underpaid')), - ('pendingpaid', _('Pending (but fully paid)')), - ('testmode', _('Test mode')), - ('rc', _('Cancellation requested')), - ), - required=False, - ) def __init__(self, *args, **kwargs): self.event = kwargs.pop('event') @@ -300,47 +326,6 @@ class EventOrderFilterForm(OrderFilterForm): ) qs = qs.annotate(has_answer=Exists(answers)).filter(has_answer=True) - if fdata.get('status') == 'overpaid': - qs = Order.annotate_overpayments(qs, refunds=False, results=False, sums=True) - qs = qs.filter( - Q(~Q(status=Order.STATUS_CANCELED) & Q(pending_sum_t__lt=0)) - | Q(Q(status=Order.STATUS_CANCELED) & Q(pending_sum_rc__lt=0)) - ) - elif fdata.get('status') == 'rc': - qs = qs.filter( - cancellation_requests__isnull=False - ) - elif fdata.get('status') == 'pendingpaid': - qs = Order.annotate_overpayments(qs, refunds=False, results=False, sums=True) - qs = qs.filter( - Q(status__in=(Order.STATUS_EXPIRED, Order.STATUS_PENDING)) & Q(pending_sum_t__lte=0) - & Q(require_approval=False) - ) - elif fdata.get('status') == 'underpaid': - qs = Order.annotate_overpayments(qs, refunds=False, results=False, sums=True) - qs = qs.filter( - status=Order.STATUS_PAID, - pending_sum_t__gt=0 - ) - elif fdata.get('status') == 'pa': - qs = qs.filter( - status=Order.STATUS_PENDING, - require_approval=True - ) - elif fdata.get('status') == 'testmode': - qs = qs.filter( - testmode=True - ) - elif fdata.get('status') == 'cp': - s = OrderPosition.objects.filter( - order=OuterRef('pk') - ) - qs = qs.annotate( - has_pc=Exists(s) - ).filter( - Q(status=Order.STATUS_PAID, has_pc=False) | Q(status=Order.STATUS_CANCELED) - ) - return qs @@ -363,28 +348,6 @@ class OrderSearchFilterForm(OrderFilterForm): ) ) - status = forms.ChoiceField( - label=_('Order status'), - choices=( - ('', _('All orders')), - (Order.STATUS_PAID, _('Paid (or canceled with paid fee)')), - (Order.STATUS_PENDING, _('Pending')), - ('o', _('Pending (overdue)')), - (Order.STATUS_PENDING + Order.STATUS_PAID, _('Pending or paid')), - (Order.STATUS_EXPIRED, _('Expired')), - (Order.STATUS_PENDING + Order.STATUS_EXPIRED, _('Pending or expired')), - (Order.STATUS_CANCELED, _('Canceled')), - ('cp', _('Canceled (or with paid fee)')), - ('pa', _('Approval pending')), - ('overpaid', _('Overpaid')), - ('underpaid', _('Underpaid')), - ('pendingpaid', _('Pending (but fully paid)')), - ('testmode', _('Test mode')), - ('rc', _('Cancellation requested')), - ), - required=False, - ) - def __init__(self, *args, **kwargs): request = kwargs.pop('request') super().__init__(*args, **kwargs) @@ -403,47 +366,6 @@ class OrderSearchFilterForm(OrderFilterForm): if fdata.get('organizer'): qs = qs.filter(event__organizer=fdata.get('organizer')) - if fdata.get('status') == 'overpaid': - qs = Order.annotate_overpayments(qs, refunds=False, results=False, sums=True) - qs = qs.filter( - Q(~Q(status=Order.STATUS_CANCELED) & Q(pending_sum_t__lt=0)) - | Q(Q(status=Order.STATUS_CANCELED) & Q(pending_sum_rc__lt=0)) - ) - elif fdata.get('status') == 'rc': - qs = qs.filter( - cancellation_requests__isnull=False - ) - elif fdata.get('status') == 'pendingpaid': - qs = Order.annotate_overpayments(qs, refunds=False, results=False, sums=True) - qs = qs.filter( - Q(status__in=(Order.STATUS_EXPIRED, Order.STATUS_PENDING)) & Q(pending_sum_t__lte=0) - & Q(require_approval=False) - ) - elif fdata.get('status') == 'underpaid': - qs = Order.annotate_overpayments(qs, refunds=False, results=False, sums=True) - qs = qs.filter( - status=Order.STATUS_PAID, - pending_sum_t__gt=0 - ) - elif fdata.get('status') == 'pa': - qs = qs.filter( - status=Order.STATUS_PENDING, - require_approval=True - ) - elif fdata.get('status') == 'testmode': - qs = qs.filter( - testmode=True - ) - elif fdata.get('status') == 'cp': - s = OrderPosition.objects.filter( - order=OuterRef('pk') - ) - qs = qs.annotate( - has_pc=Exists(s) - ).filter( - Q(status=Order.STATUS_PAID, has_pc=False) | Q(status=Order.STATUS_CANCELED) - ) - return qs