Fix #571 -- Partial payments and refunds

This commit is contained in:
Raphael Michel
2018-06-26 12:09:36 +02:00
parent 8e7af49206
commit 18a378976b
115 changed files with 6026 additions and 1598 deletions

View File

@@ -3,18 +3,20 @@ import textwrap
from collections import OrderedDict
from django import forms
from django.http import HttpRequest
from django.template.loader import get_template
from django.utils.translation import ugettext_lazy as _
from i18nfield.fields import I18nFormField, I18nTextarea
from i18nfield.strings import LazyI18nString
from pretix.base.models import Order
from pretix.base.models import OrderPayment
from pretix.base.payment import BasePaymentProvider
class BankTransfer(BasePaymentProvider):
identifier = 'banktransfer'
verbose_name = _('Bank transfer')
abort_pending_allowed = True
@staticmethod
def form_field(**kwargs):
@@ -65,6 +67,9 @@ class BankTransfer(BasePaymentProvider):
def checkout_prepare(self, request, total):
return True
def payment_prepare(self, request: HttpRequest, payment: OrderPayment):
return True
def payment_is_valid_session(self, request):
return True
@@ -81,12 +86,12 @@ class BankTransfer(BasePaymentProvider):
}
return template.render(ctx)
def order_pending_render(self, request, order) -> str:
def payment_pending_render(self, request: HttpRequest, payment: OrderPayment):
template = get_template('pretixplugins/banktransfer/pending.html')
ctx = {
'event': self.event,
'order': order,
'code': self._code(order),
'code': self._code(payment.order),
'order': payment.order,
'details': self.settings.get('bank_details', as_type=LazyI18nString),
}
return template.render(ctx)
@@ -102,18 +107,18 @@ class BankTransfer(BasePaymentProvider):
'payment_info': payment_info, 'order': order}
return template.render(ctx)
def _code(self, order: Order):
def _code(self, order):
if self.settings.get('omit_hyphen', as_type=bool):
return self.event.slug.upper() + order.code
else:
return order.full_code
def shred_payment_info(self, order: Order):
if not order.payment_info:
def shred_payment_info(self, obj):
if not obj.info_data:
return
d = json.loads(order.payment_info)
d = obj.info_data
d['reference'] = ''
d['payer'] = ''
d['_shredded'] = True
order.payment_info = json.dumps(d)
order.save(update_fields=['payment_info'])
obj.info = json.dumps(d)
obj.save(update_fields=['info'])

View File

