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

@@ -38,7 +38,7 @@ def dashboard_env():
code='FOO', event=event, email='dummy@dummy.test',
status=Order.STATUS_PAID,
datetime=now(), expires=now() + timedelta(days=10),
total=33, payment_provider='banktransfer', locale='en'
total=33, locale='en'
)
OrderPosition.objects.create(
order=order_paid,
@@ -70,7 +70,7 @@ def test_dashboard_pending_not_count(dashboard_env):
code='FOO', event=dashboard_env[0], email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now() + timedelta(days=10),
total=23, payment_provider='banktransfer', locale='en'
total=23, locale='en'
)
OrderPosition.objects.create(
order=order_pending,
@@ -122,25 +122,25 @@ def checkin_list_env():
code='PENDING', event=event, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now() + timedelta(days=10),
total=23, payment_provider='banktransfer', locale='en'
total=23, locale='en'
)
order_a1 = Order.objects.create(
code='A1', event=event, email='a1dummy@dummy.test',
status=Order.STATUS_PAID,
datetime=now(), expires=now() + timedelta(days=10),
total=33, payment_provider='banktransfer', locale='en'
total=33, locale='en'
)
order_a2 = Order.objects.create(
code='A2', event=event, email='a2dummy@dummy.test',
status=Order.STATUS_PAID,
datetime=now(), expires=now() + timedelta(days=10),
total=23, payment_provider='banktransfer', locale='en'
total=23, locale='en'
)
order_a3 = Order.objects.create(
code='A3', event=event, email='a3dummy@dummy.test',
status=Order.STATUS_PAID,
datetime=now(), expires=now() + timedelta(days=10),
total=23, payment_provider='banktransfer', locale='en'
total=23, locale='en'
)
# order position
@@ -298,19 +298,19 @@ def checkin_list_with_addon_env():
code='PENDING', event=event, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now() + timedelta(days=10),
total=23, payment_provider='banktransfer', locale='en'
total=23, locale='en'
)
order_a1 = Order.objects.create(
code='A1', event=event, email='a1dummy@dummy.test',
status=Order.STATUS_PAID,
datetime=now(), expires=now() + timedelta(days=10),
total=33, payment_provider='banktransfer', locale='en'
total=33, locale='en'
)
order_a2 = Order.objects.create(
code='A2', event=event, email='a2dummy@dummy.test',
status=Order.STATUS_PAID,
datetime=now(), expires=now() + timedelta(days=10),
total=23, payment_provider='banktransfer', locale='en'
total=23, locale='en'
)
# order position

View File

