mirror of
https://github.com/pretix/pretix.git
synced 2026-05-06 15:24:02 +00:00
Allow to cancel an order without creating a cancellation invoice
This commit is contained in:
@@ -327,7 +327,7 @@ def deny_order(order, comment='', user=None, send_mail: bool=True, auth=None):
|
||||
|
||||
|
||||
def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device=None, oauth_application=None,
|
||||
cancellation_fee=None, keep_fees=None):
|
||||
cancellation_fee=None, keep_fees=None, cancel_invoice=True):
|
||||
"""
|
||||
Mark this order as canceled
|
||||
:param order: The order to change
|
||||
@@ -351,9 +351,10 @@ def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device
|
||||
if not order.cancel_allowed():
|
||||
raise OrderError(_('You cannot cancel this order.'))
|
||||
invoices = []
|
||||
i = order.invoices.filter(is_cancellation=False).last()
|
||||
if i and not i.refered.exists():
|
||||
invoices.append(generate_cancellation(i))
|
||||
if cancel_invoice:
|
||||
i = order.invoices.filter(is_cancellation=False).last()
|
||||
if i and not i.refered.exists():
|
||||
invoices.append(generate_cancellation(i))
|
||||
|
||||
for position in order.positions.all():
|
||||
for gc in position.issued_gift_cards.all():
|
||||
@@ -403,7 +404,7 @@ def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device
|
||||
order.cancellation_date = now()
|
||||
order.save(update_fields=['status', 'cancellation_date', 'total'])
|
||||
|
||||
if i:
|
||||
if cancel_invoice and i:
|
||||
invoices.append(generate_invoice(order))
|
||||
else:
|
||||
with order.event.lock():
|
||||
@@ -2152,11 +2153,12 @@ def _try_auto_refund(order, manual_refund=False, allow_partial=False, source=Ord
|
||||
@app.task(base=ProfiledTask, bind=True, max_retries=5, default_retry_delay=1, throws=(OrderError,))
|
||||
@scopes_disabled()
|
||||
def cancel_order(self, order: int, user: int=None, send_mail: bool=True, api_token=None, oauth_application=None,
|
||||
device=None, cancellation_fee=None, try_auto_refund=False, refund_as_giftcard=False, comment=None):
|
||||
device=None, cancellation_fee=None, try_auto_refund=False, refund_as_giftcard=False, comment=None,
|
||||
cancel_invoice=True):
|
||||
try:
|
||||
try:
|
||||
ret = _cancel_order(order, user, send_mail, api_token, device, oauth_application,
|
||||
cancellation_fee)
|
||||
cancellation_fee, cancel_invoice=cancel_invoice)
|
||||
if try_auto_refund:
|
||||
_try_auto_refund(order, refund_as_giftcard=refund_as_giftcard,
|
||||
comment=comment)
|
||||
|
||||
@@ -5,7 +5,7 @@ from urllib.parse import urlencode
|
||||
from django import forms
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.db.models import Exists, F, Max, Model, OuterRef, Q, QuerySet
|
||||
from django.db.models import Exists, F, Max, Model, OuterRef, Q, QuerySet, Count
|
||||
from django.db.models.functions import Coalesce, ExtractWeekDay
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils.formats import date_format, localize
|
||||
@@ -153,6 +153,7 @@ class OrderFilterForm(FilterForm):
|
||||
(Order.STATUS_CANCELED, _('Canceled (fully)')),
|
||||
('cp', _('Canceled (fully or with paid fee)')),
|
||||
('rc', _('Cancellation requested')),
|
||||
('cni', _('Fully canceled but invoice not canceled')),
|
||||
)),
|
||||
(_('Payment process'), (
|
||||
(Order.STATUS_EXPIRED, _('Expired')),
|
||||
@@ -264,6 +265,18 @@ class OrderFilterForm(FilterForm):
|
||||
status=Order.STATUS_PAID,
|
||||
pending_sum_t__gt=0
|
||||
)
|
||||
elif s == 'cni':
|
||||
i = Invoice.objects.filter(
|
||||
order=OuterRef('pk'),
|
||||
is_cancellation=False,
|
||||
refered__isnull=True,
|
||||
).order_by().values('order').annotate(k=Count('id')).values('k')
|
||||
qs = qs.annotate(
|
||||
icnt=i
|
||||
).filter(
|
||||
icnt__gt=0,
|
||||
status=Order.STATUS_CANCELED,
|
||||
)
|
||||
elif s == 'pa':
|
||||
qs = qs.filter(
|
||||
status=Order.STATUS_PENDING,
|
||||
|
||||
@@ -117,6 +117,11 @@ class CancelForm(ConfirmPaymentForm):
|
||||
'in your cancellation fee if you want to keep them. Please always enter a gross value, '
|
||||
'tax will be calculated automatically.'),
|
||||
)
|
||||
cancel_invoice = forms.BooleanField(
|
||||
label=_('Generate cancellation for invoice'),
|
||||
initial=True,
|
||||
required=False
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -130,6 +135,8 @@ class CancelForm(ConfirmPaymentForm):
|
||||
self.fields['cancellation_fee'].max_value = prs
|
||||
else:
|
||||
del self.fields['cancellation_fee']
|
||||
if not self.instance.invoices.exists():
|
||||
del self.fields['cancel_invoice']
|
||||
|
||||
def clean_cancellation_fee(self):
|
||||
val = self.cleaned_data['cancellation_fee'] or Decimal('0.00')
|
||||
|
||||
@@ -23,13 +23,16 @@
|
||||
<input type="hidden" name="status" value="c"/>
|
||||
{% bootstrap_form_errors form %}
|
||||
{% bootstrap_field form.send_email layout='' %}
|
||||
{% if form.cancel_invoice %}
|
||||
{% bootstrap_field form.cancel_invoice layout='' %}
|
||||
{% endif %}
|
||||
{% if form.cancellation_fee %}
|
||||
{% bootstrap_field form.cancellation_fee layout='' %}
|
||||
{% endif %}
|
||||
<div class="row checkout-button-row">
|
||||
<div class="col-md-4">
|
||||
<a class="btn btn-block btn-default btn-lg"
|
||||
href="{% url "control:event.order" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}">
|
||||
href="{% url "control:event.order" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}">
|
||||
{% trans "No, take me back" %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -170,6 +170,10 @@
|
||||
</span>
|
||||
{% endif %}
|
||||
{{ o.total|money:request.event.currency }}
|
||||
{% if o.status == "c" and o.icnt %}
|
||||
<br>
|
||||
<span class="label label-warning">{% trans "INVOICE NOT CANCELED" %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right flip">{{ o.pcnt|default_if_none:"0" }}</td>
|
||||
<td class="text-right flip">{% include "pretixcontrol/orders/fragment_order_status.html" with order=o %}</td>
|
||||
|
||||
@@ -151,6 +151,11 @@ class OrderList(OrderSearchMixin, EventPermissionRequiredMixin, PaginationMixin,
|
||||
s = OrderPosition.objects.filter(
|
||||
order=OuterRef('pk')
|
||||
).order_by().values('order').annotate(k=Count('id')).values('k')
|
||||
i = Invoice.objects.filter(
|
||||
order=OuterRef('pk'),
|
||||
is_cancellation=False,
|
||||
refered__isnull=True,
|
||||
).order_by().values('order').annotate(k=Count('id')).values('k')
|
||||
annotated = {
|
||||
o['pk']: o
|
||||
for o in
|
||||
@@ -158,10 +163,11 @@ class OrderList(OrderSearchMixin, EventPermissionRequiredMixin, PaginationMixin,
|
||||
pk__in=[o.pk for o in ctx['orders']]
|
||||
).annotate(
|
||||
pcnt=Subquery(s, output_field=IntegerField()),
|
||||
icnt=Subquery(i, output_field=IntegerField()),
|
||||
has_cancellation_request=Exists(CancellationRequest.objects.filter(order=OuterRef('pk')))
|
||||
).values(
|
||||
'pk', 'pcnt', 'is_overpaid', 'is_underpaid', 'is_pending_with_full_payment', 'has_external_refund',
|
||||
'has_pending_refund', 'has_cancellation_request', 'computed_payment_refund_sum'
|
||||
'has_pending_refund', 'has_cancellation_request', 'computed_payment_refund_sum', 'icnt'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -177,6 +183,7 @@ class OrderList(OrderSearchMixin, EventPermissionRequiredMixin, PaginationMixin,
|
||||
o.has_pending_refund = annotated.get(o.pk)['has_pending_refund']
|
||||
o.has_cancellation_request = annotated.get(o.pk)['has_cancellation_request']
|
||||
o.computed_payment_refund_sum = annotated.get(o.pk)['computed_payment_refund_sum']
|
||||
o.icnt = annotated.get(o.pk)['icnt']
|
||||
o.sales_channel_obj = scs[o.sales_channel]
|
||||
|
||||
if ctx['page_obj'].paginator.count < 1000:
|
||||
@@ -1134,6 +1141,7 @@ class OrderTransition(OrderView):
|
||||
try:
|
||||
cancel_order(self.order.pk, user=self.request.user,
|
||||
send_mail=self.mark_canceled_form.cleaned_data['send_email'],
|
||||
cancel_invoice=self.mark_canceled_form.cleaned_data.get('cancel_invoice', True),
|
||||
cancellation_fee=self.mark_canceled_form.cleaned_data.get('cancellation_fee'))
|
||||
except OrderError as e:
|
||||
messages.error(self.request, str(e))
|
||||
|
||||
Reference in New Issue
Block a user