forked from CGM_Public/pretix_original
3047 lines
108 KiB
Python
3047 lines
108 KiB
Python
#
|
|
# This file is part of pretix (Community Edition).
|
|
#
|
|
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
|
# Copyright (C) 2020-2021 rami.io GmbH and contributors
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
|
# Public License as published by the Free Software Foundation in version 3 of the License.
|
|
#
|
|
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
|
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
|
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
|
# this file, see <https://pretix.eu/about/en/license>.
|
|
#
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
|
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
|
# <https://www.gnu.org/licenses/>.
|
|
#
|
|
import copy
|
|
import datetime
|
|
import json
|
|
from decimal import Decimal
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
from django.conf import settings
|
|
from django.core import mail as djmail
|
|
from django.core.files.base import ContentFile
|
|
from django.utils.timezone import now
|
|
from django_countries.fields import Country
|
|
from django_scopes import scopes_disabled
|
|
from tests.const import SAMPLE_PNG
|
|
|
|
from pretix.base.models import (
|
|
InvoiceAddress, Item, Order, OrderPosition, Organizer, Question,
|
|
SeatingPlan,
|
|
)
|
|
from pretix.base.models.orders import CartPosition, OrderFee, QuestionAnswer
|
|
|
|
|
|
@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 medium(organizer):
|
|
return organizer.reusable_media.create(
|
|
type="barcode",
|
|
identifier="ABCDE"
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def organizer2():
|
|
return Organizer.objects.create(name='Partner', slug='partner')
|
|
|
|
|
|
@pytest.fixture
|
|
def medium2(organizer2):
|
|
return organizer2.reusable_media.create(
|
|
type="barcode",
|
|
identifier="ABCDE"
|
|
)
|
|
|
|
|
|
@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=datetime.timezone.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=datetime.timezone.utc),
|
|
expires=datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=datetime.timezone.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)
|
|
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, canceled=True)
|
|
InvoiceAddress.objects.create(order=o, company="Sample company", country=Country('NZ'),
|
|
vat_id="DE123", vat_id_validated=True)
|
|
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",
|
|
positionid=1,
|
|
)
|
|
OrderPosition.objects.create(
|
|
order=o,
|
|
item=item,
|
|
variation=None,
|
|
price=Decimal("23"),
|
|
attendee_name_parts={"full_name": "Peter", "_scheme": "full"},
|
|
secret="YBiYJrmF5ufiTLdV1iDf",
|
|
pseudonymization_id="JKLM",
|
|
canceled=True,
|
|
positionid=2,
|
|
)
|
|
op.answers.create(question=question, answer='S')
|
|
return o
|
|
|
|
|
|
@pytest.fixture
|
|
def clist_autocheckin(event):
|
|
c = event.checkin_lists.create(name="Default", all_products=True, auto_checkin_sales_channels=['web'])
|
|
return c
|
|
|
|
|
|
ORDER_CREATE_PAYLOAD = {
|
|
"email": "dummy@dummy.test",
|
|
"phone": "+49622112345",
|
|
"locale": "en",
|
|
"sales_channel": "web",
|
|
"valid_if_pending": True,
|
|
"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",
|
|
"state": "",
|
|
"zipcode": "",
|
|
"city": "Sample City",
|
|
"country": "NZ",
|
|
"internal_reference": "",
|
|
"custom_field": None,
|
|
"vat_id": ""
|
|
},
|
|
"positions": [
|
|
{
|
|
"positionid": 1,
|
|
"item": 1,
|
|
"variation": None,
|
|
"price": "23.00",
|
|
"attendee_name_parts": {"full_name": "Peter"},
|
|
"attendee_email": None,
|
|
"addon_to": None,
|
|
"company": "FOOCORP",
|
|
"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
|
|
with scopes_disabled():
|
|
customer = organizer.customers.create()
|
|
res['customer'] = customer.identifier
|
|
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 resp.data['positions'][0].get('pdf_data')
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert o.customer == customer
|
|
assert o.email == "dummy@dummy.test"
|
|
assert o.phone == "+49622112345"
|
|
assert o.locale == "en"
|
|
assert o.total == Decimal('23.25')
|
|
assert o.status == Order.STATUS_PENDING
|
|
assert o.sales_channel == "web"
|
|
assert o.valid_if_pending
|
|
assert o.expires > now()
|
|
assert not o.testmode
|
|
|
|
with scopes_disabled():
|
|
p = o.payments.first()
|
|
assert p.provider == "banktransfer"
|
|
assert p.amount == o.total
|
|
assert p.state == "created"
|
|
|
|
with scopes_disabled():
|
|
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"
|
|
with scopes_disabled():
|
|
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"}
|
|
assert pos.company == "FOOCORP"
|
|
with scopes_disabled():
|
|
answ = pos.answers.first()
|
|
assert answ.question == question
|
|
assert answ.answer == "S"
|
|
with scopes_disabled():
|
|
assert o.transactions.count() == 2
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_duplicate_code(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['code'] = 'ABC12'
|
|
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
|
|
assert resp.data['code'] == 'ABC12'
|
|
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."]}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_max_size(token_client, organizer, event, item, quota, question):
|
|
quota.size = settings.PRETIX_MAX_ORDER_SIZE * 2
|
|
quota.save()
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'] = [
|
|
{
|
|
"item": item.pk,
|
|
"variation": None,
|
|
"price": "23.00",
|
|
"attendee_name_parts": {"full_name": "Peter"},
|
|
"attendee_email": None,
|
|
"addon_to": None,
|
|
"subevent": None
|
|
}
|
|
] * (settings.PRETIX_MAX_ORDER_SIZE + 1)
|
|
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": [f"Orders cannot have more than {settings.PRETIX_MAX_ORDER_SIZE} positions."]}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_expires(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
|
|
|
|
expires = now() - datetime.timedelta(hours=7)
|
|
res['expires'] = expires.isoformat()
|
|
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 == {"expires": ["Expiration date must be in the future."]}
|
|
|
|
expires = now() + datetime.timedelta(hours=7)
|
|
res['expires'] = expires.isoformat()
|
|
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 resp.data['positions'][0].get('pdf_data')
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert o.expires == expires
|
|
assert o.status == Order.STATUS_PENDING
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_simulate(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
question.type = Question.TYPE_CHOICE_MULTIPLE
|
|
question.save()
|
|
with scopes_disabled():
|
|
voucher = event.vouchers.create(price_mode="set", value=21.50, item=item, budget=Decimal('2.50'),
|
|
max_usages=999)
|
|
opt = question.options.create(answer="L")
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['positions'][0]['answers'][0]['options'] = [opt.pk]
|
|
res['positions'][0]['voucher'] = voucher.code
|
|
del res['positions'][0]['price']
|
|
res['simulate'] = True
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
assert Order.objects.count() == 0
|
|
assert QuestionAnswer.objects.count() == 0
|
|
assert OrderPosition.objects.count() == 0
|
|
assert OrderFee.objects.count() == 0
|
|
assert InvoiceAddress.objects.count() == 0
|
|
d = resp.data
|
|
del d['last_modified']
|
|
del d['secret']
|
|
del d['url']
|
|
del d['expires']
|
|
del d['invoice_address']['last_modified']
|
|
del d['positions'][0]['secret']
|
|
assert d == {
|
|
'code': 'PREVIEW',
|
|
'event': 'dummy',
|
|
'status': 'n',
|
|
'testmode': False,
|
|
'email': 'dummy@dummy.test',
|
|
'phone': '+49622112345',
|
|
'customer': None,
|
|
'locale': 'en',
|
|
'datetime': None,
|
|
'payment_date': None,
|
|
'payment_provider': None,
|
|
'valid_if_pending': True,
|
|
'fees': [
|
|
{
|
|
'id': 0,
|
|
'fee_type': 'payment',
|
|
'value': '0.25',
|
|
'description': '',
|
|
'internal_type': '',
|
|
'tax_rate': '0.00',
|
|
'tax_value': '0.00',
|
|
'tax_rule': None,
|
|
'canceled': False
|
|
}
|
|
],
|
|
'total': '21.75',
|
|
'comment': '',
|
|
"custom_followup_at": None,
|
|
'invoice_address': {
|
|
'is_business': False,
|
|
'company': 'Sample company',
|
|
'name': 'Fo',
|
|
'name_parts': {'full_name': 'Fo', '_scheme': 'full'},
|
|
'street': 'Bar',
|
|
'zipcode': '',
|
|
'city': 'Sample City',
|
|
'country': 'NZ',
|
|
'state': '',
|
|
'vat_id': '',
|
|
'vat_id_validated': False,
|
|
'internal_reference': '',
|
|
'custom_field': None
|
|
},
|
|
'positions': [
|
|
{
|
|
'id': 0,
|
|
'order': '',
|
|
'positionid': 1,
|
|
'item': item.pk,
|
|
'variation': None,
|
|
'price': '21.50',
|
|
'attendee_name': 'Peter',
|
|
'attendee_name_parts': {'full_name': 'Peter', '_scheme': 'full'},
|
|
'attendee_email': None,
|
|
'voucher': voucher.pk,
|
|
'voucher_budget_use': '1.50',
|
|
'tax_rate': '0.00',
|
|
'tax_value': '0.00',
|
|
'addon_to': None,
|
|
'subevent': None,
|
|
'discount': None,
|
|
'checkins': [],
|
|
'downloads': [],
|
|
"valid_from": None,
|
|
"valid_until": None,
|
|
"blocked": None,
|
|
'answers': [
|
|
{'question': question.pk, 'answer': 'L', 'question_identifier': 'ABC',
|
|
'options': [opt.pk],
|
|
'option_identifiers': [opt.identifier]}
|
|
],
|
|
'tax_rule': None,
|
|
'pseudonymization_id': 'PREVIEW',
|
|
'seat': None,
|
|
'company': "FOOCORP",
|
|
'street': None,
|
|
'city': None,
|
|
'zipcode': None,
|
|
'state': None,
|
|
'country': None,
|
|
'canceled': False
|
|
}
|
|
],
|
|
'downloads': [],
|
|
'checkin_attention': False,
|
|
'checkin_text': None,
|
|
'payments': [],
|
|
'refunds': [],
|
|
'require_approval': False,
|
|
'sales_channel': 'web',
|
|
}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_positionids_addons_simulated(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
|
|
}
|
|
]
|
|
res['simulate'] = True
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
del resp.data['positions'][0]['secret']
|
|
del resp.data['positions'][1]['secret']
|
|
assert [dict(f) for f in resp.data['positions']] == [
|
|
{'id': 0, 'order': '', 'positionid': 1, 'item': item.pk, 'variation': None, 'price': '23.00',
|
|
'attendee_name': 'Peter', 'attendee_name_parts': {'full_name': 'Peter', '_scheme': 'full'}, 'company': None,
|
|
'street': None, 'zipcode': None, 'city': None, 'country': None, 'state': None, 'attendee_email': None,
|
|
'voucher': None, 'tax_rate': '0.00', 'tax_value': '0.00', 'discount': None, 'voucher_budget_use': None,
|
|
'addon_to': None, 'subevent': None, 'checkins': [], 'downloads': [], 'answers': [], 'tax_rule': None,
|
|
'pseudonymization_id': 'PREVIEW', 'seat': None, 'canceled': False, 'valid_from': None, 'valid_until': None, 'blocked': None},
|
|
{'id': 0, 'order': '', 'positionid': 2, 'item': item.pk, 'variation': None, 'price': '23.00',
|
|
'attendee_name': 'Peter', 'attendee_name_parts': {'full_name': 'Peter', '_scheme': 'full'}, 'company': None,
|
|
'street': None, 'zipcode': None, 'city': None, 'country': None, 'state': None, 'attendee_email': None,
|
|
'voucher': None, 'tax_rate': '0.00', 'tax_value': '0.00', 'discount': None, 'voucher_budget_use': None,
|
|
'addon_to': 1, 'subevent': None, 'checkins': [], 'downloads': [], 'answers': [], 'tax_rule': None,
|
|
'pseudonymization_id': 'PREVIEW', 'seat': None, 'canceled': False, 'valid_from': None, 'valid_until': None, 'blocked': None}
|
|
]
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_autocheckin(token_client, organizer, event, item, quota, question, clist_autocheckin):
|
|
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
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert "web" in clist_autocheckin.auto_checkin_sales_channels
|
|
assert o.positions.first().checkins.first().auto_checked_in
|
|
|
|
clist_autocheckin.auto_checkin_sales_channels = []
|
|
clist_autocheckin.save()
|
|
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert clist_autocheckin.auto_checkin_sales_channels == []
|
|
assert o.positions.first().checkins.count() == 0
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_require_approval_free(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['require_approval'] = True
|
|
res['send_email'] = True
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['positions'][0]['price'] = '0.00'
|
|
res['fees'] = []
|
|
djmail.outbox = []
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert o.require_approval
|
|
assert o.status == Order.STATUS_PENDING
|
|
assert len(djmail.outbox) == 1
|
|
assert djmail.outbox[0].subject == "Your order: {}".format(resp.data['code'])
|
|
assert "approval" in djmail.outbox[0].body
|
|
|
|
|
|
@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
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
with pytest.raises(InvoiceAddress.DoesNotExist):
|
|
o.invoice_address
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_sales_channel_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['sales_channel']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert o.sales_channel == "web"
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_sales_channel_invalid(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['sales_channel'] = '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 == {'sales_channel': ['Unknown sales channel.']}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_in_test_mode(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['testmode'] = True
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert o.testmode
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_in_test_mode_saleschannel_limited(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['testmode'] = True
|
|
res['sales_channel'] = 'baz'
|
|
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 == {'testmode': ['This sales channel does not provide support for test mode.']}
|
|
|
|
|
|
@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
|
|
with scopes_disabled():
|
|
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
|
|
|
|
with scopes_disabled():
|
|
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
|
|
with scopes_disabled():
|
|
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
|
|
with scopes_disabled():
|
|
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
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert not o.email
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_payment_provider_optional_free(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['positions'][0]['price'] = '0.00'
|
|
res['positions'][0]['status'] = 'p'
|
|
del res['payment_provider']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert not o.payments.exists()
|
|
|
|
|
|
@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
|
|
|
|
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
|
|
with scopes_disabled():
|
|
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
|
|
with scopes_disabled():
|
|
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
|
|
with scopes_disabled():
|
|
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
|
|
with scopes_disabled():
|
|
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"
|
|
with scopes_disabled():
|
|
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_fee_as_percentage(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['fees'][0]['_treat_value_as_percentage'] = True
|
|
res['fees'][0]['value'] = '10.00'
|
|
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
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
fee = o.fees.first()
|
|
assert fee.value == Decimal('2.30')
|
|
assert o.total == Decimal('25.30')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_fee_with_auto_tax(token_client, organizer, event, item, quota, question, taxrule):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['fees'][0]['_split_taxes_like_products'] = True
|
|
res['fees'][0]['_treat_value_as_percentage'] = True
|
|
res['fees'][0]['value'] = '10.00'
|
|
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
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
fee = o.fees.first()
|
|
assert fee.value == Decimal('2.30')
|
|
assert fee.tax_rate == Decimal('19.00')
|
|
assert o.total == Decimal('25.30')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_negative_fee_with_auto_tax(token_client, organizer, event, item, quota, question, taxrule):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['fees'][0]['_split_taxes_like_products'] = True
|
|
res['fees'][0]['value'] = '-10.00'
|
|
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
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
fee = o.fees.first()
|
|
assert fee.value == Decimal('-10.00')
|
|
assert fee.tax_value == Decimal('-1.60')
|
|
assert fee.tax_rate == Decimal('19.00')
|
|
assert o.total == Decimal('13.00')
|
|
|
|
|
|
@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.']}]}
|
|
|
|
with scopes_disabled():
|
|
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.']}]}
|
|
|
|
with scopes_disabled():
|
|
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.']}]}
|
|
|
|
with scopes_disabled():
|
|
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_subevent_disabled(token_client, organizer, event, item, subevent, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['positions'][0]['subevent'] = subevent.pk
|
|
s = item.subeventitem_set.create(subevent=subevent, disabled=True)
|
|
quota.subevent = subevent
|
|
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': ['The product "Budget Ticket" is not available on this date.']}]}
|
|
|
|
s.delete()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_subevent_variation_disabled(token_client, organizer, event, item, subevent, quota, question):
|
|
with scopes_disabled():
|
|
item2 = event.items.create(name="Budget Ticket", default_price=23)
|
|
var = item2.variations.create(default_price=12, value="XS")
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item2.pk
|
|
res['positions'][0]['variation'] = var.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['positions'][0]['subevent'] = subevent.pk
|
|
s = var.subeventitemvariation_set.create(subevent=subevent, disabled=True)
|
|
quota.subevent = subevent
|
|
quota.items.add(item2)
|
|
quota.variations.add(var)
|
|
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': ['The product "Budget Ticket" is not available on this date.']}]}
|
|
|
|
s.delete()
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
|
|
|
|
@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
|
|
with scopes_disabled():
|
|
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_is_bundled_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,
|
|
"is_bundled": True,
|
|
"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
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
pos1 = o.positions.first()
|
|
pos2 = o.positions.last()
|
|
assert pos2.addon_to == pos1
|
|
assert pos2.is_bundled
|
|
|
|
|
|
@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.']
|
|
}
|
|
]
|
|
}
|
|
|
|
res['positions'] = [
|
|
{
|
|
"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 == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert o.positions.first().positionid == 1
|
|
assert o.positions.last().positionid == 2
|
|
|
|
|
|
@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.']}]}]}
|
|
|
|
with scopes_disabled():
|
|
question2.options.create(answer="L")
|
|
with scopes_disabled():
|
|
res['positions'][0]['answers'][0]['options'] = [
|
|
question2.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': ['The specified option does not belong to this question.']}]}]}
|
|
|
|
with scopes_disabled():
|
|
question.options.create(answer="L")
|
|
with scopes_disabled():
|
|
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.']}]}]}
|
|
|
|
r = token_client.post(
|
|
'/api/v1/upload',
|
|
data={
|
|
'media_type': 'image/png',
|
|
'file': ContentFile(SAMPLE_PNG)
|
|
},
|
|
format='upload',
|
|
HTTP_CONTENT_DISPOSITION='attachment; filename="file.png"',
|
|
)
|
|
assert r.status_code == 201
|
|
file_id_png = r.data['id']
|
|
res['positions'][0]['answers'][0]['options'] = []
|
|
res['positions'][0]['answers'][0]['answer'] = file_id_png
|
|
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 == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
pos = o.positions.first()
|
|
answ = pos.answers.first()
|
|
assert answ.file
|
|
assert answ.answer.startswith("file://")
|
|
|
|
question.type = Question.TYPE_CHOICE_MULTIPLE
|
|
question.save()
|
|
with scopes_disabled():
|
|
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
|
|
with scopes_disabled():
|
|
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
|
|
with scopes_disabled():
|
|
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
|
|
with scopes_disabled():
|
|
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
|
|
with scopes_disabled():
|
|
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
|
|
with scopes_disabled():
|
|
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
|
|
with scopes_disabled():
|
|
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
|
|
with scopes_disabled():
|
|
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.']},
|
|
]
|
|
}
|
|
|
|
res['force'] = True
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
|
|
|
|
@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
|
|
|
|
with scopes_disabled():
|
|
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
|
|
with scopes_disabled():
|
|
assert not CartPosition.objects.filter(pk=cr.pk).exists()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_quota_consume_cart_expired(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
|
|
|
|
with scopes_disabled():
|
|
cr = CartPosition.objects.create(
|
|
event=event, cart_id="uxLJBUMEcnxOLI2EuxLYN1hWJq9GKu4yWL9FEgs2m7M0vdFi@api", item=item,
|
|
price=23,
|
|
expires=now() - datetime.timedelta(hours=3)
|
|
)
|
|
|
|
quota.size = 0
|
|
quota.save()
|
|
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 == 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_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
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert o.total == Decimal('0.00')
|
|
assert o.status == Order.STATUS_PAID
|
|
|
|
with scopes_disabled():
|
|
p = o.payments.first()
|
|
assert p.provider == "free"
|
|
assert p.amount == o.total
|
|
assert p.state == "confirmed"
|
|
assert o.all_logentries().count() == 3
|
|
|
|
|
|
@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['payment_date'] = '2019-04-01 08:20:00Z'
|
|
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
|
|
with scopes_disabled():
|
|
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"
|
|
assert p.payment_date.year == 2019
|
|
assert p.payment_date.month == 4
|
|
assert p.payment_date.day == 1
|
|
assert p.payment_date.hour == 8
|
|
assert p.payment_date.minute == 20
|
|
|
|
|
|
@pytest.fixture
|
|
def seat(event, organizer, item):
|
|
SeatingPlan.objects.create(
|
|
name="Plan", organizer=organizer, layout="{}"
|
|
)
|
|
event.seat_category_mappings.create(
|
|
layout_category='Stalls', product=item
|
|
)
|
|
return event.seats.create(seat_number="A1", product=item, seat_guid="A1")
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_with_seat(token_client, organizer, event, item, quota, seat, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['seat'] = seat.seat_guid
|
|
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
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
p = o.positions.first()
|
|
assert p.seat == seat
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_with_blocked_seat_allowed(token_client, organizer, event, item, quota, seat, question):
|
|
seat.blocked = True
|
|
seat.save()
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['seat'] = seat.seat_guid
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['sales_channel'] = 'bar'
|
|
event.settings.seating_allow_blocked_seats_for_channel = ['bar']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_with_blocked_seat(token_client, organizer, event, item, quota, seat, question):
|
|
seat.blocked = True
|
|
seat.save()
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['seat'] = seat.seat_guid
|
|
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': [
|
|
{'seat': ['The selected seat "Seat A1" is not available.']},
|
|
]
|
|
}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_with_used_seat(token_client, organizer, event, item, quota, seat, question):
|
|
CartPosition.objects.create(
|
|
event=event, cart_id='aaa', item=item,
|
|
price=21.5, expires=now() + datetime.timedelta(minutes=10), seat=seat
|
|
)
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['seat'] = seat.seat_guid
|
|
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': [
|
|
{'seat': ['The selected seat "Seat A1" is not available.']},
|
|
]
|
|
}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_with_unknown_seat(token_client, organizer, event, item, quota, seat, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['seat'] = seat.seat_guid + '_'
|
|
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': [
|
|
{'seat': ['The specified seat does not exist.']},
|
|
]
|
|
}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_require_seat(token_client, organizer, event, item, quota, seat, 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': [
|
|
{'seat': ['The specified product requires to choose a seat.']},
|
|
]
|
|
}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_unseated(token_client, organizer, event, item, quota, seat, question):
|
|
with scopes_disabled():
|
|
item2 = event.items.create(name="Budget Ticket", default_price=23)
|
|
quota.items.add(item2)
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item2.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['positions'][0]['seat'] = seat.seat_guid
|
|
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': [
|
|
{'seat': ['The specified product does not allow to choose a seat.']},
|
|
]
|
|
}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_with_duplicate_seat(token_client, organizer, event, item, quota, seat, 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,
|
|
"seat": seat.seat_guid
|
|
},
|
|
{
|
|
"positionid": 2,
|
|
"item": item.pk,
|
|
"variation": None,
|
|
"price": "23.00",
|
|
"attendee_name_parts": {"full_name": "Peter"},
|
|
"attendee_email": None,
|
|
"addon_to": None,
|
|
"answers": [],
|
|
"subevent": None,
|
|
"seat": seat.seat_guid
|
|
}
|
|
]
|
|
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': [
|
|
{},
|
|
{'seat': ['The selected seat "Seat A1" is not available.']},
|
|
]
|
|
}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_with_seat_consumed_from_cart(token_client, organizer, event, item, quota, seat, question):
|
|
CartPosition.objects.create(
|
|
event=event, cart_id='aaa', item=item,
|
|
price=21.5, expires=now() + datetime.timedelta(minutes=10), seat=seat
|
|
)
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['seat'] = seat.seat_guid
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['consume_carts'] = ['aaa']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
p = o.positions.first()
|
|
assert p.seat == seat
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_with_voucher_and_price_set(token_client, organizer, event, item, quota, question):
|
|
with scopes_disabled():
|
|
voucher = event.vouchers.create(code="FOOBAR", item=item, price_mode="set", value=0, max_usages=3)
|
|
item.tax_rule = event.tax_rules.create(rate=19, price_includes_tax=False)
|
|
item.save()
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['voucher'] = voucher.code
|
|
res['positions'][0]['price'] = '0.00'
|
|
res['positions'][0].pop("tax_rate", None)
|
|
res['positions'][0].pop("tax_rule", None)
|
|
res['positions'][0].pop("tax_value", None)
|
|
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
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
p = o.positions.first()
|
|
assert p.voucher == voucher
|
|
assert p.price == Decimal("0.00")
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_with_voucher_consumed_from_cart(token_client, organizer, event, item, quota, question):
|
|
with scopes_disabled():
|
|
voucher = event.vouchers.create(code="FOOBAR", item=item, max_usages=3, redeemed=2)
|
|
CartPosition.objects.create(
|
|
event=event, cart_id='aaa', item=item, voucher=voucher,
|
|
price=21.5, expires=now() + datetime.timedelta(minutes=10),
|
|
)
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['voucher'] = voucher.code
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['consume_carts'] = ['aaa']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
p = o.positions.first()
|
|
assert p.voucher == voucher
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_send_no_emails(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
|
|
djmail.outbox = []
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
assert len(djmail.outbox) == 0
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_send_emails(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['send_email'] = True
|
|
djmail.outbox = []
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
assert len(djmail.outbox) == 1
|
|
assert djmail.outbox[0].subject == "Your order: {}".format(resp.data['code'])
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_send_emails_free(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['price'] = '0.00'
|
|
res['payment_provider'] = 'free'
|
|
del res['fees']
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['send_email'] = True
|
|
djmail.outbox = []
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
assert len(djmail.outbox) == 1
|
|
assert djmail.outbox[0].subject == "Your order: {}".format(resp.data['code'])
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_send_emails_based_on_sales_channel(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['price'] = '0.00'
|
|
res['payment_provider'] = 'free'
|
|
del res['fees']
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['send_email'] = None
|
|
djmail.outbox = []
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
assert len(djmail.outbox) == 1
|
|
assert djmail.outbox[0].subject == "Your order: {}".format(resp.data['code'])
|
|
|
|
event.settingsmail_sales_channel_placed_paid = []
|
|
djmail.outbox = []
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
assert len(djmail.outbox) == 1
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_send_emails_paid(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['send_email'] = True
|
|
res['status'] = 'p'
|
|
djmail.outbox = []
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
assert len(djmail.outbox) == 2
|
|
assert djmail.outbox[0].subject == "Your order: {}".format(resp.data['code'])
|
|
assert djmail.outbox[1].subject == "Payment received for your order: {}".format(resp.data['code'])
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_send_emails_legacy(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['send_mail'] = True
|
|
res['status'] = 'p'
|
|
djmail.outbox = []
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
assert len(djmail.outbox) == 2
|
|
assert djmail.outbox[0].subject == "Your order: {}".format(resp.data['code'])
|
|
assert djmail.outbox[1].subject == "Payment received for your order: {}".format(resp.data['code'])
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_paid_require_payment_method(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['payment_provider']
|
|
res['status'] = 'p'
|
|
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 create a paid order without a payment provider.'
|
|
]
|
|
|
|
res['status'] = "n"
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
assert not o.payments.exists()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_auto_validity(token_client, organizer, event, item, quota, question):
|
|
item.validity_mode = 'dynamic'
|
|
item.validity_dynamic_duration_minutes = 30
|
|
item.save()
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
del res['positions'][0]['price']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
p = o.positions.first()
|
|
assert now() - datetime.timedelta(seconds=30) < p.valid_from <= now()
|
|
assert now() + datetime.timedelta(minutes=29) < p.valid_until < now() + datetime.timedelta(minutes=31)
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_manual_validity_precedence(token_client, organizer, event, item, quota, question):
|
|
item.validity_mode = 'dynamic'
|
|
item.validity_dynamic_duration_minutes = 30
|
|
item.save()
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['positions'][0]['valid_from'] = '2022-01-01T09:00:00.000Z'
|
|
res['positions'][0]['valid_until'] = '2022-01-03T09:00:00.000Z'
|
|
del res['positions'][0]['price']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
p = o.positions.first()
|
|
assert p.valid_from.isoformat() == '2022-01-01T09:00:00+00:00'
|
|
assert p.valid_until.isoformat() == '2022-01-03T09:00:00+00:00'
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_auto_validity_with_requested_start(token_client, organizer, event, item, quota, question):
|
|
item.validity_mode = 'dynamic'
|
|
item.validity_dynamic_duration_minutes = 30
|
|
item.validity_dynamic_start_choice = True
|
|
item.save()
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['positions'][0]['requested_valid_from'] = '2039-01-01T09:00:00.000Z'
|
|
del res['positions'][0]['price']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
p = o.positions.first()
|
|
assert p.valid_from.isoformat() == '2039-01-01T09:00:00+00:00'
|
|
assert p.valid_until.isoformat() == '2039-01-01T09:30:00+00:00'
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_auto_validity_with_requested_start_limitation(token_client, organizer, event, item, quota, question):
|
|
item.validity_mode = 'dynamic'
|
|
item.validity_dynamic_duration_minutes = 30
|
|
item.validity_dynamic_start_choice = True
|
|
item.validity_dynamic_start_choice_day_limit = 24
|
|
item.save()
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['positions'][0]['requested_valid_from'] = (now() + datetime.timedelta(days=30)).isoformat()
|
|
del res['positions'][0]['price']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
p = o.positions.first()
|
|
assert now() + datetime.timedelta(days=23) < p.valid_from <= now() + datetime.timedelta(days=26)
|
|
assert p.valid_until == p.valid_from + datetime.timedelta(minutes=30)
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_auto_validity_with_requested_start_in_past(token_client, organizer, event, item, quota, question):
|
|
item.validity_mode = 'dynamic'
|
|
item.validity_dynamic_duration_minutes = 30
|
|
item.validity_dynamic_start_choice = True
|
|
item.validity_dynamic_start_choice_day_limit = 24
|
|
item.save()
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['positions'][0]['requested_valid_from'] = (now() - datetime.timedelta(days=30)).isoformat()
|
|
del res['positions'][0]['price']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
p = o.positions.first()
|
|
assert now() - datetime.timedelta(days=31) < p.valid_from <= now() - datetime.timedelta(days=29)
|
|
assert p.valid_until == p.valid_from + datetime.timedelta(minutes=30)
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_auto_pricing(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['positions'][0]['price']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
p = o.positions.first()
|
|
assert p.price == item.default_price
|
|
assert o.total == item.default_price + Decimal('0.25')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_auto_pricing_reverse_charge(token_client, organizer, event, item, quota, question, taxrule):
|
|
taxrule.eu_reverse_charge = True
|
|
taxrule.home_country = Country('DE')
|
|
taxrule.save()
|
|
item.tax_rule = taxrule
|
|
item.save()
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['invoice_address']['country'] = 'FR'
|
|
res['invoice_address']['is_business'] = True
|
|
res['invoice_address']['vat_id'] = 'FR12345'
|
|
res['invoice_address']['vat_id_validated'] = True
|
|
del res['positions'][0]['price']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
p = o.positions.first()
|
|
assert p.price == Decimal('19.33')
|
|
assert p.tax_rate == Decimal('0.00')
|
|
assert p.tax_value == Decimal('0.00')
|
|
assert o.total == Decimal('19.58')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_auto_pricing_country_rate(token_client, organizer, event, item, quota, question, taxrule):
|
|
taxrule.eu_reverse_charge = True
|
|
taxrule.custom_rules = json.dumps([
|
|
{'country': 'FR', 'address_type': '', 'action': 'vat', 'rate': '100.00'}
|
|
])
|
|
taxrule.save()
|
|
item.tax_rule = taxrule
|
|
item.save()
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['invoice_address']['country'] = 'FR'
|
|
res['invoice_address']['is_business'] = True
|
|
res['invoice_address']['vat_id'] = 'FR12345'
|
|
res['invoice_address']['vat_id_validated'] = True
|
|
del res['positions'][0]['price']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
p = o.positions.first()
|
|
assert p.price == Decimal('38.66')
|
|
assert p.tax_rate == Decimal('100.00')
|
|
assert p.tax_value == Decimal('19.33')
|
|
assert o.total == Decimal('38.91')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_auto_pricing_reverse_charge_require_valid_vatid(token_client, organizer, event, item, quota,
|
|
question, taxrule):
|
|
taxrule.eu_reverse_charge = True
|
|
taxrule.home_country = Country('DE')
|
|
taxrule.save()
|
|
item.tax_rule = taxrule
|
|
item.save()
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['invoice_address']['country'] = 'FR'
|
|
res['invoice_address']['is_business'] = True
|
|
res['invoice_address']['vat_id'] = 'FR12345'
|
|
del res['positions'][0]['price']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
p = o.positions.first()
|
|
assert p.price == Decimal('23.00')
|
|
assert p.tax_rate == Decimal('19.00')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_autopricing_voucher_budget_partially(token_client, organizer, event, item, quota, question,
|
|
taxrule):
|
|
with scopes_disabled():
|
|
voucher = event.vouchers.create(price_mode="set", value=21.50, item=item, budget=Decimal('2.50'),
|
|
max_usages=999)
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['positions'][0]['voucher'] = voucher.code
|
|
del res['positions'][0]['price']
|
|
del res['positions'][0]['positionid']
|
|
res['positions'].append(res['positions'][0])
|
|
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
assert resp.data["positions"][0]["voucher_budget_use"] == "1.50"
|
|
assert resp.data["positions"][1]["voucher_budget_use"] == "1.00"
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
p = o.positions.first()
|
|
p2 = o.positions.last()
|
|
assert p.price == Decimal('21.50')
|
|
assert p2.price == Decimal('22.00')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_autopricing_voucher_budget_full(token_client, organizer, event, item, quota, question, taxrule):
|
|
with scopes_disabled():
|
|
voucher = event.vouchers.create(price_mode="set", value=21.50, item=item, budget=Decimal('0.50'),
|
|
max_usages=999)
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['positions'][0]['voucher'] = voucher.code
|
|
del res['positions'][0]['price']
|
|
del res['positions'][0]['positionid']
|
|
res['positions'].append(res['positions'][0])
|
|
|
|
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': [{}, {'voucher': ['The voucher has a remaining budget of 0.00, therefore a '
|
|
'discount of 1.50 can not be given.']}]}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_voucher_budget_exceeded(token_client, organizer, event, item, quota, question, taxrule):
|
|
with scopes_disabled():
|
|
voucher = event.vouchers.create(price_mode="set", value=21.50, item=item, budget=Decimal('3.00'),
|
|
max_usages=999)
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['positions'][0]['voucher'] = voucher.code
|
|
res['positions'][0]['price'] = '19.00'
|
|
del res['positions'][0]['positionid']
|
|
|
|
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': [{'voucher': ['The voucher has a remaining budget of 3.00, therefore a '
|
|
'discount of 4.00 can not be given.']}]}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_voucher_price(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['positions'][0]['price']
|
|
with scopes_disabled():
|
|
voucher = event.vouchers.create(price_mode="set", value=15, item=item)
|
|
res['positions'][0]['voucher'] = voucher.code
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
p = o.positions.first()
|
|
assert p.voucher == voucher
|
|
voucher.refresh_from_db()
|
|
assert voucher.redeemed == 1
|
|
assert p.price == Decimal('15.00')
|
|
assert o.total == Decimal('15.25')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_voucher_unknown_code(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['positions'][0]['price']
|
|
with scopes_disabled():
|
|
event.vouchers.create(price_mode="set", value=15, item=item)
|
|
res['positions'][0]['voucher'] = "FOOBAR"
|
|
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': [
|
|
{'voucher': ['Object with code=FOOBAR does not exist.']},
|
|
]
|
|
}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_voucher_redeemed(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
del res['positions'][0]['price']
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
with scopes_disabled():
|
|
voucher = event.vouchers.create(price_mode="set", value=15, item=item, redeemed=1)
|
|
res['positions'][0]['voucher'] = voucher.code
|
|
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': [
|
|
{'voucher': ['The voucher has already been used the maximum number of times.']},
|
|
]
|
|
}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_voucher_redeemed_partially(token_client, organizer, event, item, quota, question):
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
res['positions'][0]['item'] = item.pk
|
|
del res['positions'][0]['price']
|
|
del res['positions'][0]['positionid']
|
|
with scopes_disabled():
|
|
voucher = event.vouchers.create(price_mode="set", value=15, item=item, redeemed=1, max_usages=2)
|
|
res['positions'][0]['voucher'] = voucher.code
|
|
res['positions'].append(copy.deepcopy(res['positions'][0]))
|
|
res['positions'].append(copy.deepcopy(res['positions'][0]))
|
|
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': [
|
|
{},
|
|
{'voucher': ['The voucher has already been used the maximum number of times.']},
|
|
{'voucher': ['The voucher has already been used the maximum number of times.']},
|
|
]
|
|
}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_voucher_item_mismatch(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['positions'][0]['price']
|
|
with scopes_disabled():
|
|
item2 = event.items.create(name="Budget Ticket", default_price=23)
|
|
voucher = event.vouchers.create(price_mode="set", value=15, item=item2, redeemed=0)
|
|
res['positions'][0]['voucher'] = voucher.code
|
|
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': [
|
|
{'voucher': ['This voucher is not valid for this product.']},
|
|
]
|
|
}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_voucher_expired(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['positions'][0]['price']
|
|
with scopes_disabled():
|
|
voucher = event.vouchers.create(price_mode="set", value=15, item=item, redeemed=0,
|
|
valid_until=now() - datetime.timedelta(days=1))
|
|
res['positions'][0]['voucher'] = voucher.code
|
|
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': [
|
|
{'voucher': ['This voucher is expired.']},
|
|
]
|
|
}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_voucher_block_quota(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['positions'][0]['price']
|
|
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
|
|
|
|
with scopes_disabled():
|
|
voucher = event.vouchers.create(price_mode="set", value=15, item=item, redeemed=0,
|
|
block_quota=True)
|
|
res['positions'][0]['voucher'] = voucher.code
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_pdf_data(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
|
|
with scopes_disabled():
|
|
customer = organizer.customers.create()
|
|
res['customer'] = customer.identifier
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/?pdf_data=true'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
assert 'secret' in resp.data['positions'][0]['pdf_data']
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_create_cart_and_consume_cart_with_addons(token_client, organizer, event, item, quota, question):
|
|
# End to end test for the combination of cart creation and order creation, as used eg in POS
|
|
with scopes_disabled():
|
|
addon_cat = event.categories.create(name='Addons')
|
|
addon_item = event.items.create(name='Workshop', default_price=2, category=addon_cat)
|
|
item.addons.create(addon_category=addon_cat)
|
|
q = event.quotas.create(name="Addon Quota", size=1)
|
|
q.items.add(addon_item)
|
|
|
|
res = {
|
|
'cart_id': 'aaa@api',
|
|
'item': item.pk,
|
|
'variation': None,
|
|
'price': '23.00',
|
|
'attendee_name_parts': {'full_name': 'Peter'},
|
|
'attendee_email': None,
|
|
'addon_to': None,
|
|
'subevent': None,
|
|
'expires': (now() + datetime.timedelta(days=1)).isoformat(),
|
|
'includes_tax': True,
|
|
'sales_channel': 'web',
|
|
'answers': [],
|
|
'addons': [
|
|
{
|
|
'item': addon_item.pk,
|
|
'variation': None,
|
|
'price': '1.00',
|
|
'attendee_name_parts': {'full_name': 'Peter\'s friend'},
|
|
'attendee_email': None,
|
|
'subevent': None,
|
|
'includes_tax': True,
|
|
'answers': []
|
|
}
|
|
],
|
|
}
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/cartpositions/bulk_create/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=[
|
|
res
|
|
]
|
|
)
|
|
assert resp.status_code == 200
|
|
assert len(resp.data['results']) == 1
|
|
assert resp.data['results'][0]['success']
|
|
assert resp.data['results'][0]['data']['addons']
|
|
|
|
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": addon_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 == 400
|
|
assert resp.data == {
|
|
'positions': [
|
|
{},
|
|
{'item': ['There is not enough quota available on quota "Addon Quota" to perform the operation.']},
|
|
]
|
|
}
|
|
|
|
res['consume_carts'] = ['aaa@api']
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_use_medium(token_client, organizer, event, item, quota, question, medium):
|
|
item.media_type = medium.type
|
|
item.media_policy = Item.MEDIA_POLICY_REUSE_OR_NEW
|
|
item.save()
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['use_reusable_medium'] = medium.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/?pdf_data=true'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
medium.refresh_from_db()
|
|
assert o.positions.first() == medium.linked_orderposition
|
|
assert resp.data['positions'][0]['pdf_data']['medium_identifier'] == medium.identifier
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_use_medium_other_organizer(token_client, organizer, event, item, quota, question, medium2):
|
|
item.media_type = medium2.type
|
|
item.media_policy = Item.MEDIA_POLICY_REUSE_OR_NEW
|
|
item.save()
|
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
|
res['positions'][0]['item'] = item.pk
|
|
res['positions'][0]['use_reusable_medium'] = medium2.pk
|
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
|
resp = token_client.post(
|
|
'/api/v1/organizers/{}/events/{}/orders/?pdf_data=true'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.data == {
|
|
"positions": [
|
|
{
|
|
"use_reusable_medium": ["The specified medium does not belong to this organizer."]
|
|
}
|
|
]
|
|
}
|
|
assert resp.status_code == 400
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_create_create_medium(token_client, organizer, event, item, quota, question):
|
|
item.media_type = 'barcode'
|
|
item.media_policy = Item.MEDIA_POLICY_REUSE_OR_NEW
|
|
item.save()
|
|
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/?pdf_data=true'.format(
|
|
organizer.slug, event.slug
|
|
), format='json', data=res
|
|
)
|
|
assert resp.status_code == 201
|
|
with scopes_disabled():
|
|
o = Order.objects.get(code=resp.data['code'])
|
|
i = resp.data['positions'][0]['pdf_data']['medium_identifier']
|
|
assert i
|
|
m = organizer.reusable_media.get(identifier=i)
|
|
assert m.linked_orderposition == o.positions.first()
|
|
assert m.type == "barcode"
|