@@ -838,7 +838,7 @@ class SubEventsTest(SoupTest):
code='FOO', event=self.event1, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now() + datetime.timedelta(days=10),
total=14, payment_provider='banktransfer', locale='en'
total=14, locale='en'
)
OrderPosition.objects.create(
order=o,
@@ -1180,7 +1180,7 @@ class SubEventsTest(SoupTest):
code='FOO', event=self.event1, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now() + datetime.timedelta(days=10),
total=14, payment_provider='banktransfer', locale='en'
total=14, locale='en'
)
OrderPosition.objects.create(
order=o,
@@ -1266,7 +1266,7 @@ class EventDeletionTest(SoupTest):
code='FOO', event=self.event1, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now(),
total=14, payment_provider='banktransfer', locale='en'
total=14, locale='en'
)
self.post_doc('/control/event/ccc/30c3/delete/', {
'user_pw': 'dummy',

View File

@@ -185,7 +185,7 @@ class QuestionsTest(ItemFormTest):
o = Order.objects.create(code='FOO', event=self.event1, email='dummy@dummy.test',
status=Order.STATUS_PENDING, datetime=now(),
expires=now() + datetime.timedelta(days=10),
total=14, payment_provider='banktransfer', locale='en')
total=14, locale='en')
op = OrderPosition.objects.create(order=o, item=item1, variation=None, price=Decimal("14"),
attendee_name="Peter")
op.answers.create(question=c, answer='42')
@@ -407,7 +407,7 @@ class ItemsTest(ItemFormTest):
code='FOO', event=self.event1, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now() + datetime.timedelta(days=10),
total=14, payment_provider='banktransfer', locale='en'
total=14, locale='en'
)
OrderPosition.objects.create(
order=o,

View File

@@ -3,15 +3,18 @@ from decimal import Decimal
from unittest import mock
import pytest
from bs4 import BeautifulSoup
from django.core import mail
from django.utils.timezone import now
from django_countries.fields import Country
from tests.base import SoupTest
from tests.plugins.stripe.test_provider import MockedCharge
from pretix.base.models import (
Event, InvoiceAddress, Item, Order, OrderPosition, Organizer, Question,
QuestionAnswer, Quota, Team, User,
Event, InvoiceAddress, Item, Order, OrderPayment, OrderPosition,
OrderRefund, Organizer, Question, QuestionAnswer, Quota, Team, User,
)
from pretix.base.payment import PaymentException
from pretix.base.services.invoices import (
generate_cancellation, generate_invoice,
)
@@ -22,7 +25,7 @@ def env():
o = Organizer.objects.create(name='Dummy', slug='dummy')
event = Event.objects.create(
organizer=o, name='Dummy', slug='dummy',
date_from=now(), plugins='pretix.plugins.banktransfer,tests.testdummy'
date_from=now(), plugins='pretix.plugins.banktransfer,pretix.plugins.stripe,tests.testdummy'
)
event.settings.set('ticketoutput_testdummy__enabled', True)
user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
@@ -33,7 +36,10 @@ def env():
code='FOO', event=event, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now() + timedelta(days=10),
total=14, payment_provider='banktransfer', locale='en'
total=14, locale='en'
)
o.payments.create(
amount=o.total, provider='banktransfer', state=OrderPayment.PAYMENT_STATE_PENDING
)
ticket = Item.objects.create(event=event, name='Early-bird ticket',
category=None, default_price=23,
@@ -212,7 +218,7 @@ def test_order_transition_to_paid_expired_quota_left(client, env):
(Order.STATUS_PAID, Order.STATUS_PENDING, True),
(Order.STATUS_PAID, Order.STATUS_CANCELED, False),
(Order.STATUS_PAID, Order.STATUS_REFUNDED, True),
(Order.STATUS_PAID, Order.STATUS_REFUNDED, False),
(Order.STATUS_PAID, Order.STATUS_EXPIRED, False),
(Order.STATUS_PENDING, Order.STATUS_CANCELED, True),
@@ -688,7 +694,7 @@ class OrderChangeTests(SoupTest):
code='FOO', event=self.event, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now() + timedelta(days=10),
total=Decimal('46.00'), payment_provider='banktransfer'
total=Decimal('46.00'),
)
self.tr7 = self.event.tax_rules.create(rate=Decimal('7.00'))
self.tr19 = self.event.tax_rules.create(rate=Decimal('19.00'))
@@ -944,3 +950,530 @@ def test_check_vatid_unavailable(client, env):
assert 'alert-danger' in response.rendered_content
ia.refresh_from_db()
assert not ia.vat_id_validated
@pytest.mark.django_db
def test_cancel_payment(client, env):
p = env[2].payments.last()
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.post('/control/event/dummy/dummy/orders/FOO/payments/{}/cancel'.format(p.pk), {}, follow=True)
assert 'alert-success' in response.rendered_content
p.refresh_from_db()
assert p.state == OrderPayment.PAYMENT_STATE_CANCELED
response = client.post('/control/event/dummy/dummy/orders/FOO/payments/{}/cancel'.format(p.pk), {}, follow=True)
assert 'alert-danger' in response.rendered_content
@pytest.mark.django_db
def test_cancel_refund(client, env):
r = env[2].refunds.create(
provider='stripe',
state='transit',
source='admin',
amount=Decimal('23.00'),
execution_date=now(),
)
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/cancel'.format(r.pk), {}, follow=True)
assert 'alert-success' in response.rendered_content
r.refresh_from_db()
assert r.state == OrderRefund.REFUND_STATE_CANCELED
r.state = OrderRefund.REFUND_STATE_DONE
r.save()
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/cancel'.format(r.pk), {}, follow=True)
assert 'alert-danger' in response.rendered_content
r.refresh_from_db()
assert r.state == OrderRefund.REFUND_STATE_DONE
@pytest.mark.django_db
def test_process_refund(client, env):
r = env[2].refunds.create(
provider='stripe',
state='external',
source='external',
amount=Decimal('23.00'),
execution_date=now(),
)
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/process'.format(r.pk), {}, follow=True)
assert 'alert-success' in response.rendered_content
r.refresh_from_db()
assert r.state == OrderRefund.REFUND_STATE_DONE
env[2].refresh_from_db()
assert env[2].status == Order.STATUS_PENDING
@pytest.mark.django_db
def test_process_refund_invalid_state(client, env):
r = env[2].refunds.create(
provider='stripe',
state='canceled',
source='external',
amount=Decimal('23.00'),
execution_date=now(),
)
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/process'.format(r.pk), {}, follow=True)
assert 'alert-danger' in response.rendered_content
r.refresh_from_db()
assert r.state == OrderRefund.REFUND_STATE_CANCELED
@pytest.mark.django_db
def test_process_refund_mark_refunded(client, env):
r = env[2].refunds.create(
provider='stripe',
state='external',
source='external',
amount=Decimal('23.00'),
execution_date=now(),
)
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/process'.format(r.pk), {'action': 'r'},
follow=True)
assert 'alert-success' in response.rendered_content
r.refresh_from_db()
assert r.state == OrderRefund.REFUND_STATE_DONE
env[2].refresh_from_db()
assert env[2].status == Order.STATUS_REFUNDED
@pytest.mark.django_db
def test_done_refund(client, env):
r = env[2].refunds.create(
provider='stripe',
state='transit',
source='admin',
amount=Decimal('23.00'),
execution_date=now(),
)
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/done'.format(r.pk), {}, follow=True)
assert 'alert-success' in response.rendered_content
r.refresh_from_db()
assert r.state == OrderRefund.REFUND_STATE_DONE
@pytest.mark.django_db
def test_done_refund_invalid_state(client, env):
r = env[2].refunds.create(
provider='stripe',
state='external',
source='external',
amount=Decimal('23.00'),
execution_date=now(),
)
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/done'.format(r.pk), {}, follow=True)
assert 'alert-danger' in response.rendered_content
r.refresh_from_db()
assert r.state == OrderRefund.REFUND_STATE_EXTERNAL
@pytest.mark.django_db
def test_confirm_payment(client, env):
p = env[2].payments.last()
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.post('/control/event/dummy/dummy/orders/FOO/payments/{}/confirm'.format(p.pk), {}, follow=True)
assert 'alert-success' in response.rendered_content
p.refresh_from_db()
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
env[2].refresh_from_db()
assert env[2].status == Order.STATUS_PAID
@pytest.mark.django_db
def test_confirm_payment_invalid_state(client, env):
p = env[2].payments.last()
p.state = OrderPayment.PAYMENT_STATE_FAILED
p.save()
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.post('/control/event/dummy/dummy/orders/FOO/payments/{}/confirm'.format(p.pk), {}, follow=True)
assert 'alert-danger' in response.rendered_content
p.refresh_from_db()
assert p.state == OrderPayment.PAYMENT_STATE_FAILED
env[2].refresh_from_db()
assert env[2].status == Order.STATUS_PENDING
@pytest.mark.django_db
def test_confirm_payment_partal_amount(client, env):
p = env[2].payments.last()
p.amount -= Decimal(5.00)
p.save()
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.post('/control/event/dummy/dummy/orders/FOO/payments/{}/confirm'.format(p.pk), {}, follow=True)
assert 'alert-success' in response.rendered_content
p.refresh_from_db()
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
env[2].refresh_from_db()
assert env[2].status == Order.STATUS_PENDING
@pytest.mark.django_db
def test_refund_paid_order_fully_mark_as_refunded(client, env):
p = env[2].payments.last()
p.confirm()
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.get('/control/event/dummy/dummy/orders/FOO/refund')
doc = BeautifulSoup(response.content, "lxml")
assert doc.select("input[name$=partial_amount]")[0]["value"] == "14.00"
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
'start-partial_amount': '14.00',
'start-mode': 'full',
'start-action': 'mark_refunded'
}, follow=True)
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
'start-partial_amount': '14.00',
'start-mode': 'full',
'start-action': 'mark_refunded',
'refund-manual': '14.00',
'manual_state': 'done',
'perform': 'on'
}, follow=True)
p.refresh_from_db()
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
env[2].refresh_from_db()
r = env[2].refunds.last()
assert r.provider == "manual"
assert r.state == OrderRefund.REFUND_STATE_DONE
assert r.amount == Decimal('14.00')
assert env[2].status == Order.STATUS_REFUNDED
@pytest.mark.django_db
def test_refund_paid_order_fully_mark_as_pending(client, env):
p = env[2].payments.last()
p.confirm()
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.get('/control/event/dummy/dummy/orders/FOO/refund')
doc = BeautifulSoup(response.content, "lxml")
assert doc.select("input[name$=partial_amount]")[0]["value"] == "14.00"
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
'start-partial_amount': '14.00',
'start-mode': 'full',
'start-action': 'mark_pending',
'refund-manual': '14.00',
'manual_state': 'pending',
'perform': 'on'
}, follow=True)
p.refresh_from_db()
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
env[2].refresh_from_db()
r = env[2].refunds.last()
assert r.provider == "manual"
assert r.state == OrderRefund.REFUND_STATE_CREATED
assert r.amount == Decimal('14.00')
assert env[2].status == Order.STATUS_PENDING
@pytest.mark.django_db
def test_refund_paid_order_partially_mark_as_pending(client, env):
p = env[2].payments.last()
p.confirm()
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.get('/control/event/dummy/dummy/orders/FOO/refund')
doc = BeautifulSoup(response.content, "lxml")
assert doc.select("input[name$=partial_amount]")[0]["value"] == "14.00"
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
'start-partial_amount': '7.00',
'start-mode': 'partial',
'start-action': 'mark_pending'
}, follow=True)
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
'start-partial_amount': '7.00',
'start-mode': 'partial',
'start-action': 'mark_pending',
'refund-manual': '7.00',
'manual_state': 'pending',
'perform': 'on'
}, follow=True)
p.refresh_from_db()
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
env[2].refresh_from_db()
r = env[2].refunds.last()
assert r.provider == "manual"
assert r.state == OrderRefund.REFUND_STATE_CREATED
assert r.amount == Decimal('7.00')
assert env[2].status == Order.STATUS_PENDING
@pytest.mark.django_db
def test_refund_propose_lower_payment(client, env):
p = env[2].payments.last()
p.amount = Decimal('8.00')
p.confirm()
p2 = env[2].payments.create(
amount=Decimal('6.00'), provider='stripe', state=OrderPayment.PAYMENT_STATE_CONFIRMED
)
client.login(email='dummy@dummy.dummy', password='dummy')
client.get('/control/event/dummy/dummy/orders/FOO/refund')
response = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
'start-partial_amount': '7.00',
'start-mode': 'partial',
'start-action': 'mark_pending'
}, follow=True)
doc = BeautifulSoup(response.content, "lxml")
assert doc.select("input[name=refund-{}]".format(p2.pk))[0]['value'] == '6.00'
assert doc.select("input[name=refund-manual]".format(p2.pk))[0]['value'] == '1.00'
@pytest.mark.django_db
def test_refund_propose_equal_payment(client, env):
p = env[2].payments.last()
p.amount = Decimal('7.00')
p.confirm()
p2 = env[2].payments.create(
amount=Decimal('7.00'), provider='stripe', state=OrderPayment.PAYMENT_STATE_CONFIRMED
)
client.login(email='dummy@dummy.dummy', password='dummy')
client.get('/control/event/dummy/dummy/orders/FOO/refund')
response = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
'start-partial_amount': '7.00',
'start-mode': 'partial',
'start-action': 'mark_pending'
}, follow=True)
doc = BeautifulSoup(response.content, "lxml")
assert doc.select("input[name=refund-{}]".format(p2.pk))[0]['value'] == '7.00'
assert doc.select("input[name=refund-manual]".format(p2.pk))[0]['value'] == '0.00'
@pytest.mark.django_db
def test_refund_propose_higher_payment(client, env):
p = env[2].payments.last()
p.amount = Decimal('6.00')
p.confirm()
p2 = env[2].payments.create(
amount=Decimal('8.00'), provider='stripe', state=OrderPayment.PAYMENT_STATE_CONFIRMED
)
client.login(email='dummy@dummy.dummy', password='dummy')
client.get('/control/event/dummy/dummy/orders/FOO/refund')
response = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
'start-partial_amount': '7.00',
'start-mode': 'partial',
'start-action': 'mark_pending'
}, follow=True)
doc = BeautifulSoup(response.content, "lxml")
assert doc.select("input[name=refund-{}]".format(p2.pk))[0]['value'] == '7.00'
assert doc.select("input[name=refund-manual]".format(p2.pk))[0]['value'] == '0.00'
@pytest.mark.django_db
def test_refund_amount_does_not_match_or_invalid(client, env):
p = env[2].payments.last()
p.confirm()
client.login(email='dummy@dummy.dummy', password='dummy')
resp = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
'start-partial_amount': '7.00',
'start-mode': 'partial',
'start-action': 'mark_pending',
'refund-manual': '4.00',
'refund-{}'.format(p.pk): '4.00',
'manual_state': 'pending',
'perform': 'on'
}, follow=True)
assert b'alert-danger' in resp.content
assert b'do not match the' in resp.content
resp = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
'start-partial_amount': '15.00',
'start-mode': 'partial',
'start-action': 'mark_pending',
'refund-manual': '0.00',
'refund-{}'.format(p.pk): '15.00',
'manual_state': 'pending',
'perform': 'on'
}, follow=True)
assert b'alert-danger' in resp.content
assert b'The refund amount needs to be positive' in resp.content
resp = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
'start-partial_amount': '7.00',
'start-mode': 'partial',
'start-action': 'mark_pending',
'refund-manual': '-3.00',
'refund-{}'.format(p.pk): '10.00',
'manual_state': 'pending',
'perform': 'on'
}, follow=True)
assert b'alert-danger' in resp.content
assert b'do not match the' in resp.content
resp = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
'start-partial_amount': '7.00',
'start-mode': 'partial',
'start-action': 'mark_pending',
'refund-manual': 'AA',
'refund-{}'.format(p.pk): '10.00',
'manual_state': 'pending',
'perform': 'on'
}, follow=True)
assert b'alert-danger' in resp.content
assert b'invalid number' in resp.content
@pytest.mark.django_db
def test_refund_paid_order_automatically_failed(client, env, monkeypatch):
p = env[2].payments.last()
p.provider = 'stripe'
p.info_data = {
'id': 'foo'
}
p.confirm()
client.login(email='dummy@dummy.dummy', password='dummy')
def charge_retr(*args, **kwargs):
def refund_create(amount):
raise PaymentException('This failed.')
c = MockedCharge()
c.refunds.create = refund_create
return c
monkeypatch.setattr("stripe.Charge.retrieve", charge_retr)
r = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
'start-partial_amount': '7.00',
'start-mode': 'partial',
'start-action': 'mark_pending',
'refund-{}'.format(p.pk): '7.00',
'manual_state': 'pending',
'perform': 'on'
}, follow=True)
assert b'This failed.' in r.content
p.refresh_from_db()
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
env[2].refresh_from_db()
r = env[2].refunds.last()
assert r.provider == "stripe"
assert r.state == OrderRefund.REFUND_STATE_FAILED
assert r.amount == Decimal('7.00')
assert env[2].status == Order.STATUS_PAID
@pytest.mark.django_db
def test_refund_paid_order_automatically(client, env, monkeypatch):
p = env[2].payments.last()
p.provider = 'stripe'
p.info_data = {
'id': 'foo'
}
p.confirm()
client.login(email='dummy@dummy.dummy', password='dummy')
def charge_retr(*args, **kwargs):
def refund_create(amount):
pass
c = MockedCharge()
c.refunds.create = refund_create
return c
monkeypatch.setattr("stripe.Charge.retrieve", charge_retr)
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
'start-partial_amount': '7.00',
'start-mode': 'partial',
'start-action': 'mark_pending',
'refund-{}'.format(p.pk): '7.00',
'manual_state': 'pending',
'perform': 'on'
}, follow=True)
p.refresh_from_db()
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
env[2].refresh_from_db()
r = env[2].refunds.last()
assert r.provider == "stripe"
assert r.state == OrderRefund.REFUND_STATE_DONE
assert r.amount == Decimal('7.00')
assert env[2].status == Order.STATUS_PENDING
@pytest.mark.django_db
def test_refund_paid_order_offsetting_to_unknown(client, env):
p = env[2].payments.last()
p.confirm()
client.login(email='dummy@dummy.dummy', password='dummy')
r = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
'start-partial_amount': '5.00',
'start-mode': 'partial',
'start-action': 'mark_pending',
'refund-offsetting': '5.00',
'order-offsetting': 'BAZ',
'manual_state': 'pending',
'perform': 'on'
}, follow=True)
assert b'alert-danger' in r.content
@pytest.mark.django_db
def test_refund_paid_order_offsetting(client, env):
p = env[2].payments.last()
p.confirm()
client.login(email='dummy@dummy.dummy', password='dummy')
o = Order.objects.create(
code='BAZ', event=env[0], email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now() + timedelta(days=10),
total=5, locale='en'
)
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
'start-partial_amount': '5.00',
'start-mode': 'partial',
'start-action': 'mark_pending',
'refund-offsetting': '5.00',
'order-offsetting': 'BAZ',
'manual_state': 'pending',
'perform': 'on'
}, follow=True)
p.refresh_from_db()
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
env[2].refresh_from_db()
r = env[2].refunds.last()
assert r.provider == "offsetting"
assert r.state == OrderRefund.REFUND_STATE_DONE
assert r.amount == Decimal('5.00')
assert env[2].status == Order.STATUS_PENDING
o.refresh_from_db()
assert o.status == Order.STATUS_PAID
p2 = o.payments.first()
assert p2.provider == "offsetting"
assert p2.amount == Decimal('5.00')
assert p2.state == OrderPayment.PAYMENT_STATE_CONFIRMED
@pytest.mark.django_db
def test_refund_list(client, env):
env[2].refunds.create(
provider='banktransfer',
state='done',
source='admin',
amount=Decimal('23.00'),
execution_date=now(),
)
env[2].refunds.create(
provider='manual',
state='created',
source='admin',
amount=Decimal('23.00'),
execution_date=now(),
)
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.get('/control/event/dummy/dummy/orders/refunds/')
assert 'R-1' not in response.rendered_content
assert 'R-2' in response.rendered_content
response = client.get('/control/event/dummy/dummy/orders/refunds/?status=all')
assert 'R-1' in response.rendered_content
assert 'R-2' in response.rendered_content
response = client.get('/control/event/dummy/dummy/orders/refunds/?status=created')
assert 'R-1' not in response.rendered_content
assert 'R-2' in response.rendered_content
response = client.get('/control/event/dummy/dummy/orders/refunds/?status=done')
assert 'R-1' in response.rendered_content
assert 'R-2' not in response.rendered_content
response = client.get('/control/event/dummy/dummy/orders/refunds/?status=all&provider=manual')
assert 'R-1' not in response.rendered_content
assert 'R-2' in response.rendered_content
response = client.get('/control/event/dummy/dummy/orders/refunds/?status=all&provider=banktransfer')
assert 'R-1' in response.rendered_content
assert 'R-2' not in response.rendered_content