@@ -1,4 +1,3 @@
import json
import logging
import re
from decimal import Decimal
@@ -10,11 +9,10 @@ from django.db.models import Q
from django.utils.translation import ugettext_noop
from pretix.base.i18n import language
from pretix.base.models import Event, Order, Organizer, Quota
from pretix.base.models import Event, Order, OrderPayment, Organizer, Quota
from pretix.base.services.async import TransactionAwareTask
from pretix.base.services.locking import LockTimeoutException
from pretix.base.services.mail import SendMailException
from pretix.base.services.orders import mark_order_paid
from pretix.celery_app import app
from .models import BankImportJob, BankTransaction
@@ -50,7 +48,7 @@ def _handle_transaction(trans: BankTransaction, code: str, event: Event=None, or
trans.save()
return
if trans.order.status == Order.STATUS_PAID:
if trans.order.status == Order.STATUS_PAID and trans.order.pending_sum <= Decimal('0.00'):
trans.state = BankTransaction.STATE_DUPLICATE
elif trans.order.status == Order.STATUS_REFUNDED:
trans.state = BankTransaction.STATE_ERROR
@@ -58,17 +56,23 @@ def _handle_transaction(trans: BankTransaction, code: str, event: Event=None, or
elif trans.order.status == Order.STATUS_CANCELED:
trans.state = BankTransaction.STATE_ERROR
trans.message = ugettext_noop('The order has already been canceled.')
elif trans.amount != trans.order.total:
trans.state = BankTransaction.STATE_INVALID
trans.message = ugettext_noop('The transaction amount is incorrect.')
else:
p = trans.order.payments.get_or_create(
amount=trans.amount,
provider='banktransfer',
state__in=(OrderPayment.PAYMENT_STATE_CREATED, OrderPayment.PAYMENT_STATE_PENDING),
defaults={
'state': OrderPayment.PAYMENT_STATE_CREATED,
}
)[0]
p.info_data = {
'reference': trans.reference,
'date': trans.date,
'payer': trans.payer,
'trans_id': trans.pk
}
try:
mark_order_paid(trans.order, provider='banktransfer', info=json.dumps({
'reference': trans.reference,
'date': trans.date,
'payer': trans.payer,
'trans_id': trans.pk
}))
p.confirm()
except Quota.QuotaExceededException as e:
trans.state = BankTransaction.STATE_ERROR
trans.message = str(e)
@@ -77,6 +81,10 @@ def _handle_transaction(trans: BankTransaction, code: str, event: Event=None, or
trans.message = ugettext_noop('Problem sending email.')
else:
trans.state = BankTransaction.STATE_VALID
trans.order.payments.filter(
provider='banktransfer',
state__in=(OrderPayment.PAYMENT_STATE_CREATED, OrderPayment.PAYMENT_STATE_PENDING),
).update(state=OrderPayment.PAYMENT_STATE_CANCELED)
trans.save()

View File

@@ -1,22 +1,5 @@
{% load i18n %}
{% if payment_info and order.status == "p" %}
<p>{% blocktrans trimmed %}
This order has been paid via bank transfer.
{% endblocktrans %}</p>
{% elif order.status == "p" %}
<p>{% blocktrans trimmed %}
This order has been marked as paid via bank transfer manually.
{% endblocktrans %}</p>
{% elif order.status == "r" %}
<p>{% blocktrans trimmed %}
This order has been paid via bank transfer and marked as refunded.
{% endblocktrans %}</p>
{% else %}
<p>{% blocktrans trimmed %}
This order has been planned to be paid via bank transfer, but no payment has been received yet.
{% endblocktrans %}</p>
{% endif %}
{% if payment_info %}
<dl class="dl-horizontal">
<dt>{% trans "Payer" %}</dt>

View File

@@ -14,9 +14,8 @@ from django.utils.timezone import now
from django.utils.translation import ugettext as _
from django.views.generic import DetailView, ListView, View
from pretix.base.models import Order, Quota
from pretix.base.models import Order, OrderPayment, Quota
from pretix.base.services.mail import SendMailException
from pretix.base.services.orders import mark_order_paid
from pretix.base.settings import SettingsSandbox
from pretix.base.templatetags.money import money_filter
from pretix.control.permissions import (
@@ -66,15 +65,22 @@ class ActionView(View):
'message': _('The order has already been canceled.')
})
p = trans.order.payments.get_or_create(
amount=trans.amount,
provider='banktransfer',
state__in=(OrderPayment.PAYMENT_STATE_CREATED, OrderPayment.PAYMENT_STATE_PENDING),
defaults={
'state': OrderPayment.PAYMENT_STATE_CREATED,
}
)[0]
p.info_data = {
'reference': trans.reference,
'date': trans.date,
'payer': trans.payer,
'trans_id': trans.pk
}
try:
mark_order_paid(trans.order, provider='banktransfer', info=json.dumps({
'reference': trans.reference,
'date': trans.date,
'payer': trans.payer,
'trans_id': trans.pk
}), count_waitinglist=False)
trans.state = BankTransaction.STATE_VALID
trans.save()
p.confirm(user=self.request.user)
except Quota.QuotaExceededException as e:
return JsonResponse({
'status': 'error',
@@ -88,8 +94,12 @@ class ActionView(View):
else:
trans.state = BankTransaction.STATE_VALID
trans.save()
trans.order.payments.filter(
provider='banktransfer',
state__in=(OrderPayment.PAYMENT_STATE_CREATED, OrderPayment.PAYMENT_STATE_PENDING),
).update(state=OrderPayment.PAYMENT_STATE_CANCELED)
return JsonResponse({
'status': 'ok'
'status': 'ok',
})
def _assign(self, trans, code):