mirror of
https://github.com/pretix/pretix.git
synced 2026-08-19 12:16:26 +00:00
Fix #1067 -- Allow to manually create partial payments
This commit is contained in:
@@ -49,7 +49,7 @@ class ExtendForm(I18nModelForm):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
class MarkPaidForm(forms.Form):
|
class ConfirmPaymentForm(forms.Form):
|
||||||
force = forms.BooleanField(
|
force = forms.BooleanField(
|
||||||
label=_('Overbook quota and ignore late payment'),
|
label=_('Overbook quota and ignore late payment'),
|
||||||
help_text=_('If you check this box, this operation will be performed even if it leads to an overbooked quota '
|
help_text=_('If you check this box, this operation will be performed even if it leads to an overbooked quota '
|
||||||
@@ -75,6 +75,20 @@ class MarkPaidForm(forms.Form):
|
|||||||
del self.fields['force']
|
del self.fields['force']
|
||||||
|
|
||||||
|
|
||||||
|
class MarkPaidForm(ConfirmPaymentForm):
|
||||||
|
amount = forms.DecimalField(
|
||||||
|
required=True,
|
||||||
|
max_digits=10, decimal_places=2,
|
||||||
|
localize=True,
|
||||||
|
label=_('Payment amount'),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
change_decimal_field(self.fields['amount'], self.instance.event.currency)
|
||||||
|
self.fields['amount'].initial = max(0, self.instance.pending_sum)
|
||||||
|
|
||||||
|
|
||||||
class ExporterForm(forms.Form):
|
class ExporterForm(forms.Form):
|
||||||
def clean(self):
|
def clean(self):
|
||||||
data = super().clean()
|
data = super().clean()
|
||||||
|
|||||||
@@ -18,17 +18,21 @@
|
|||||||
<form method="post" class="form-horizontal" href="">
|
<form method="post" class="form-horizontal" href="">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<p>{% blocktrans trimmed %}
|
<p>{% blocktrans trimmed %}
|
||||||
Do you really want to mark this order as paid?
|
Do you really want to create a manual payment for this order?
|
||||||
{% endblocktrans %}</p>
|
{% endblocktrans %}</p>
|
||||||
<input type="hidden" name="status" value="p" />
|
<input type="hidden" name="status" value="p" />
|
||||||
{% bootstrap_form form layout='horizontal' horizontal_label_class='sr-only' horizontal_field_class='col-md-12' %}
|
{% bootstrap_form_errors form %}
|
||||||
|
{% bootstrap_field form.amount layout='horizontal' %}
|
||||||
|
{% if form.force %}
|
||||||
|
{% bootstrap_field form.force layout='horizontal' horizontal_label_class='sr-only' horizontal_field_class='col-md-12' %}
|
||||||
|
{% endif %}
|
||||||
<div class="form-group submit-group">
|
<div class="form-group submit-group">
|
||||||
<a class="btn btn-default btn-lg"
|
<a class="btn 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 "Cancel" %}
|
{% trans "Cancel" %}
|
||||||
</a>
|
</a>
|
||||||
<button class="btn btn-primary btn-save btn-lg" type="submit">
|
<button class="btn btn-primary btn-save btn-lg" type="submit">
|
||||||
{% trans "Mark as paid" %}
|
{% trans "Create payment" %}
|
||||||
</button>
|
</button>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ from pretix.base.views.mixins import OrderQuestionsViewMixin
|
|||||||
from pretix.base.views.tasks import AsyncAction
|
from pretix.base.views.tasks import AsyncAction
|
||||||
from pretix.control.forms.filter import EventOrderFilterForm, RefundFilterForm
|
from pretix.control.forms.filter import EventOrderFilterForm, RefundFilterForm
|
||||||
from pretix.control.forms.orders import (
|
from pretix.control.forms.orders import (
|
||||||
CommentForm, ExporterForm, ExtendForm, MarkPaidForm, OrderContactForm,
|
CommentForm, ConfirmPaymentForm, ExporterForm, ExtendForm, MarkPaidForm,
|
||||||
OrderLocaleForm, OrderMailForm, OrderPositionAddForm,
|
OrderContactForm, OrderLocaleForm, OrderMailForm, OrderPositionAddForm,
|
||||||
OrderPositionChangeForm, OrderRefundForm, OtherOperationsForm,
|
OrderPositionChangeForm, OrderRefundForm, OtherOperationsForm,
|
||||||
)
|
)
|
||||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||||
@@ -404,7 +404,7 @@ class OrderPaymentConfirm(OrderView):
|
|||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def mark_paid_form(self):
|
def mark_paid_form(self):
|
||||||
return MarkPaidForm(
|
return ConfirmPaymentForm(
|
||||||
instance=self.order,
|
instance=self.order,
|
||||||
data=self.request.POST if self.request.method == "POST" else None,
|
data=self.request.POST if self.request.method == "POST" else None,
|
||||||
)
|
)
|
||||||
@@ -675,7 +675,7 @@ class OrderTransition(OrderView):
|
|||||||
def post(self, *args, **kwargs):
|
def post(self, *args, **kwargs):
|
||||||
to = self.request.POST.get('status', '')
|
to = self.request.POST.get('status', '')
|
||||||
if self.order.status in (Order.STATUS_PENDING, Order.STATUS_EXPIRED) and to == 'p' and self.mark_paid_form.is_valid():
|
if self.order.status in (Order.STATUS_PENDING, Order.STATUS_EXPIRED) and to == 'p' and self.mark_paid_form.is_valid():
|
||||||
ps = max(0, self.order.pending_sum)
|
ps = self.mark_paid_form.cleaned_data['amount']
|
||||||
try:
|
try:
|
||||||
p = self.order.payments.get(
|
p = self.order.payments.get(
|
||||||
state__in=(OrderPayment.PAYMENT_STATE_PENDING, OrderPayment.PAYMENT_STATE_CREATED),
|
state__in=(OrderPayment.PAYMENT_STATE_PENDING, OrderPayment.PAYMENT_STATE_CREATED),
|
||||||
@@ -718,7 +718,7 @@ class OrderTransition(OrderView):
|
|||||||
messages.warning(self.request, _('The order has been marked as paid, but we were unable to send a '
|
messages.warning(self.request, _('The order has been marked as paid, but we were unable to send a '
|
||||||
'confirmation mail.'))
|
'confirmation mail.'))
|
||||||
else:
|
else:
|
||||||
messages.success(self.request, _('The order has been marked as paid.'))
|
messages.success(self.request, _('The payment has been created successfully.'))
|
||||||
elif self.order.cancel_allowed() and to == 'c':
|
elif self.order.cancel_allowed() and to == 'c':
|
||||||
cancel_order(self.order, user=self.request.user, send_mail=self.request.POST.get("send_email") == "on")
|
cancel_order(self.order, user=self.request.user, send_mail=self.request.POST.get("send_email") == "on")
|
||||||
messages.success(self.request, _('The order has been canceled.'))
|
messages.success(self.request, _('The order has been canceled.'))
|
||||||
|
|||||||
@@ -193,6 +193,7 @@ def test_order_transition_to_paid_in_time_success(client, env):
|
|||||||
q.items.add(env[3])
|
q.items.add(env[3])
|
||||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||||
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||||
|
'amount': str(env[2].pending_sum),
|
||||||
'status': 'p'
|
'status': 'p'
|
||||||
})
|
})
|
||||||
o = Order.objects.get(id=env[2].id)
|
o = Order.objects.get(id=env[2].id)
|
||||||
@@ -208,7 +209,8 @@ def test_order_transition_to_paid_expired_quota_left(client, env):
|
|||||||
q.items.add(env[3])
|
q.items.add(env[3])
|
||||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||||
res = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
res = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||||
'status': 'p'
|
'status': 'p',
|
||||||
|
'amount': str(o.pending_sum),
|
||||||
})
|
})
|
||||||
o = Order.objects.get(id=env[2].id)
|
o = Order.objects.get(id=env[2].id)
|
||||||
assert res.status_code < 400
|
assert res.status_code < 400
|
||||||
@@ -279,6 +281,7 @@ def test_order_transition(client, env, process):
|
|||||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||||
client.get('/control/event/dummy/dummy/orders/FOO/transition?status=' + process[1])
|
client.get('/control/event/dummy/dummy/orders/FOO/transition?status=' + process[1])
|
||||||
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||||
|
'amount': str(o.pending_sum),
|
||||||
'status': process[1]
|
'status': process[1]
|
||||||
})
|
})
|
||||||
o = Order.objects.get(id=env[2].id)
|
o = Order.objects.get(id=env[2].id)
|
||||||
@@ -562,7 +565,8 @@ def test_order_mark_paid_overdue_quota_blocked_by_waiting_list(client, env):
|
|||||||
|
|
||||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||||
response = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
response = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||||
'status': 'p'
|
'status': 'p',
|
||||||
|
'amount': str(o.pending_sum),
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
assert 'alert-success' in response.rendered_content
|
assert 'alert-success' in response.rendered_content
|
||||||
o = Order.objects.get(id=env[2].id)
|
o = Order.objects.get(id=env[2].id)
|
||||||
@@ -580,6 +584,7 @@ def test_order_mark_paid_blocked(client, env):
|
|||||||
|
|
||||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||||
response = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
response = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||||
|
'amount': str(o.pending_sum),
|
||||||
'status': 'p'
|
'status': 'p'
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
assert 'alert-danger' in response.rendered_content
|
assert 'alert-danger' in response.rendered_content
|
||||||
@@ -588,7 +593,7 @@ def test_order_mark_paid_blocked(client, env):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_order_mark_paid_overpaid_exired(client, env):
|
def test_order_mark_paid_overpaid_epxired(client, env):
|
||||||
o = Order.objects.get(id=env[2].id)
|
o = Order.objects.get(id=env[2].id)
|
||||||
o.status = Order.STATUS_EXPIRED
|
o.status = Order.STATUS_EXPIRED
|
||||||
o.expires = now() - timedelta(days=5)
|
o.expires = now() - timedelta(days=5)
|
||||||
@@ -601,6 +606,7 @@ def test_order_mark_paid_overpaid_exired(client, env):
|
|||||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||||
response = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
response = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||||
'status': 'p',
|
'status': 'p',
|
||||||
|
'amount': '0.00',
|
||||||
'force': 'on'
|
'force': 'on'
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
assert 'alert-success' in response.rendered_content
|
assert 'alert-success' in response.rendered_content
|
||||||
@@ -622,6 +628,7 @@ def test_order_mark_paid_forced(client, env):
|
|||||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||||
response = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
response = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||||
'status': 'p',
|
'status': 'p',
|
||||||
|
'amount': str(o.pending_sum),
|
||||||
'force': 'on'
|
'force': 'on'
|
||||||
}, follow=True)
|
}, follow=True)
|
||||||
assert 'alert-success' in response.rendered_content
|
assert 'alert-success' in response.rendered_content
|
||||||
|
|||||||
Reference in New Issue
Block a user