View File

@@ -18,7 +18,7 @@ def env():
code='FOO', event=event,
status=Order.STATUS_PENDING,
datetime=now(), expires=now() + timedelta(days=10),
total=0, payment_provider='banktransfer'
total=0,
)
Team.objects.create(pk=1, organizer=o)
return event, user, o
@@ -103,6 +103,12 @@ event_urls = [
"orders/ABC/comment",
"orders/ABC/locale",
"orders/ABC/checkvatid",
"orders/ABC/payments/1/cancel",
"orders/ABC/payments/1/confirm",
"orders/ABC/refund",
"orders/ABC/refunds/1/cancel",
"orders/ABC/refunds/1/process",
"orders/ABC/refunds/1/done",
"orders/ABC/",
"orders/",
"checkinlists/",

View File

@@ -28,7 +28,7 @@ class OrderSearchTest(SoupTest):
code='FO1A', event=self.event1, email='dummy1@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now() + datetime.timedelta(days=10),
total=14, payment_provider='banktransfer', locale='en'
total=14, locale='en'
)
InvoiceAddress.objects.create(order=o1, company="Test Ltd.", name="Peter Miller")
ticket1 = Item.objects.create(event=self.event1, name='Early-bird ticket',
@@ -47,7 +47,7 @@ class OrderSearchTest(SoupTest):
code='FO2', event=self.event2, email='dummy2@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now() + datetime.timedelta(days=10),
total=14, payment_provider='banktransfer', locale='en'
total=14, locale='en'
)
ticket2 = Item.objects.create(event=self.event1, name='Early-bird ticket',
category=None, default_price=23,

View File

@@ -29,7 +29,7 @@ class EventShredderTest(SoupTest):
code='FOO', event=self.event1, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now(),
total=14, payment_provider='banktransfer', locale='en'
total=14, locale='en'
)
self.client.login(email='dummy@dummy.dummy', password='dummy')
@@ -37,7 +37,7 @@ class EventShredderTest(SoupTest):
def test_shred_simple(self):
doc = self.get_doc('/control/event/%s/%s/shredder/' % (self.orga1.slug, self.event1.slug))
assert doc.select("input[value=order_emails]")
assert doc.select("input[value=stripe_logs]")
assert doc.select("input[value=invoices]")
doc = self.post_doc('/control/event/%s/%s/shredder/export' % (self.orga1.slug, self.event1.slug), {
'shredder': 'order_emails'
})
@@ -67,7 +67,7 @@ class EventShredderTest(SoupTest):
def test_shred_password_wrong(self):
doc = self.get_doc('/control/event/%s/%s/shredder/' % (self.orga1.slug, self.event1.slug))
assert doc.select("input[value=order_emails]")
assert doc.select("input[value=stripe_logs]")
assert doc.select("input[value=invoices]")
doc = self.post_doc('/control/event/%s/%s/shredder/export' % (self.orga1.slug, self.event1.slug), {
'shredder': 'order_emails'
})
@@ -97,7 +97,7 @@ class EventShredderTest(SoupTest):
def test_shred_confirm_code_wrong(self):
doc = self.get_doc('/control/event/%s/%s/shredder/' % (self.orga1.slug, self.event1.slug))
assert doc.select("input[value=order_emails]")
assert doc.select("input[value=stripe_logs]")
assert doc.select("input[value=invoices]")
doc = self.post_doc('/control/event/%s/%s/shredder/export' % (self.orga1.slug, self.event1.slug), {
'shredder': 'order_emails'
})
@@ -137,7 +137,7 @@ class EventShredderTest(SoupTest):
def test_shred_something_happened(self):
doc = self.get_doc('/control/event/%s/%s/shredder/' % (self.orga1.slug, self.event1.slug))
assert doc.select("input[value=order_emails]")
assert doc.select("input[value=stripe_logs]")
assert doc.select("input[value=invoices]")
doc = self.post_doc('/control/event/%s/%s/shredder/export' % (self.orga1.slug, self.event1.slug), {
'shredder': 'order_emails'
})

