Fix #1255 -- Approvals of free orders after last date of payments

This commit is contained in:
Raphael Michel
2019-05-01 12:37:04 +02:00
parent d86b3a2173
commit 788f73d842
3 changed files with 33 additions and 10 deletions

View File

@@ -606,7 +606,7 @@ class Order(LockModel, LoggedModel):
), tz) ), tz)
return term_last return term_last
def _can_be_paid(self, count_waitinglist=True) -> Union[bool, str]: def _can_be_paid(self, count_waitinglist=True, ignore_date=False) -> Union[bool, str]:
error_messages = { error_messages = {
'late_lastdate': _("The payment can not be accepted as the last date of payments configured in the " 'late_lastdate': _("The payment can not be accepted as the last date of payments configured in the "
"payment settings is over."), "payment settings is over."),
@@ -617,13 +617,13 @@ class Order(LockModel, LoggedModel):
if self.require_approval: if self.require_approval:
return error_messages['require_approval'] return error_messages['require_approval']
term_last = self.payment_term_last term_last = self.payment_term_last
if term_last: if term_last and not ignore_date:
if now() > term_last: if now() > term_last:
return error_messages['late_lastdate'] return error_messages['late_lastdate']
if self.status == self.STATUS_PENDING: if self.status == self.STATUS_PENDING:
return True return True
if not self.event.settings.get('payment_term_accept_late'): if not self.event.settings.get('payment_term_accept_late') and not ignore_date:
return error_messages['late'] return error_messages['late']
return self._is_still_available(count_waitinglist=count_waitinglist) return self._is_still_available(count_waitinglist=count_waitinglist)
@@ -1137,9 +1137,9 @@ class OrderPayment(models.Model):
""" """
return self.order.event.get_payment_providers().get(self.provider) return self.order.event.get_payment_providers().get(self.provider)
def _mark_paid(self, force, count_waitinglist, user, auth, overpaid=False): def _mark_paid(self, force, count_waitinglist, user, auth, ignore_date=False, overpaid=False):
from pretix.base.signals import order_paid from pretix.base.signals import order_paid
can_be_paid = self.order._can_be_paid(count_waitinglist=count_waitinglist) can_be_paid = self.order._can_be_paid(count_waitinglist=count_waitinglist, ignore_date=ignore_date)
if not force and can_be_paid is not True: if not force and can_be_paid is not True:
self.order.log_action('pretix.event.order.quotaexceeded', { self.order.log_action('pretix.event.order.quotaexceeded', {
'message': can_be_paid 'message': can_be_paid
@@ -1159,7 +1159,7 @@ class OrderPayment(models.Model):
self.order.log_action('pretix.event.order.overpaid', {}, user=user, auth=auth) self.order.log_action('pretix.event.order.overpaid', {}, user=user, auth=auth)
order_paid.send(self.order.event, order=self.order) order_paid.send(self.order.event, order=self.order)
def confirm(self, count_waitinglist=True, send_mail=True, force=False, user=None, auth=None, mail_text='', lock=True): def confirm(self, count_waitinglist=True, send_mail=True, force=False, user=None, auth=None, mail_text='', ignore_date=False, lock=True):
""" """
Marks the payment as complete. If possible, this also marks the order as paid if no further Marks the payment as complete. If possible, this also marks the order as paid if no further
payment is required payment is required
@@ -1169,6 +1169,7 @@ class OrderPayment(models.Model):
:type count_waitinglist: boolean :type count_waitinglist: boolean
:param force: Whether this payment should be marked as paid even if no remaining :param force: Whether this payment should be marked as paid even if no remaining
quota is available (default: ``False``). quota is available (default: ``False``).
:param ignore_date: Whether this order should be marked as paid even when the last date of payments is over.
:type force: boolean :type force: boolean
:param send_mail: Whether an email should be sent to the user about this event (default: ``True``). :param send_mail: Whether an email should be sent to the user about this event (default: ``True``).
:type send_mail: boolean :type send_mail: boolean
@@ -1222,10 +1223,12 @@ class OrderPayment(models.Model):
# Performance optimization. In this case, there's really no reason to lock everything and an atomic # Performance optimization. In this case, there's really no reason to lock everything and an atomic
# database transaction is more than enough. # database transaction is more than enough.
with transaction.atomic(): with transaction.atomic():
self._mark_paid(force, count_waitinglist, user, auth, overpaid=payment_sum - refund_sum > self.order.total) self._mark_paid(force, count_waitinglist, user, auth, overpaid=payment_sum - refund_sum > self.order.total,
ignore_date=ignore_date)
else: else:
with self.order.event.lock(): with self.order.event.lock():
self._mark_paid(force, count_waitinglist, user, auth, overpaid=payment_sum - refund_sum > self.order.total) self._mark_paid(force, count_waitinglist, user, auth, overpaid=payment_sum - refund_sum > self.order.total,
ignore_date=ignore_date)
invoice = None invoice = None
if invoice_qualified(self.order): if invoice_qualified(self.order):

View File

@@ -162,7 +162,7 @@ def mark_order_expired(order, user=None, auth=None):
return order return order
def approve_order(order, user=None, send_mail: bool=True, auth=None): def approve_order(order, user=None, send_mail: bool=True, auth=None, force=False):
""" """
Mark this order as approved Mark this order as approved
:param order: The order to change :param order: The order to change
@@ -185,7 +185,7 @@ def approve_order(order, user=None, send_mail: bool=True, auth=None):
fee=None fee=None
) )
try: try:
p.confirm(send_mail=False, count_waitinglist=False, user=user, auth=auth) p.confirm(send_mail=False, count_waitinglist=False, user=user, auth=auth, ignore_date=True, force=force)
except Quota.QuotaExceededException: except Quota.QuotaExceededException:
raise OrderError(error_messages['unavailable']) raise OrderError(error_messages['unavailable'])

View File

@@ -268,6 +268,26 @@ def test_approve_free(event):
assert 'confirmed' in djmail.outbox[0].subject assert 'confirmed' in djmail.outbox[0].subject
@pytest.mark.django_db
def test_approve_free_after_last_payment_date(event):
event.settings.payment_term_last = (now() - timedelta(days=1)).date().isoformat()
djmail.outbox = []
event.settings.invoice_generate = 'True'
o1 = Order.objects.create(
code='FOO', event=event, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now() - timedelta(days=10),
total=0, require_approval=True
)
approve_order(o1)
o1.refresh_from_db()
assert o1.status == Order.STATUS_PAID
assert not o1.require_approval
assert o1.invoices.count() == 0
assert len(djmail.outbox) == 1
assert 'confirmed' in djmail.outbox[0].subject
@pytest.mark.django_db @pytest.mark.django_db
def test_deny(event): def test_deny(event):
djmail.outbox = [] djmail.outbox = []