forked from CGM_Public/pretix_original
@@ -531,7 +531,6 @@ class Quota(LoggedModel):
|
||||
|
||||
return OrderPosition.objects.filter(
|
||||
self._position_lookup, order__status=Order.STATUS_PENDING,
|
||||
order__expires__gte=now()
|
||||
).distinct().count()
|
||||
|
||||
def count_paid_orders(self):
|
||||
|
||||
@@ -4,7 +4,6 @@ import string
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
|
||||
from django import forms
|
||||
from django.db import models
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
@@ -204,6 +203,13 @@ class Order(LoggedModel):
|
||||
return True
|
||||
return False # nothing there to modify
|
||||
|
||||
@property
|
||||
def is_expired_by_time(self):
|
||||
return (
|
||||
self.status == Order.STATUS_PENDING and self.expires < now()
|
||||
and not self.event.settings.get('payment_term_expire_automatically')
|
||||
)
|
||||
|
||||
def _can_be_paid(self) -> Union[bool, str]:
|
||||
error_messages = {
|
||||
'late': _("The payment is too late to be accepted."),
|
||||
@@ -212,7 +218,7 @@ class Order(LoggedModel):
|
||||
if self.event.settings.get('payment_term_last') \
|
||||
and now() > self.event.settings.get('payment_term_last'):
|
||||
return error_messages['late']
|
||||
if now() < self.expires:
|
||||
if self.status == self.STATUS_PENDING:
|
||||
return True
|
||||
if not self.event.settings.get('payment_term_accept_late'):
|
||||
return error_messages['late']
|
||||
|
||||
@@ -2,6 +2,7 @@ from datetime import datetime, timedelta
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from django.dispatch import receiver
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext as _
|
||||
from typing import List
|
||||
@@ -19,7 +20,7 @@ from pretix.base.services.invoices import (
|
||||
)
|
||||
from pretix.base.services.mail import mail
|
||||
from pretix.base.signals import (
|
||||
order_paid, order_placed, register_payment_providers,
|
||||
order_paid, order_placed, periodic_task, register_payment_providers,
|
||||
)
|
||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||
|
||||
@@ -253,7 +254,7 @@ def _perform_order(event: str, payment_provider: str, position_ids: List[str],
|
||||
event = Event.objects.get(id=event)
|
||||
responses = register_payment_providers.send(event)
|
||||
pprov = None
|
||||
for receiver, response in responses:
|
||||
for rec, response in responses:
|
||||
provider = response(event)
|
||||
if provider.identifier == payment_provider:
|
||||
pprov = provider
|
||||
@@ -316,6 +317,20 @@ def perform_order(event: str, payment_provider: str, positions: List[str],
|
||||
raise OrderError(error_messages['busy'])
|
||||
|
||||
|
||||
@receiver(signal=periodic_task)
|
||||
def expire_orders(sender, **kwargs):
|
||||
eventcache = {}
|
||||
for o in Order.objects.filter(expires__lt=now(), status=Order.STATUS_PENDING).select_related('event'):
|
||||
expire = eventcache.get(o.event.pk, None)
|
||||
if expire is None:
|
||||
expire = o.event.settings.get('payment_term_expire_automatically', as_type=bool)
|
||||
eventcache[o.event.pk] = expire
|
||||
if expire:
|
||||
o.status = Order.STATUS_EXPIRED
|
||||
o.log_action('pretix.event.order.expired')
|
||||
o.save()
|
||||
|
||||
|
||||
if settings.HAS_CELERY:
|
||||
from pretix.celery import app
|
||||
|
||||
|
||||
@@ -49,6 +49,10 @@ DEFAULTS = {
|
||||
'default': None,
|
||||
'type': datetime,
|
||||
},
|
||||
'payment_term_expire_automatically': {
|
||||
'default': 'True',
|
||||
'type': bool
|
||||
},
|
||||
'payment_term_accept_late': {
|
||||
'default': 'True',
|
||||
'type': bool
|
||||
|
||||
@@ -117,11 +117,17 @@ class EventSettingsForm(SettingsForm):
|
||||
"days configured above."),
|
||||
required=False
|
||||
)
|
||||
payment_term_expire_automatically = forms.BooleanField(
|
||||
label=_('Automatically expire unpaid orders'),
|
||||
help_text=_("If checked, all unpaid orders will automatically go from 'pending' to 'expired' "
|
||||
"after the end of their payment deadline. This means that those tickets go back to "
|
||||
"the pool and can be ordered by other people."),
|
||||
required=False
|
||||
)
|
||||
payment_term_accept_late = forms.BooleanField(
|
||||
label=_('Accept late payments'),
|
||||
help_text=_("Accept payments that come after the end of the order's payment term. "
|
||||
"Payments will only be accepted if the regarding quotas have remaining "
|
||||
"capacity. No payments will be accepted after the 'Last date of payments' "
|
||||
help_text=_("Accept payments for orders even when they are in 'expired' state as long as enough "
|
||||
"capacity is available. No payments will ever be accepted after the 'Last date of payments' "
|
||||
"configured above."),
|
||||
required=False
|
||||
)
|
||||
|
||||
@@ -11,6 +11,7 @@ def pretixcontrol_logentry_display(sender, logentry, **kwargs):
|
||||
'pretix.event.order.unpaid': _('The order has been marked as unpaid.'),
|
||||
'pretix.event.order.resend': _('The link to the order detail page has been resent to the user.'),
|
||||
'pretix.event.order.expirychanged': _('The order\'s expiry date has been changed.'),
|
||||
'pretix.event.order.expired': _('The order has been marked as expired.'),
|
||||
'pretix.event.order.paid': _('The order has been marked as paid.'),
|
||||
'pretix.event.order.refunded': _('The order has been refunded.'),
|
||||
'pretix.event.order.cancelled': _('The order has been cancelled.'),
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
{% bootstrap_field sform.show_items_outside_presale_period layout="horizontal" %}
|
||||
{% bootstrap_field sform.payment_term_days layout="horizontal" %}
|
||||
{% bootstrap_field sform.payment_term_last layout="horizontal" %}
|
||||
{% bootstrap_field sform.payment_term_expire_automatically layout="horizontal" %}
|
||||
{% bootstrap_field sform.payment_term_accept_late layout="horizontal" %}
|
||||
{% bootstrap_field sform.last_order_modification_date layout="horizontal" %}
|
||||
</fieldset>
|
||||
|
||||
@@ -47,6 +47,17 @@
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if order.is_expired_by_time %}
|
||||
<form action="{% url "control:event.order.transition" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}"
|
||||
method="post">
|
||||
{% csrf_token %}
|
||||
<div class="alert alert-info">
|
||||
<button name="status" value="e" class="btn btn-default pull-right">{% trans "Expire order" %}</button>
|
||||
{% trans "The payment for this order is overdue, but you have configured not to expire orders automatically. To free quota capacity, you can mark it as expired manually." %}
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-lg-10">
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
{% elif order.status == "p" %}
|
||||
<span class="label label-success {{ class }}">{% trans "Paid" %}</span>
|
||||
{% elif order.status == "e" %} {# expired #}
|
||||
<span class="label label-danger {{ class }}">{% trans "Pending (expired)" %}</span>
|
||||
<span class="label label-danger {{ class }}">{% trans "Expired" %}</span>
|
||||
{% elif order.status == "c" %}
|
||||
<span class="label label-danger {{ class }}">{% trans "Cancelled" %}</span>
|
||||
{% elif order.status == "r" %}
|
||||
|
||||
@@ -167,23 +167,28 @@ class OrderTransition(OrderView):
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
to = self.request.POST.get('status', '')
|
||||
if self.order.status == 'n' and to == 'p':
|
||||
if self.order.status in (Order.STATUS_PENDING, Order.STATUS_EXPIRED) and to == 'p':
|
||||
try:
|
||||
mark_order_paid(self.order, manual=True, user=self.request.user)
|
||||
except Quota.QuotaExceededException as e:
|
||||
messages.error(self.request, str(e))
|
||||
else:
|
||||
messages.success(self.request, _('The order has been marked as paid.'))
|
||||
elif self.order.status == 'n' and to == 'c':
|
||||
elif self.order.status == Order.STATUS_PENDING and to == 'c':
|
||||
cancel_order(self.order, user=self.request.user)
|
||||
messages.success(self.request, _('The order has been cancelled.'))
|
||||
elif self.order.status == 'p' and to == 'n':
|
||||
elif self.order.status == Order.STATUS_PAID and to == 'n':
|
||||
self.order.status = Order.STATUS_PENDING
|
||||
self.order.payment_manual = True
|
||||
self.order.save()
|
||||
self.order.log_action('pretix.event.order.unpaid', user=self.request.user)
|
||||
messages.success(self.request, _('The order has been marked as not paid.'))
|
||||
elif self.order.status == 'p' and to == 'r':
|
||||
elif self.order.status == Order.STATUS_PENDING and to == 'e':
|
||||
self.order.status = Order.STATUS_EXPIRED
|
||||
self.order.save()
|
||||
self.order.log_action('pretix.event.order.expired', user=self.request.user)
|
||||
messages.success(self.request, _('The order has been marked as expired.'))
|
||||
elif self.order.status == Order.STATUS_PAID and to == 'r':
|
||||
ret = self.payment_provider.order_control_refund_perform(self.request, self.order)
|
||||
if ret:
|
||||
return redirect(ret)
|
||||
@@ -191,11 +196,11 @@ class OrderTransition(OrderView):
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
to = self.request.GET.get('status', '')
|
||||
if self.order.status == 'n' and to == 'c':
|
||||
if self.order.status == Order.STATUS_PENDING and to == 'c':
|
||||
return render(self.request, 'pretixcontrol/order/cancel.html', {
|
||||
'order': self.order,
|
||||
})
|
||||
elif self.order.status == 'p' and to == 'r':
|
||||
elif self.order.status == Order.STATUS_PAID and to == 'r':
|
||||
return render(self.request, 'pretixcontrol/order/refund.html', {
|
||||
'order': self.order,
|
||||
'payment': self.payment_provider.order_control_refund_render(self.order),
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
{% elif order.status == "p" %}
|
||||
<span class="label label-success {{ class }}">{% trans "Paid" %}</span>
|
||||
{% elif order.status == "e" %}
|
||||
<span class="label label-danger {{ class }}">{% trans "Payment pending" %}</span>
|
||||
<span class="label label-danger {{ class }}">{% trans "Expired" %}</span>
|
||||
{% elif order.status == "c" %}
|
||||
<span class="label label-danger {{ class }}">{% trans "Cancelled" %}</span>
|
||||
{% elif order.status == "r" %}
|
||||
|
||||
Reference in New Issue
Block a user