View File

@@ -83,7 +83,7 @@ class TaxRateFormTest(SoupTest):
code='FOO', event=self.event1, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now() + datetime.timedelta(days=10),
total=14, payment_provider='banktransfer', locale='en',
total=14, locale='en',
)
o.fees.create(fee_type=OrderFee.FEE_TYPE_PAYMENT, value=Decimal('0.25'), tax_rate=Decimal('19.00'),
tax_value=Decimal('0.05'), tax_rule=tr)
@@ -101,7 +101,7 @@ class TaxRateFormTest(SoupTest):
code='FOO', event=self.event1, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now() + datetime.timedelta(days=10),
total=12, payment_provider='banktransfer', locale='en'
total=12, locale='en'
)
o.positions.create(
item=i, price=12, tax_rule=tr, tax_rate=19, tax_value=12 - 12 / 1.19

View File

@@ -1,4 +1,5 @@
import datetime
from decimal import Decimal
import pytest
from django.utils.timezone import now
@@ -34,8 +35,22 @@ def order(item):
o = Order.objects.create(event=item.event, status=Order.STATUS_PENDING,
expires=now() + datetime.timedelta(hours=1),
total=13, code='DUMMY', email='dummy@dummy.test',
datetime=now(), payment_provider='banktransfer')
datetime=now())
OrderPosition.objects.create(order=o, item=item, price=13)
p1 = o.payments.create(
provider='stripe',
state='refunded',
amount=Decimal('23.00'),
payment_date=o.datetime,
)
o.refunds.create(
provider='stripe',
state='done',
source='admin',
amount=Decimal('23.00'),
execution_date=o.datetime,
payment=p1,
)
return o
@@ -131,6 +146,12 @@ def logged_in_client(client, event):
('/control/event/{orga}/{event}/orders/{order_code}/comment', 405),
('/control/event/{orga}/{event}/orders/{order_code}/change', 200),
('/control/event/{orga}/{event}/orders/{order_code}/locale', 200),
('/control/event/{orga}/{event}/orders/{order_code}/payments/{payment}/cancel', 200),
('/control/event/{orga}/{event}/orders/{order_code}/payments/{payment}/confirm', 200),
('/control/event/{orga}/{event}/orders/{order_code}/refund', 200),
('/control/event/{orga}/{event}/orders/{order_code}/refunds/{refund}/cancel', 200),
('/control/event/{orga}/{event}/orders/{order_code}/refunds/{refund}/process', 200),
('/control/event/{orga}/{event}/orders/{order_code}/refunds/{refund}/done', 200),
('/control/event/{orga}/{event}/orders/{order_code}/', 200),
('/control/event/{orga}/{event}/orders/overview/', 200),
('/control/event/{orga}/{event}/orders/export/', 200),
@@ -141,6 +162,8 @@ def logged_in_client(client, event):
])
@pytest.mark.django_db
def test_one_view(logged_in_client, url, expected, event, item, item_category, order, question, quota, voucher):
payment = order.payments.first()
refund = order.refunds.first()
url = url.format(
event=event.slug, orga=event.organizer.slug,
category=item_category.pk,
@@ -149,6 +172,8 @@ def test_one_view(logged_in_client, url, expected, event, item, item_category, o
question=question.pk,
quota=quota.pk,
voucher=voucher.pk,
payment=payment.pk,
refund=refund.pk
)
response = logged_in_client.get(url)
assert response.status_code == expected