mirror of
https://github.com/pretix/pretix.git
synced 2026-05-03 14:54:04 +00:00
2419 lines
84 KiB
Python
2419 lines
84 KiB
Python
import copy
|
|
import datetime
|
|
import json
|
|
from decimal import Decimal
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
from django.core import mail as djmail
|
|
from django.utils.timezone import now
|
|
from django_countries.fields import Country
|
|
from pytz import UTC
|
|
from stripe.error import APIConnectionError
|
|
from tests.plugins.stripe.test_provider import MockedCharge
|
|
|
|
from pretix.base.models import InvoiceAddress, Order, OrderPosition, Question
|
|
from pretix.base.models.orders import (
|
|
CartPosition, OrderFee, OrderPayment, OrderRefund,
|
|
)
|
|
from pretix.base.services.invoices import (
|
|
generate_cancellation, generate_invoice,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def item(event):
|
|
return event.items.create(name="Budget Ticket", default_price=23)
|
|
|
|
|
|
@pytest.fixture
|
|
def item2(event2):
|
|
return event2.items.create(name="Budget Ticket", default_price=23)
|
|
|
|
|
|
@pytest.fixture
|
|
def taxrule(event):
|
|
return event.tax_rules.create(rate=Decimal('19.00'))
|
|
|
|
|
|
@pytest.fixture
|
|
def question(event, item):
|
|
q = event.questions.create(question="T-Shirt size", type="S", identifier="ABC")
|
|
q.items.add(item)
|
|
q.options.create(answer="XL", identifier="LVETRWVU")
|
|
return q
|
|
|
|
|
|
@pytest.fixture
|
|
def question2(event2, item2):
|
|
q = event2.questions.create(question="T-Shirt size", type="S", identifier="ABC")
|
|
q.items.add(item2)
|
|
return q
|
|
|
|
|
|
@pytest.fixture
|
|
def quota(event, item):
|
|
q = event.quotas.create(name="Budget Quota", size=200)
|
|
q.items.add(item)
|
|
return q
|
|
|
|
|
|
@pytest.fixture
|
|
def order(event, item, taxrule, question):
|
|
testtime = datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC)
|
|
event.plugins += ",pretix.plugins.stripe"
|
|
event.save()
|
|
|
|
with mock.patch('django.utils.timezone.now') as mock_now:
|
|
mock_now.return_value = testtime
|
|
o = Order.objects.create(
|
|
code='FOO', event=event, email='dummy@dummy.test',
|
|
status=Order.STATUS_PENDING, secret="k24fiuwvu8kxz3y1",
|
|
datetime=datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC),
|
|
expires=datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=UTC),
|
|
total=23, locale='en'
|
|
)
|
|
p1 = o.payments.create(
|
|
provider='stripe',
|
|
state='refunded',
|
|
amount=Decimal('23.00'),
|
|
payment_date=testtime,
|
|
)
|
|
o.refunds.create(
|
|
provider='stripe',
|
|
state='done',
|
|
source='admin',
|
|
amount=Decimal('23.00'),
|
|
execution_date=testtime,
|
|
payment=p1,
|
|
)
|
|
o.payments.create(
|
|
provider='banktransfer',
|
|
state='pending',
|
|
amount=Decimal('23.00'),
|
|
)
|
|
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=taxrule)
|
|
InvoiceAddress.objects.create(order=o, company="Sample company", country=Country('NZ'))
|
|
op = OrderPosition.objects.create(
|
|
order=o,
|
|
item=item,
|
|
variation=None,
|
|
price=Decimal("23"),
|
|
attendee_name_parts={"full_name": "Peter", "_scheme": "full"},
|
|
secret="z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
|
|
pseudonymization_id="ABCDEFGHKL",
|
|
)
|
|
op.answers.create(question=question, answer='S')
|
|
return o
|
|
|
|
|
|
TEST_ORDERPOSITION_RES = {
|
|
"id": 1,
|
|
"order": "FOO",
|
|
"positionid": 1,
|
|
"item": 1,
|
|
"variation": None,
|
|
"price": "23.00",
|
|
"attendee_name_parts": {"full_name": "Peter", "_scheme": "full"},
|
|
"attendee_name": "Peter",
|
|
"attendee_email": None,
|
|
"voucher": None,
|
|
"tax_rate": "0.00",
|
|
"tax_value": "0.00",
|
|
"tax_rule": None,
|
|
"secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
|
|
"addon_to": None,
|
|
"pseudonymization_id": "ABCDEFGHKL",
|
|
"checkins": [],
|
|
"downloads": [],
|
|
"answers": [
|
|
{
|
|
"question": 1,
|
|
"answer": "S",
|
|
"question_identifier": "ABC",
|
|
"options": [],
|
|
"option_identifiers": []
|
|
}
|
|
],
|
|
"subevent": None
|
|
}
|
|
TEST_PAYMENTS_RES = [
|
|
{
|
|
"local_id": 1,
|
|
"created": "2017-12-01T10:00:00Z",
|
|
"payment_date": "2017-12-01T10:00:00Z",
|
|
"provider": "stripe",
|
|
"state": "refunded",
|
|
"amount": "23.00"
|
|
},
|
|
{
|
|
"local_id": 2,
|
|
"created": "2017-12-01T10:00:00Z",
|
|
"payment_date": None,
|
|
"provider": "banktransfer",
|
|
"state": "pending",
|
|
"amount": "23.00"
|
|
}
|
|
]
|
|
TEST_REFUNDS_RES = [
|
|
{
|
|
"local_id": 1,
|
|
"payment": 1,
|
|
"source": "admin",
|
|
"created": "2017-12-01T10:00:00Z",
|
|
"execution_date": "2017-12-01T10:00:00Z",
|
|
"provider": "stripe",
|
|
"state": "done",
|
|
"amount": "23.00"
|
|
},
|
|
]
|
|
TEST_ORDER_RES = {
|
|
"code": "FOO",
|
|
"status": "n",
|
|
"secret": "k24fiuwvu8kxz3y1",
|
|
"email": "dummy@dummy.test",
|
|
"locale": "en",
|
|
"datetime": "2017-12-01T10:00:00Z",
|
|
"expires": "2017-12-10T10:00:00Z",
|
|
"payment_date": "2017-12-01",
|
|
"fees": [
|
|
{
|
|
"fee_type": "payment",
|
|
"value": "0.25",
|
|
"description": "",
|
|
"internal_type": "",
|
|
"tax_rate": "19.00",
|
|
"tax_value": "0.05"
|
|
}
|
|
],
|
|
"payment_provider": "banktransfer",
|
|
"total": "23.00",
|
|
"comment": "",
|
|
"checkin_attention": False,
|
|
"invoice_address": {
|
|
"last_modified": "2017-12-01T10:00:00Z",
|
|
"is_business": False,
|
|
"company": "Sample company",
|
|
"name": "",
|
|
"name_parts": {},
|
|
"street": "",
|
|
"zipcode": "",
|
|
"city": "",
|
|
"country": "NZ",
|
|
"internal_reference": "",
|
|
"vat_id": "",
|
|
"vat_id_validated": False
|
|
},
|
|
"require_approval": False,
|
|
"positions": [TEST_ORDERPOSITION_RES],
|
|
"downloads": [],
|
|
"payments": TEST_PAYMENTS_RES,
|
|
"refunds": TEST_REFUNDS_RES,
|
|
}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_list(token_client, organizer, event, order, item, taxrule, question):
|
|
res = dict(TEST_ORDER_RES)
|
|
res["positions"][0]["id"] = order.positions.first().pk
|
|
res["positions"][0]["item"] = item.pk
|
|
res["positions"][0]["answers"][0]["question"] = question.pk
|
|
res["last_modified"] = order.last_modified.isoformat().replace('+00:00', 'Z')
|
|
res["fees"][0]["tax_rule"] = taxrule.pk
|
|
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/'.format(organizer.slug, event.slug))
|
|
assert resp.status_code == 200
|
|
assert [res] == resp.data['results']
|
|
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?code=FOO'.format(organizer.slug, event.slug))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?code=BAR'.format(organizer.slug, event.slug))
|
|
assert [] == resp.data['results']
|
|
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?status=n'.format(organizer.slug, event.slug))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?status=p'.format(organizer.slug, event.slug))
|
|
assert [] == resp.data['results']
|
|
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orders/?email=dummy@dummy.test'.format(organizer.slug, event.slug))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orders/?email=foo@example.org'.format(organizer.slug, event.slug))
|
|
assert [] == resp.data['results']
|
|
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?locale=en'.format(organizer.slug, event.slug))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?locale=de'.format(organizer.slug, event.slug))
|
|
assert [] == resp.data['results']
|
|
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?modified_since={}'.format(
|
|
organizer.slug, event.slug,
|
|
(order.last_modified - datetime.timedelta(hours=1)).isoformat().replace('+00:00', 'Z')
|
|
))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?modified_since={}'.format(
|
|
organizer.slug, event.slug, order.last_modified.isoformat().replace('+00:00', 'Z')
|
|
))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?modified_since={}'.format(
|
|
organizer.slug, event.slug,
|
|
(order.last_modified + datetime.timedelta(hours=1)).isoformat().replace('+00:00', 'Z')
|
|
))
|
|
assert [] == resp.data['results']
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_detail(token_client, organizer, event, order, item, taxrule, question):
|
|
res = dict(TEST_ORDER_RES)
|
|
res["positions"][0]["id"] = order.positions.first().pk
|
|
res["positions"][0]["item"] = item.pk
|
|
res["fees"][0]["tax_rule"] = taxrule.pk
|
|
res["positions"][0]["answers"][0]["question"] = question.pk
|
|
res["last_modified"] = order.last_modified.isoformat().replace('+00:00', 'Z')
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/{}/'.format(organizer.slug, event.slug,
|
|
order.code))
|
|
assert resp.status_code == 200
|
|
assert res == resp.data
|
|
|
|
order.status = 'p'
|
|
order.save()
|
|
event.settings.ticketoutput_pdf__enabled = True
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/{}/'.format(organizer.slug, event.slug,
|
|
order.code))
|
|
assert len(resp.data['downloads']) == 1
|
|
assert len(resp.data['positions'][0]['downloads']) == 1
|
|
|
|
order.status = 'n'
|
|
order.save()
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/{}/'.format(organizer.slug, event.slug,
|
|
order.code))
|
|
assert len(resp.data['downloads']) == 0
|
|
assert len(resp.data['positions'][0]['downloads']) == 0
|
|
|
|
event.settings.ticket_download_pending = True
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/{}/'.format(organizer.slug, event.slug,
|
|
order.code))
|
|
assert len(resp.data['downloads']) == 1
|
|
assert len(resp.data['positions'][0]['downloads']) == 1
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_payment_list(token_client, organizer, event, order):
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/{}/payments/'.format(organizer.slug, event.slug,
|
|
order.code))
|
|
assert resp.status_code == 200
|
|
assert TEST_PAYMENTS_RES == resp.data['results']
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_payment_detail(token_client, organizer, event, order):
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/{}/payments/1/'.format(organizer.slug, event.slug,
|
|
order.code))
|
|
assert resp.status_code == 200
|
|
assert TEST_PAYMENTS_RES[0] == resp.data
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_payment_confirm(token_client, organizer, event, order):
|
|
resp = token_client.post('/api/v1/organizers/{}/events/{}/orders/{}/payments/2/confirm/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data={'force': True})
|
|
p = order.payments.get(local_id=2)
|
|
assert resp.status_code == 200
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
|
|
|
|
resp = token_client.post('/api/v1/organizers/{}/events/{}/orders/{}/payments/2/confirm/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data={'force': True})
|
|
assert resp.status_code == 400
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_payment_cancel(token_client, organizer, event, order):
|
|
resp = token_client.post('/api/v1/organizers/{}/events/{}/orders/{}/payments/2/cancel/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
))
|
|
p = order.payments.get(local_id=2)
|
|
assert resp.status_code == 200
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CANCELED
|
|
|
|
resp = token_client.post('/api/v1/organizers/{}/events/{}/orders/{}/payments/2/cancel/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
))
|
|
assert resp.status_code == 400
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_payment_refund_fail(token_client, organizer, event, order, monkeypatch):
|
|
order.payments.last().confirm()
|
|
resp = token_client.post('/api/v1/organizers/{}/events/{}/orders/{}/payments/2/refund/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data={
|
|
'amount': '25.00',
|
|
'mark_refunded': False
|
|
})
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'amount': ['Invalid refund amount, only 23.00 are available to refund.']}
|
|
|
|
resp = token_client.post('/api/v1/organizers/{}/events/{}/orders/{}/payments/2/refund/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data={
|
|
'amount': '20.00',
|
|
'mark_refunded': False
|
|
})
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'amount': ['Partial refund not available for this payment method.']}
|
|
|
|
resp = token_client.post('/api/v1/organizers/{}/events/{}/orders/{}/payments/2/refund/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data={
|
|
'mark_refunded': False
|
|
})
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'amount': ['Full refund not available for this payment method.']}
|
|
|
|
resp = token_client.post('/api/v1/organizers/{}/events/{}/orders/{}/payments/2/refund/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data={
|
|
'amount': '23.00',
|
|
'mark_refunded': False
|
|
})
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'amount': ['Full refund not available for this payment method.']}
|
|
|
|
resp = token_client.post('/api/v1/organizers/{}/events/{}/orders/{}/payments/1/refund/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data={
|
|
'amount': '23.00',
|
|
'mark_refunded': False
|
|
})
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'detail': 'Invalid state of payment.'}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_payment_refund_success(token_client, organizer, event, order, monkeypatch):
|
|
def charge_retr(*args, **kwargs):
|
|
def refund_create(amount):
|
|
r = MockedCharge()
|
|
r.id = 'foo'
|
|
r.status = 'succeeded'
|
|
return r
|
|
|
|
c = MockedCharge()
|
|
c.refunds.create = refund_create
|
|
return c
|
|
|
|
p1 = order.payments.create(
|
|
provider='stripe',
|
|
state='confirmed',
|
|
amount=Decimal('23.00'),
|
|
payment_date=order.datetime,
|
|
info=json.dumps({
|
|
'id': 'ch_123345345'
|
|
})
|
|
)
|
|
monkeypatch.setattr("stripe.Charge.retrieve", charge_retr)
|
|
resp = token_client.post('/api/v1/organizers/{}/events/{}/orders/{}/payments/{}/refund/'.format(
|
|
organizer.slug, event.slug, order.code, p1.local_id
|
|
), format='json', data={
|
|
'amount': '23.00',
|
|
'mark_refunded': False,
|
|
})
|
|
assert resp.status_code == 200
|
|
r = order.refunds.get(local_id=resp.data['local_id'])
|
|
assert r.provider == "stripe"
|
|
assert r.state == OrderRefund.REFUND_STATE_DONE
|
|
assert r.source == OrderRefund.REFUND_SOURCE_ADMIN
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_payment_refund_unavailable(token_client, organizer, event, order, monkeypatch):
|
|
def charge_retr(*args, **kwargs):
|
|
def refund_create(amount):
|
|
raise APIConnectionError(message='Foo')
|
|
|
|
c = MockedCharge()
|
|
c.refunds.create = refund_create
|
|
return c
|
|
|
|
p1 = order.payments.create(
|
|
provider='stripe',
|
|
state='confirmed',
|
|
amount=Decimal('23.00'),
|
|
payment_date=order.datetime,
|
|
info=json.dumps({
|
|
'id': 'ch_123345345'
|
|
})
|
|
)
|
|
monkeypatch.setattr("stripe.Charge.retrieve", charge_retr)
|
|
resp = token_client.post('/api/v1/organizers/{}/events/{}/orders/{}/payments/{}/refund/'.format(
|
|
organizer.slug, event.slug, order.code, p1.local_id
|
|
), format='json', data={
|
|
'amount': '23.00',
|
|
'mark_refunded': False,
|
|
})
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'detail': 'External error: We had trouble communicating with Stripe. Please try again and contact support if the problem persists.'}
|
|
r = order.refunds.last()
|
|
assert r.provider == "stripe"
|
|
assert r.state == OrderRefund.REFUND_STATE_FAILED
|
|
assert r.source == OrderRefund.REFUND_SOURCE_ADMIN
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_list(token_client, organizer, event, order):
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/{}/refunds/'.format(organizer.slug, event.slug,
|
|
order.code))
|
|
assert resp.status_code == 200
|
|
assert TEST_REFUNDS_RES == resp.data['results']
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_detail(token_client, organizer, event, order):
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/{}/refunds/1/'.format(organizer.slug, event.slug,
|
|
order.code))
|
|
assert resp.status_code == 200
|
|
assert TEST_REFUNDS_RES[0] == resp.data
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_done(token_client, organizer, event, order):
|
|
r = order.refunds.get(local_id=1)
|
|
r.state = 'transit'
|
|
r.save()
|
|
resp = token_client.post('/api/v1/organizers/{}/events/{}/orders/{}/refunds/1/done/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
))
|
|
r = order.refunds.get(local_id=1)
|
|
assert resp.status_code == 200
|
|
assert r.state == OrderRefund.REFUND_STATE_DONE
|
|
|
|
resp = token_client.post('/api/v1/organizers/{}/events/{}/orders/{}/refunds/1/done/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
))
|
|
assert resp.status_code == 400
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_process_mark_refunded(token_client, organizer, event, order):
|
|
p = order.payments.get(local_id=1)
|
|
p.create_external_refund()
|
|
resp = token_client.post('/api/v1/organizers/{}/events/{}/orders/{}/refunds/2/process/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data={'mark_refunded': True})
|
|
r = order.refunds.get(local_id=1)
|
|
assert resp.status_code == 200
|
|
assert r.state == OrderRefund.REFUND_STATE_DONE
|
|
order.refresh_from_db()
|
|
assert order.status == Order.STATUS_REFUNDED
|
|
|
|
resp = token_client.post('/api/v1/organizers/{}/events/{}/orders/{}/refunds/2/process/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data={'mark_refunded': True})
|
|
assert resp.status_code == 400
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_process_mark_pending(token_client, organizer, event, order):
|
|
p = order.payments.get(local_id=1)
|
|
p.create_external_refund()
|
|
resp = token_client.post('/api/v1/organizers/{}/events/{}/orders/{}/refunds/2/process/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data={'mark_refunded': False})
|
|
r = order.refunds.get(local_id=1)
|
|
assert resp.status_code == 200
|
|
assert r.state == OrderRefund.REFUND_STATE_DONE
|
|
order.refresh_from_db()
|
|
assert order.status == Order.STATUS_PENDING
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_cancel(token_client, organizer, event, order):
|
|
r = order.refunds.get(local_id=1)
|
|
r.state = 'transit'
|
|
r.save()
|
|
resp = token_client.post('/api/v1/organizers/{}/events/{}/orders/{}/refunds/1/cancel/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
))
|
|
r = order.refunds.get(local_id=1)
|
|
assert resp.status_code == 200
|
|
assert r.state == OrderRefund.REFUND_STATE_CANCELED
|
|
|
|
resp = token_client.post('/api/v1/organizers/{}/events/{}/orders/{}/refunds/1/cancel/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
))
|
|
assert resp.status_code == 400
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_orderposition_list(token_client, organizer, event, order, item, subevent, subevent2, question):
|
|
i2 = copy.copy(item)
|
|
i2.pk = None
|
|
i2.save()
|
|
var = item.variations.create(value="Children")
|
|
var2 = item.variations.create(value="Children")
|
|
res = dict(TEST_ORDERPOSITION_RES)
|
|
op = order.positions.first()
|
|
op.variation = var
|
|
op.save()
|
|
res["id"] = op.pk
|
|
res["item"] = item.pk
|
|
res["variation"] = var.pk
|
|
res["answers"][0]["question"] = question.pk
|
|
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orderpositions/'.format(organizer.slug, event.slug))
|
|
assert resp.status_code == 200
|
|
assert [res] == resp.data['results']
|
|
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?order__status=n'.format(organizer.slug, event.slug))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?order__status=p'.format(organizer.slug, event.slug))
|
|
assert [] == resp.data['results']
|
|
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?item={}'.format(organizer.slug, event.slug, item.pk))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?item__in={},{}'.format(
|
|
organizer.slug, event.slug, item.pk, i2.pk
|
|
))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?item={}'.format(organizer.slug, event.slug, i2.pk))
|
|
assert [] == resp.data['results']
|
|
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?variation={}'.format(organizer.slug, event.slug, var.pk))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?variation={}'.format(organizer.slug, event.slug, var2.pk))
|
|
assert [] == resp.data['results']
|
|
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?attendee_name=Peter'.format(organizer.slug, event.slug))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?attendee_name=peter'.format(organizer.slug, event.slug))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?attendee_name=Mark'.format(organizer.slug, event.slug))
|
|
assert [] == resp.data['results']
|
|
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?secret=z3fsn8jyufm5kpk768q69gkbyr5f4h6w'.format(
|
|
organizer.slug, event.slug))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?secret=abc123'.format(organizer.slug, event.slug))
|
|
assert [] == resp.data['results']
|
|
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?pseudonymization_id=ABCDEFGHKL'.format(
|
|
organizer.slug, event.slug))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?pseudonymization_id=FOO'.format(organizer.slug, event.slug))
|
|
assert [] == resp.data['results']
|
|
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?search=FO'.format(organizer.slug, event.slug))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?search=z3fsn8j'.format(organizer.slug, event.slug))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?search=Peter'.format(organizer.slug, event.slug))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?search=5f4h6w'.format(organizer.slug, event.slug))
|
|
assert [] == resp.data['results']
|
|
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?order=FOO'.format(organizer.slug, event.slug))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?order=BAR'.format(organizer.slug, event.slug))
|
|
assert [] == resp.data['results']
|
|
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?has_checkin=false'.format(organizer.slug, event.slug))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?has_checkin=true'.format(organizer.slug, event.slug))
|
|
assert [] == resp.data['results']
|
|
|
|
cl = event.checkin_lists.create(name="Default")
|
|
op.checkins.create(datetime=datetime.datetime(2017, 12, 26, 10, 0, 0, tzinfo=UTC), list=cl)
|
|
res['checkins'] = [{'datetime': '2017-12-26T10:00:00Z', 'list': cl.pk}]
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?has_checkin=true'.format(organizer.slug, event.slug))
|
|
assert [res] == resp.data['results']
|
|
|
|
op.subevent = subevent
|
|
op.save()
|
|
res['subevent'] = subevent.pk
|
|
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?subevent={}'.format(organizer.slug, event.slug, subevent.pk))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?subevent__in={},{}'.format(organizer.slug, event.slug,
|
|
subevent.pk, subevent2.pk))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get(
|
|
'/api/v1/organizers/{}/events/{}/orderpositions/?subevent={}'.format(organizer.slug, event.slug,
|
|
subevent.pk + 1))
|
|
assert [] == resp.data['results']
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_orderposition_detail(token_client, organizer, event, order, item, question):
|
|
res = dict(TEST_ORDERPOSITION_RES)
|
|
op = order.positions.first()
|
|
res["id"] = op.pk
|
|
res["item"] = item.pk
|
|
res["answers"][0]["question"] = question.pk
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orderpositions/{}/'.format(organizer.slug, event.slug,
|
|
op.pk))
|
|
assert resp.status_code == 200
|
|
assert res == resp.data
|
|
|
|
order.status = 'p'
|
|
order.save()
|
|
event.settings.ticketoutput_pdf__enabled = True
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/orderpositions/{}/'.format(organizer.slug, event.slug,
|
|
op.pk))
|
|
assert len(resp.data['downloads']) == 1
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_orderposition_delete(token_client, organizer, event, order, item, question):
|
|
op = order.positions.first()
|
|
resp = token_client.delete('/api/v1/organizers/{}/events/{}/orderpositions/{}/'.format(
|
|
organizer.slug, event.slug, op.pk
|
|
))
|
|
assert resp.status_code == 400
|
|
assert resp.data == ['This operation would leave the order empty. Please cancel the order itself instead.']
|
|
|
|
op2 = OrderPosition.objects.create(
|
|
order=order,
|
|
item=item,
|
|
variation=None,
|
|
price=Decimal("23"),
|
|
attendee_name_parts={"full_name": "Peter", "_scheme": "full"},
|
|
secret="foobar",
|
|
pseudonymization_id="BAZ",
|
|
)
|
|
order.refresh_from_db()
|
|
order.total = Decimal('46')
|
|
order.save()
|
|
assert order.positions.count() == 2
|
|
|
|
resp = token_client.delete('/api/v1/organizers/{}/events/{}/orderpositions/{}/'.format(
|
|
organizer.slug, event.slug, op2.pk
|
|
))
|
|
assert resp.status_code == 204
|
|
assert order.positions.count() == 1
|
|
order.refresh_from_db()
|
|
assert order.total == Decimal('23.25')
|
|
|
|
|
|
@pytest.fixture
|
|
def invoice(order):
|
|
testtime = datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=UTC)
|
|
|
|
with mock.patch('django.utils.timezone.now') as mock_now:
|
|
mock_now.return_value = testtime
|
|
return generate_invoice(order)
|
|
|
|
|
|
TEST_INVOICE_RES = {
|
|
"order": "FOO",
|
|
"number": "DUMMY-00001",
|
|
"is_cancellation": False,
|
|
"invoice_from": "",
|
|
"invoice_to": "Sample company\n\n\n \nNew Zealand",
|
|
"date": "2017-12-10",
|
|
"refers": None,
|
|
"locale": "en",
|
|
"introductory_text": "",
|
|
"internal_reference": "",
|
|
"additional_text": "",
|
|
"payment_provider_text": "",
|
|
"footer_text": "",
|
|
"foreign_currency_display": None,
|
|
"foreign_currency_rate": None,
|
|
"foreign_currency_rate_date": None,
|
|
"lines": [
|
|
{
|
|
"description": "Budget Ticket<br />Attendee: Peter",
|
|
"gross_value": "23.00",
|
|
"tax_value": "0.00",
|
|
"tax_name": "",
|
|
"tax_rate": "0.00"
|
|
},
|
|
{
|
|
"description": "Payment fee",
|
|
"gross_value": "0.25",
|
|
"tax_value": "0.05",
|
|
"tax_name": "",
|
|
"tax_rate": "19.00"
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_invoice_list(token_client, organizer, event, order, invoice):
|
|
res = dict(TEST_INVOICE_RES)
|
|
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/'.format(organizer.slug, event.slug))
|
|
assert resp.status_code == 200
|
|
assert [res] == resp.data['results']
|
|
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/?order=FOO'.format(organizer.slug, event.slug))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/?order=BAR'.format(organizer.slug, event.slug))
|
|
assert [] == resp.data['results']
|
|
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/?number={}'.format(
|
|
organizer.slug, event.slug, invoice.number))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/?number=XXX'.format(
|
|
organizer.slug, event.slug))
|
|
assert [] == resp.data['results']
|
|
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/?locale=en'.format(
|
|
organizer.slug, event.slug))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/?locale=de'.format(
|
|
organizer.slug, event.slug))
|
|
assert [] == resp.data['results']
|
|
|
|
ic = generate_cancellation(invoice)
|
|
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/?is_cancellation=false'.format(
|
|
organizer.slug, event.slug))
|
|
assert [res] == resp.data['results']
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/?is_cancellation=true'.format(
|
|
organizer.slug, event.slug))
|
|
assert len(resp.data['results']) == 1
|
|
assert resp.data['results'][0]['number'] == ic.number
|
|
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/?refers={}'.format(
|
|
organizer.slug, event.slug, invoice.number))
|
|
assert len(resp.data['results']) == 1
|
|
assert resp.data['results'][0]['number'] == ic.number
|
|
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/?refers={}'.format(
|
|
organizer.slug, event.slug, ic.number))
|
|
assert [] == resp.data['results']
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_invoice_detail(token_client, organizer, event, invoice):
|
|
res = dict(TEST_INVOICE_RES)
|
|
|
|
resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/{}/'.format(organizer.slug, event.slug,
|
|
invoice.number))
|
|
assert resp.status_code == 200
|
|
assert res == resp.data
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_invoice_regenerate(token_client, organizer, event, invoice):
|
|
InvoiceAddress.objects.filter(order=invoice.order).update(company="ACME Ltd")
|
|
|
|
resp = token_client.post('/api/v1/organizers/{}/events/{}/invoices/{}/regenerate/'.format(
|
|
organizer.slug, event.slug, invoice.number
|
|
))
|
|
assert resp.status_code == 204
|
|
invoice.refresh_from_db()
|
|
assert "ACME Ltd" in invoice.invoice_to
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_invoice_reissue(token_client, organizer, event, invoice):
|
|
InvoiceAddress.objects.filter(order=invoice.order).update(company="ACME Ltd")
|
|
|
|
resp = token_client.post('/api/v1/organizers/{}/events/{}/invoices/{}/reissue/'.format(
|
|
organizer.slug, event.slug, invoice.number
|
|
))
|
|
assert resp.status_code == 204
|
|
invoice.refresh_from_db()
|
|
assert "ACME Ltd" not in invoice.invoice_to
|
|
assert invoice.order.invoices.count() == 3
|
|
invoice = invoice.order.invoices.last()
|
|
assert "ACME Ltd" in invoice.invoice_to
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_mark_paid_pending(token_client, organizer, event, order):
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/mark_paid/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
)
|
|
)
|
|
assert resp.status_code == 200
|
|
assert resp.data['status'] == Order.STATUS_PAID
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_mark_paid_canceled(token_client, organizer, event, order):
|
|
order.status = Order.STATUS_CANCELED
|
|
order.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/mark_paid/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
)
|
|
)
|
|
assert resp.status_code == 400
|
|
order.refresh_from_db()
|
|
assert order.status == Order.STATUS_CANCELED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_mark_paid_expired_quota_free(token_client, organizer, event, order, quota):
|
|
order.status = Order.STATUS_EXPIRED
|
|
order.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/mark_paid/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
)
|
|
)
|
|
assert resp.status_code == 200
|
|
order.refresh_from_db()
|
|
assert order.status == Order.STATUS_PAID
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_mark_paid_expired_quota_fill(token_client, organizer, event, order, quota):
|
|
order.status = Order.STATUS_EXPIRED
|
|
order.save()
|
|
quota.size = 0
|
|
quota.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/mark_paid/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
)
|
|
)
|
|
assert resp.status_code == 400
|
|
order.refresh_from_db()
|
|
assert order.status == Order.STATUS_EXPIRED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_mark_paid_locked(token_client, organizer, event, order):
|
|
order.status = Order.STATUS_EXPIRED
|
|
order.save()
|
|
with event.lock():
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/mark_paid/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
)
|
|
)
|
|
assert resp.status_code == 409
|
|
order.refresh_from_db()
|
|
assert order.status == Order.STATUS_EXPIRED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_mark_canceled_pending(token_client, organizer, event, order):
|
|
djmail.outbox = []
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/mark_canceled/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
)
|
|
)
|
|
assert resp.status_code == 200
|
|
assert resp.data['status'] == Order.STATUS_CANCELED
|
|
assert len(djmail.outbox) == 1
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_mark_canceled_pending_no_email(token_client, organizer, event, order):
|
|
djmail.outbox = []
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/mark_canceled/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data={
|
|
'send_email': False
|
|
}
|
|
)
|
|
assert resp.status_code == 200
|
|
assert resp.data['status'] == Order.STATUS_CANCELED
|
|
assert len(djmail.outbox) == 0
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_mark_canceled_paid(token_client, organizer, event, order):
|
|
order.status = Order.STATUS_PAID
|
|
order.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/mark_canceled/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
)
|
|
)
|
|
assert resp.status_code == 400
|
|
order.refresh_from_db()
|
|
assert order.status == Order.STATUS_PAID
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_mark_paid_refunded(token_client, organizer, event, order):
|
|
order.status = Order.STATUS_PAID
|
|
order.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/mark_refunded/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
)
|
|
)
|
|
assert resp.status_code == 200
|
|
assert resp.data['status'] == Order.STATUS_REFUNDED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_mark_canceled_refunded(token_client, organizer, event, order):
|
|
order.status = Order.STATUS_CANCELED
|
|
order.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/mark_refunded/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
)
|
|
)
|
|
assert resp.status_code == 400
|
|
order.refresh_from_db()
|
|
assert order.status == Order.STATUS_CANCELED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_mark_paid_unpaid(token_client, organizer, event, order):
|
|
order.status = Order.STATUS_PAID
|
|
order.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/mark_pending/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
)
|
|
)
|
|
assert resp.status_code == 200
|
|
assert resp.data['status'] == Order.STATUS_PENDING
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_mark_canceled_unpaid(token_client, organizer, event, order):
|
|
order.status = Order.STATUS_CANCELED
|
|
order.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/mark_pending/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
)
|
|
)
|
|
assert resp.status_code == 400
|
|
order.refresh_from_db()
|
|
assert order.status == Order.STATUS_CANCELED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_mark_pending_expired(token_client, organizer, event, order):
|
|
order.status = Order.STATUS_PENDING
|
|
order.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/mark_expired/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
)
|
|
)
|
|
assert resp.status_code == 200
|
|
assert resp.data['status'] == Order.STATUS_EXPIRED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_mark_paid_expired(token_client, organizer, event, order):
|
|
order.status = Order.STATUS_PAID
|
|
order.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/mark_expired/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
)
|
|
)
|
|
assert resp.status_code == 400
|
|
order.refresh_from_db()
|
|
assert order.status == Order.STATUS_PAID
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_extend_paid(token_client, organizer, event, order):
|
|
order.status = Order.STATUS_PAID
|
|
order.save()
|
|
newdate = (now() + datetime.timedelta(days=20)).strftime("%Y-%m-%d")
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/extend/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data={
|
|
'expires': newdate
|
|
}
|
|
)
|
|
assert resp.status_code == 400
|
|
order.refresh_from_db()
|
|
assert order.status == Order.STATUS_PAID
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_extend_pending(token_client, organizer, event, order):
|
|
order.status = Order.STATUS_PENDING
|
|
order.save()
|
|
newdate = (now() + datetime.timedelta(days=20)).strftime("%Y-%m-%d")
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/extend/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data={
|
|
'expires': newdate
|
|
}
|
|
)
|
|
assert resp.status_code == 200
|
|
order.refresh_from_db()
|
|
assert order.status == Order.STATUS_PENDING
|
|
assert order.expires.strftime("%Y-%m-%d %H:%M:%S") == newdate[:10] + " 23:59:59"
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_extend_expired_quota_empty(token_client, organizer, event, order, quota):
|
|
order.status = Order.STATUS_EXPIRED
|
|
order.save()
|
|
quota.size = 0
|
|
quota.save()
|
|
newdate = (now() + datetime.timedelta(days=20)).strftime("%Y-%m-%d")
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/extend/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data={
|
|
'expires': newdate
|
|
}
|
|
)
|
|
assert resp.status_code == 400
|
|
order.refresh_from_db()
|
|
assert order.status == Order.STATUS_EXPIRED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_extend_expired_quota_ignore(token_client, organizer, event, order, quota):
|
|
order.status = Order.STATUS_EXPIRED
|
|
order.save()
|
|
quota.size = 0
|
|
quota.save()
|
|
newdate = (now() + datetime.timedelta(days=20)).strftime("%Y-%m-%d")
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/extend/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data={
|
|
'expires': newdate,
|
|
'force': True
|
|
}
|
|
)
|
|
assert resp.status_code == 200
|
|
order.refresh_from_db()
|
|
assert order.status == Order.STATUS_PENDING
|
|
assert order.expires.strftime("%Y-%m-%d %H:%M:%S") == newdate[:10] + " 23:59:59"
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_extend_expired_quota_waiting_list(token_client, organizer, event, order, item, quota):
|
|
order.status = Order.STATUS_EXPIRED
|
|
order.save()
|
|
quota.size = 1
|
|
quota.save()
|
|
event.waitinglistentries.create(item=item, email='foo@bar.com')
|
|
newdate = (now() + datetime.timedelta(days=20)).strftime("%Y-%m-%d")
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/extend/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data={
|
|
'expires': newdate,
|
|
}
|
|
)
|
|
assert resp.status_code == 200
|
|
order.refresh_from_db()
|
|
assert order.status == Order.STATUS_PENDING
|
|
assert order.expires.strftime("%Y-%m-%d %H:%M:%S") == newdate[:10] + " 23:59:59"
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_extend_expired_quota_left(token_client, organizer, event, order, quota):
|
|
order.status = Order.STATUS_EXPIRED
|
|
order.save()
|
|
quota.size = 2
|
|
quota.save()
|
|
newdate = (now() + datetime.timedelta(days=20)).strftime("%Y-%m-%d")
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/extend/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data={
|
|
'expires': newdate,
|
|
}
|
|
)
|
|
assert resp.status_code == 200
|
|
order.refresh_from_db()
|
|
assert order.status == Order.STATUS_PENDING
|
|
assert order.expires.strftime("%Y-%m-%d %H:%M:%S") == newdate[:10] + " 23:59:59"
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_pending_approve(token_client, organizer, event, order):
|
|
order.require_approval = True
|
|
order.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/approve/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
)
|
|
)
|
|
assert resp.status_code == 200
|
|
assert resp.data['status'] == Order.STATUS_PENDING
|
|
assert not resp.data['require_approval']
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_invalid_state_approve(token_client, organizer, event, order):
|
|
order.require_approval = True
|
|
order.status = Order.STATUS_CANCELED
|
|
order.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/approve/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
)
|
|
)
|
|
assert resp.status_code == 400
|
|
|
|
order.require_approval = False
|
|
order.status = Order.STATUS_PENDING
|
|
order.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/approve/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
)
|
|
)
|
|
assert resp.status_code == 400
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_pending_deny(token_client, organizer, event, order):
|
|
order.require_approval = True
|
|
order.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/deny/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
)
|
|
)
|
|
assert resp.status_code == 200
|
|
assert resp.data['status'] == Order.STATUS_CANCELED
|
|
assert resp.data['require_approval']
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_invalid_state_deny(token_client, organizer, event, order):
|
|
order.require_approval = True
|
|
order.status = Order.STATUS_CANCELED
|
|
order.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/deny/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
)
|
|
)
|
|
assert resp.status_code == 400
|
|
|
|
order.require_approval = False
|
|
order.status = Order.STATUS_PENDING
|
|
order.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/deny/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
)
|
|
)
|
|
assert resp.status_code == 400
|
|
|
|
|
|
ORDER_CREATE_PAYLOAD = {
|
|
"email": "dummy@dummy.test",
|
|
"locale": "en",
|
|
"fees": [
|
|
{
|
|
"fee_type": "payment",
|
|
"value": "0.25",
|
|
"description": "",
|
|
"internal_type": "",
|
|
"tax_rule": None
|
|
}
|
|
],
|
|
"payment_provider": "banktransfer",
|
|
"invoice_address": {
|
|
"is_business": False,
|
|
"company": "Sample company",
|
|
"name_parts": {"full_name": "Fo"},
|
|
"street": "Bar",
|
|
"zipcode": "",
|
|
"city": "Sample City",
|
|
"country": "NZ",
|
|
"internal_reference": "",
|
|
"vat_id": ""
|
|
},
|
|
"positions": [
|
|
{
|
|
"positionid": 1,
|
|
"item": 1,
|
|
"variation": None,
|
|
"price": "23.00",
|
|
"attendee_name_parts": {"full_name": "Peter"},
|
|
"attendee_email": None,
|
|
"addon_to": None,
|
|
"answers": [
|
|
{
|
|
"question": 1,
|
|
"answer": "S",
|
|
"options": []
|
|
}
|
|
],
|
|
"subevent": None
|
|
}
|
|
],
|
|
}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert o.email == "dummy@dummy.test"
|
|
assert o.locale == "en"
|
|
assert o.total == Decimal('23.25')
|
|
assert o.status == Order.STATUS_PENDING
|
|
|
|
p = o.payments.first()
|
|
assert p.provider == "banktransfer"
|
|
assert p.amount == o.total
|
|
assert p.state == "created"
|
|
|
|
fee = o.fees.first()
|
|
assert fee.fee_type == "payment"
|
|
assert fee.value == Decimal('0.25')
|
|
ia = o.invoice_address
|
|
assert ia.company == "Sample company"
|
|
assert ia.name_parts == {"full_name": "Fo", "_scheme": "full"}
|
|
assert ia.name_cached == "Fo"
|
|
assert o.positions.count() == 1
|
|
pos = o.positions.first()
|
|
assert pos.item == item
|
|
assert pos.price == Decimal("23.00")
|
|
assert pos.attendee_name_parts == {"full_name": "Peter", "_scheme": "full"}
|
|
answ = pos.answers.first()
|
|
assert answ.question == question
|
|
assert answ.answer == "S"
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_invoice_address_optional(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
del res['invoice_address']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
with pytest.raises(InvoiceAddress.DoesNotExist):
|
|
o.invoice_address
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_attendee_name_optional(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['attendee_name'] = None
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
del res['positions'][0]['attendee_name_parts']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert o.positions.first().attendee_name_parts == {}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_legacy_attendee_name(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['attendee_name'] = 'Peter'
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
del res['positions'][0]['attendee_name_parts']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert o.positions.first().attendee_name_parts == {"_legacy": "Peter"}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_legacy_invoice_name(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['invoice_address']['name'] = 'Peter'
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
del res['invoice_address']['name_parts']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert o.invoice_address.name_parts == {"_legacy": "Peter"}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_code_optional(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['code'] = 'ABCDE'
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert o.code == "ABCDE"
|
|
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'code': ['This order code is already in use.']}
|
|
|
|
res['code'] = 'ABaDE'
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'code': ['This order code contains invalid characters.']}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_email_optional(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
del res['email']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert not o.email
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_payment_info_optional(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
|
|
res['payment_info'] = {
|
|
'foo': {
|
|
'bar': [1, 2],
|
|
'test': False
|
|
}
|
|
}
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
|
|
p = o.payments.first()
|
|
assert p.provider == "banktransfer"
|
|
assert p.amount == o.total
|
|
assert json.loads(p.info) == res['payment_info']
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_position_secret_optional(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert o.positions.first().secret
|
|
|
|
res['positions'][0]['secret'] = "aaa"
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert o.positions.first().secret == "aaa"
|
|
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
|
|
assert resp.data == {'positions': [{'secret': ['You cannot assign a position secret that already exists.']}]}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_tax_rules(token_client, organizer, event, item, quota, question, taxrule):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['fees'][0]['tax_rule'] = taxrule.pk
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
item.tax_rule = taxrule
|
|
item.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
fee = o.fees.first()
|
|
assert fee.fee_type == "payment"
|
|
assert fee.value == Decimal('0.25')
|
|
assert fee.tax_rate == Decimal('19.00')
|
|
assert fee.tax_rule == taxrule
|
|
ia = o.invoice_address
|
|
assert ia.company == "Sample company"
|
|
pos = o.positions.first()
|
|
assert pos.item == item
|
|
assert pos.tax_rate == Decimal('19.00')
|
|
assert pos.tax_value == Decimal('3.67')
|
|
assert pos.tax_rule == taxrule
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_fee_type_validation(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['fees'][0]['fee_type'] = 'unknown'
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'fees': [{'fee_type': ['"unknown" is not a valid choice.']}]}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_tax_rule_wrong_event(token_client, organizer, event, item, quota, question, taxrule2):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['fees'][0]['tax_rule'] = taxrule2.pk
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'fees': [{'tax_rule': ['The specified tax rate does not belong to this event.']}]}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_subevent_not_allowed(token_client, organizer, event, item, quota, question, subevent2):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['positions'][0]['subevent'] = subevent2.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'positions': [{'subevent': ['You cannot set a subevent for this event.']}]}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_empty(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'] = []
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'positions': ['An order cannot be empty.']}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_subevent_validation(token_client, organizer, event, item, subevent, subevent2, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'positions': [{'subevent': ['You need to set a subevent.']}]}
|
|
|
|
res['positions'][0]['subevent'] = subevent2.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'positions': [{'subevent': ['The specified subevent does not belong to this event.']}]}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_item_validation(token_client, organizer, event, item, item2, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
item.active = False
|
|
item.save()
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'positions': [{'item': ['The specified item is not active.']}]}
|
|
item.active = True
|
|
item.save()
|
|
|
|
res['positions'][0]['item'] = item2.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'positions': [{'item': ['The specified item does not belong to this event.']}]}
|
|
|
|
var2 = item2.variations.create(value="A")
|
|
quota.variations.add(var2)
|
|
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['variation'] = var2.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'positions': [{'variation': ['You cannot specify a variation for this item.']}]}
|
|
|
|
var1 = item.variations.create(value="A")
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['variation'] = var1.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'positions': [{'item': ['The product "Budget Ticket" is not assigned to a quota.']}]}
|
|
|
|
quota.variations.add(var1)
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
|
|
res['positions'][0]['variation'] = var2.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {
|
|
'positions': [{'variation': ['The specified variation does not belong to the specified item.']}]}
|
|
|
|
res['positions'][0]['variation'] = None
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'positions': [{'variation': ['You should specify a variation for this item.']}]}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_positionids_addons(token_client, organizer, event, item, quota):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'] = [
|
|
{
|
|
"positionid": 1,
|
|
"item": item.pk,
|
|
"variation": None,
|
|
"price": "23.00",
|
|
"attendee_name_parts": {"full_name": "Peter"},
|
|
"attendee_email": None,
|
|
"addon_to": None,
|
|
"answers": [],
|
|
"subevent": None
|
|
},
|
|
{
|
|
"positionid": 2,
|
|
"item": item.pk,
|
|
"variation": None,
|
|
"price": "23.00",
|
|
"attendee_name_parts": {"full_name": "Peter"},
|
|
"attendee_email": None,
|
|
"addon_to": 1,
|
|
"answers": [],
|
|
"subevent": None
|
|
}
|
|
]
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
pos1 = o.positions.first()
|
|
pos2 = o.positions.last()
|
|
assert pos2.addon_to == pos1
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_positionid_validation(token_client, organizer, event, item, quota):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'] = [
|
|
{
|
|
"positionid": 1,
|
|
"item": item.pk,
|
|
"variation": None,
|
|
"price": "23.00",
|
|
"attendee_name_parts": {"full_name": "Peter"},
|
|
"attendee_email": None,
|
|
"addon_to": None,
|
|
"answers": [],
|
|
"subevent": None
|
|
},
|
|
{
|
|
"positionid": 2,
|
|
"item": item.pk,
|
|
"variation": None,
|
|
"price": "23.00",
|
|
"attendee_name_parts": {"full_name": "Peter"},
|
|
"attendee_email": None,
|
|
"addon_to": 2,
|
|
"answers": [],
|
|
"subevent": None
|
|
}
|
|
]
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {
|
|
'positions': [
|
|
{},
|
|
{
|
|
'addon_to': [
|
|
'If you set addon_to, you need to make sure that the '
|
|
'referenced position ID exists and is transmitted directly '
|
|
'before its add-ons.'
|
|
]
|
|
}
|
|
]
|
|
}
|
|
|
|
res['positions'] = [
|
|
{
|
|
"item": item.pk,
|
|
"variation": None,
|
|
"price": "23.00",
|
|
"attendee_name_parts": {"full_name": "Peter"},
|
|
"attendee_email": None,
|
|
"addon_to": None,
|
|
"answers": [],
|
|
"subevent": None
|
|
},
|
|
{
|
|
"item": item.pk,
|
|
"variation": None,
|
|
"price": "23.00",
|
|
"attendee_name_parts": {"full_name": "Peter"},
|
|
"attendee_email": None,
|
|
"addon_to": 2,
|
|
"answers": [],
|
|
"subevent": None
|
|
}
|
|
]
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'positions': [
|
|
{'positionid': ["If you set addon_to on any position, you need to specify position IDs manually."]},
|
|
{'positionid': ["If you set addon_to on any position, you need to specify position IDs manually."]}
|
|
]}
|
|
|
|
res['positions'] = [
|
|
{
|
|
"positionid": 1,
|
|
"item": item.pk,
|
|
"variation": None,
|
|
"price": "23.00",
|
|
"attendee_name_parts": {"full_name": "Peter"},
|
|
"attendee_email": None,
|
|
"answers": [],
|
|
"subevent": None
|
|
},
|
|
{
|
|
"item": item.pk,
|
|
"variation": None,
|
|
"price": "23.00",
|
|
"attendee_name_parts": {"full_name": "Peter"},
|
|
"attendee_email": None,
|
|
"answers": [],
|
|
"subevent": None
|
|
}
|
|
]
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {
|
|
'positions': [
|
|
{},
|
|
{
|
|
'positionid': ['If you set position IDs manually, you need to do so for all positions.']
|
|
}
|
|
]
|
|
}
|
|
|
|
res['positions'] = [
|
|
{
|
|
"positionid": 1,
|
|
"item": item.pk,
|
|
"variation": None,
|
|
"price": "23.00",
|
|
"attendee_name_parts": {"full_name": "Peter"},
|
|
"attendee_email": None,
|
|
"answers": [],
|
|
"subevent": None
|
|
},
|
|
{
|
|
"positionid": 3,
|
|
"item": item.pk,
|
|
"variation": None,
|
|
"price": "23.00",
|
|
"attendee_name_parts": {"full_name": "Peter"},
|
|
"attendee_email": None,
|
|
"answers": [],
|
|
"subevent": None
|
|
}
|
|
]
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {
|
|
'positions': [
|
|
{},
|
|
{
|
|
'positionid': ['Position IDs need to be consecutive.']
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_answer_validation(token_client, organizer, event, item, quota, question, question2):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question2.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {
|
|
'positions': [{'answers': [{'question': ['The specified question does not belong to this event.']}]}]}
|
|
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['positions'][0]['answers'][0]['options'] = [question.options.first().pk]
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'positions': [{'answers': [
|
|
{'non_field_errors': ['You should not specify options if the question is not of a choice type.']}]}]}
|
|
|
|
question.type = Question.TYPE_CHOICE
|
|
question.save()
|
|
res['positions'][0]['answers'][0]['options'] = []
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'positions': [
|
|
{'answers': [{'non_field_errors': ['You need to specify options if the question is of a choice type.']}]}]}
|
|
|
|
question.options.create(answer="L")
|
|
res['positions'][0]['answers'][0]['options'] = [
|
|
question.options.first().pk,
|
|
question.options.last().pk,
|
|
]
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {
|
|
'positions': [{'answers': [{'non_field_errors': ['You can specify at most one option for this question.']}]}]}
|
|
|
|
question.type = Question.TYPE_FILE
|
|
question.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {
|
|
'positions': [{'answers': [{'non_field_errors': ['File uploads are currently not supported via the API.']}]}]}
|
|
|
|
question.type = Question.TYPE_CHOICE_MULTIPLE
|
|
question.save()
|
|
res['positions'][0]['answers'][0]['options'] = [
|
|
question.options.first().pk,
|
|
question.options.last().pk,
|
|
]
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
pos = o.positions.first()
|
|
answ = pos.answers.first()
|
|
assert answ.question == question
|
|
assert answ.answer == "XL, L"
|
|
|
|
question.type = Question.TYPE_NUMBER
|
|
question.save()
|
|
res['positions'][0]['answers'][0]['options'] = []
|
|
res['positions'][0]['answers'][0]['answer'] = '3.45'
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
pos = o.positions.first()
|
|
answ = pos.answers.first()
|
|
assert answ.answer == "3.45"
|
|
|
|
question.type = Question.TYPE_NUMBER
|
|
question.save()
|
|
res['positions'][0]['answers'][0]['options'] = []
|
|
res['positions'][0]['answers'][0]['answer'] = 'foo'
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'positions': [{'answers': [{'non_field_errors': ['A valid number is required.']}]}]}
|
|
|
|
question.type = Question.TYPE_BOOLEAN
|
|
question.save()
|
|
res['positions'][0]['answers'][0]['options'] = []
|
|
res['positions'][0]['answers'][0]['answer'] = 'True'
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
pos = o.positions.first()
|
|
answ = pos.answers.first()
|
|
assert answ.answer == "True"
|
|
|
|
question.type = Question.TYPE_BOOLEAN
|
|
question.save()
|
|
res['positions'][0]['answers'][0]['answer'] = '0'
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
pos = o.positions.first()
|
|
answ = pos.answers.first()
|
|
assert answ.answer == "False"
|
|
|
|
question.type = Question.TYPE_BOOLEAN
|
|
question.save()
|
|
res['positions'][0]['answers'][0]['answer'] = 'bla'
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {
|
|
'positions': [{'answers': [{'non_field_errors': ['Please specify "true" or "false" for boolean questions.']}]}]}
|
|
|
|
question.type = Question.TYPE_DATE
|
|
question.save()
|
|
res['positions'][0]['answers'][0]['answer'] = '2018-05-14'
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
pos = o.positions.first()
|
|
answ = pos.answers.first()
|
|
assert answ.answer == "2018-05-14"
|
|
|
|
question.type = Question.TYPE_DATE
|
|
question.save()
|
|
res['positions'][0]['answers'][0]['answer'] = 'bla'
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'positions': [{'answers': [
|
|
{'non_field_errors': ['Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]].']}]}]}
|
|
|
|
question.type = Question.TYPE_DATETIME
|
|
question.save()
|
|
res['positions'][0]['answers'][0]['answer'] = '2018-05-14T13:00:00Z'
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
pos = o.positions.first()
|
|
answ = pos.answers.first()
|
|
assert answ.answer == "2018-05-14 13:00:00+00:00"
|
|
|
|
question.type = Question.TYPE_DATETIME
|
|
question.save()
|
|
res['positions'][0]['answers'][0]['answer'] = 'bla'
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'positions': [{'answers': [{'non_field_errors': [
|
|
'Datetime has wrong format. Use one of these formats instead: '
|
|
'YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z].']}]}]}
|
|
|
|
question.type = Question.TYPE_TIME
|
|
question.save()
|
|
res['positions'][0]['answers'][0]['answer'] = '13:00:00'
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
pos = o.positions.first()
|
|
answ = pos.answers.first()
|
|
assert answ.answer == "13:00:00"
|
|
|
|
question.type = Question.TYPE_TIME
|
|
question.save()
|
|
res['positions'][0]['answers'][0]['answer'] = 'bla'
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'positions': [{'answers': [
|
|
{'non_field_errors': ['Time has wrong format. Use one of these formats instead: hh:mm[:ss[.uuuuuu]].']}]}]}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_quota_validation(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'] = [
|
|
{
|
|
"positionid": 1,
|
|
"item": item.pk,
|
|
"variation": None,
|
|
"price": "23.00",
|
|
"attendee_name_parts": {"full_name": "Peter"},
|
|
"attendee_email": None,
|
|
"addon_to": None,
|
|
"answers": [],
|
|
"subevent": None
|
|
},
|
|
{
|
|
"positionid": 2,
|
|
"item": item.pk,
|
|
"variation": None,
|
|
"price": "23.00",
|
|
"attendee_name_parts": {"full_name": "Peter"},
|
|
"attendee_email": None,
|
|
"addon_to": 1,
|
|
"answers": [],
|
|
"subevent": None
|
|
}
|
|
]
|
|
|
|
quota.size = 0
|
|
quota.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {
|
|
'positions': [
|
|
{'item': ['There is not enough quota available on quota "Budget Quota" to perform the operation.']},
|
|
{'item': ['There is not enough quota available on quota "Budget Quota" to perform the operation.']},
|
|
]
|
|
}
|
|
|
|
quota.size = 1
|
|
quota.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {
|
|
'positions': [
|
|
{},
|
|
{'item': ['There is not enough quota available on quota "Budget Quota" to perform the operation.']},
|
|
]
|
|
}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_quota_consume_cart(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
|
|
cr = CartPosition.objects.create(
|
|
event=event, cart_id="uxLJBUMEcnxOLI2EuxLYN1hWJq9GKu4yWL9FEgs2m7M0vdFi@api", item=item,
|
|
price=23,
|
|
expires=now() + datetime.timedelta(hours=3)
|
|
)
|
|
|
|
quota.size = 1
|
|
quota.save()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {
|
|
'positions': [
|
|
{'item': ['There is not enough quota available on quota "Budget Quota" to perform the operation.']},
|
|
]
|
|
}
|
|
|
|
res['consume_carts'] = [cr.cart_id]
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
assert not CartPosition.objects.filter(pk=cr.pk).exists()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_free(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['fees'] = []
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['positions'][0]['price'] = '0.00'
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert o.total == Decimal('0.00')
|
|
assert o.status == Order.STATUS_PAID
|
|
|
|
p = o.payments.first()
|
|
assert p.provider == "free"
|
|
assert p.amount == o.total
|
|
assert p.state == "confirmed"
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_require_payment_provider(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
del res['payment_provider']
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'payment_provider': ['This field is required.']}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_invalid_payment_provider(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['payment_provider'] = 'foo'
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'payment_provider': ['The given payment provider is not known.']}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_invalid_free_order(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['payment_provider'] = 'free'
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == ['You cannot use the "free" payment provider for non-free orders.']
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_invalid_status(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['status'] = 'e'
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
assert resp.data == {'status': ['"e" is not a valid choice.']}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_paid_generate_invoice(token_client, organizer, event, item, quota, question):
|
|
event.settings.invoice_generate = 'paid'
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['status'] = 'p'
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert o.invoices.count() == 1
|
|
|
|
p = o.payments.first()
|
|
assert p.provider == "banktransfer"
|
|
assert p.amount == o.total
|
|
assert p.state == "confirmed"
|
|
|
|
|
|
REFUND_CREATE_PAYLOAD = {
|
|
"state": "created",
|
|
"provider": "manual",
|
|
"amount": "23.00",
|
|
"source": "admin",
|
|
"payment": 2,
|
|
"info": {
|
|
"foo": "bar",
|
|
}
|
|
}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_create(token_client, organizer, event, order):
|
|
res = copy.deepcopy(REFUND_CREATE_PAYLOAD)
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/refunds/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
r = order.refunds.get(local_id=resp.data['local_id'])
|
|
assert r.provider == "manual"
|
|
assert r.amount == Decimal("23.00")
|
|
assert r.state == "created"
|
|
assert r.source == "admin"
|
|
assert r.info_data == {"foo": "bar"}
|
|
assert r.payment.local_id == 2
|
|
order.refresh_from_db()
|
|
assert order.status == Order.STATUS_PENDING
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_create_mark_refunded(token_client, organizer, event, order):
|
|
res = copy.deepcopy(REFUND_CREATE_PAYLOAD)
|
|
res['mark_refunded'] = True
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/refunds/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
r = order.refunds.get(local_id=resp.data['local_id'])
|
|
assert r.provider == "manual"
|
|
assert r.amount == Decimal("23.00")
|
|
assert r.state == "created"
|
|
assert r.source == "admin"
|
|
assert r.info_data == {"foo": "bar"}
|
|
assert r.payment.local_id == 2
|
|
order.refresh_from_db()
|
|
assert order.status == Order.STATUS_REFUNDED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_optional_fields(token_client, organizer, event, order):
|
|
res = copy.deepcopy(REFUND_CREATE_PAYLOAD)
|
|
del res['info']
|
|
del res['payment']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/refunds/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
r = order.refunds.get(local_id=resp.data['local_id'])
|
|
assert r.provider == "manual"
|
|
assert r.amount == Decimal("23.00")
|
|
assert r.state == "created"
|
|
assert r.source == "admin"
|
|
|
|
del res['state']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/refunds/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_create_invalid_payment(token_client, organizer, event, order):
|
|
res = copy.deepcopy(REFUND_CREATE_PAYLOAD)
|
|
res['payment'] = 7
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/{}/refunds/'.format(
|
|
organizer.slug, event.slug, order.code
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 400
|