Add sub-events and relative date settings (#503)

* Data model

* little crud

* SubEventItemForm etc

* Drop SubEventItem.active, quota editor

* Fix failing tests

* First frontend stuff

* Addons form stuff

* Quota calculation

* net price display on EventIndex

* Add tests, solve some bugs

* Correct quota selection in more places, consolidate pricing logic

* Fix failing quota tests

* Fix TypeError

* Add tests for checkout

* Fixed a bug in QuotaForm

* Prevent immutable cart if a quota was removed from an item

* Add tests for pricing

* Handle waiting list

* Filter in check-in list

* Fixed import lost in rebase

* Fix waiting list widget

* Voucher management

* Voucher redemption

* Fix broken tests

* Add subevents to OrderChangeManager

* Create a subevent during event creation

* Fix bulk voucher creation

* Introduce subevent.active

* Copy from for subevents

* Show active in list

* ICal download for subevents

* Check start and end of presale

* Failing tests / show cart logic

* Test

* Rebase migrations

* REST API integration of sub-events

* Integrate quota calculation into the traditional quota form

* Make subevent argument to add_position optional

* Log-display foo

* pretixdroid and subevents

* Filter by subevent

* Add more tests

* Some mor tests

* Rebase fixes

* More tests

* Relative dates

* Restrict selection in relative datetime widgets

* Filter subevent list

* Re-label has_subevents

* Rebase fixes, subevents in calendar view

* Performance and caching issues

* Refactor calendar templates

* Permission tests

* Calendar fixes and month selection

* subevent selection

* Rename subevents to dates

* Add tests for calendar views
This commit is contained in:
Raphael Michel
2017-07-11 13:56:00 +02:00
committed by GitHub
parent 554800c06f
commit 8123effa65
141 changed files with 5920 additions and 1012 deletions

View File

@@ -45,3 +45,11 @@ def token_client(client, team):
t = team.tokens.create(name='Foo')
client.credentials(HTTP_AUTHORIZATION='Token ' + t.token)
return client
@pytest.fixture
def subevent(event):
event.has_subevents = True
event.save()
return event.subevents.create(name="Foobar",
date_from=datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC))

View File

@@ -12,6 +12,7 @@ TEST_EVENT_RES = {
"presale_end": None,
"location": None,
"slug": "dummy",
"has_subevents": False,
}

View File

@@ -192,12 +192,13 @@ TEST_QUOTA_RES = {
"name": "Budget Quota",
"size": 200,
"items": [],
"variations": []
"variations": [],
"subevent": None
}
@pytest.mark.django_db
def test_quota_list(token_client, organizer, event, quota, item):
def test_quota_list(token_client, organizer, event, quota, item, subevent):
res = dict(TEST_QUOTA_RES)
res["id"] = quota.pk
res["items"] = [item.pk]
@@ -206,6 +207,16 @@ def test_quota_list(token_client, organizer, event, quota, item):
assert resp.status_code == 200
assert [res] == resp.data['results']
quota.subevent = subevent
quota.save()
res["subevent"] = subevent.pk
resp = token_client.get(
'/api/v1/organizers/{}/events/{}/quotas/?subevent={}'.format(organizer.slug, event.slug, subevent.pk))
assert [res] == resp.data['results']
resp = token_client.get(
'/api/v1/organizers/{}/events/{}/quotas/?subevent={}'.format(organizer.slug, event.slug, subevent.pk + 1))
assert [] == resp.data['results']
@pytest.mark.django_db
def test_quota_detail(token_client, organizer, event, quota, item):

View File

@@ -57,7 +57,8 @@ TEST_ORDERPOSITION_RES = {
"addon_to": None,
"checkins": [],
"downloads": [],
"answers": []
"answers": [],
"subevent": None
}
TEST_ORDER_RES = {
"code": "FOO",
@@ -142,7 +143,7 @@ def test_order_detail(token_client, organizer, event, order, item):
@pytest.mark.django_db
def test_orderposition_list(token_client, organizer, event, order, item):
def test_orderposition_list(token_client, organizer, event, order, item, subevent):
var = item.variations.create(value="Children")
res = dict(TEST_ORDERPOSITION_RES)
op = order.positions.first()
@@ -206,12 +207,24 @@ def test_orderposition_list(token_client, organizer, event, order, item):
'/api/v1/organizers/{}/events/{}/orderpositions/?has_checkin=true'.format(organizer.slug, event.slug))
assert [] == resp.data['results']
order.positions.first().checkins.create(datetime=datetime.datetime(2017, 12, 26, 10, 0, 0, tzinfo=UTC))
op.checkins.create(datetime=datetime.datetime(2017, 12, 26, 10, 0, 0, tzinfo=UTC))
res['checkins'] = [{'datetime': '2017-12-26T10:00:00Z'}]
resp = token_client.get(
'/api/v1/organizers/{}/events/{}/orderpositions/?has_checkin=true'.format(organizer.slug, event.slug))
assert [res] == resp.data['results']
op.subevent = subevent
op.save()
res['subevent'] = subevent.pk
resp = token_client.get(
'/api/v1/organizers/{}/events/{}/orderpositions/?subevent={}'.format(organizer.slug, event.slug, subevent.pk))
assert [res] == resp.data['results']
resp = token_client.get(
'/api/v1/organizers/{}/events/{}/orderpositions/?subevent={}'.format(organizer.slug, event.slug,
subevent.pk + 1))
assert [] == resp.data['results']
@pytest.mark.django_db
def test_orderposition_detail(token_client, organizer, event, order, item):

View File

@@ -0,0 +1,42 @@
import pytest
TEST_SUBEVENT_RES = {
'active': False,
'presale_start': None,
'date_to': None,
'date_admission': None,
'name': {'en': 'Foobar'},
'date_from': '2017-12-27T10:00:00Z',
'presale_end': None,
'id': 1,
'variation_price_overrides': [],
'location': None,
'item_price_overrides': []
}
@pytest.mark.django_db
def test_subevent_list(token_client, organizer, event, subevent):
res = dict(TEST_SUBEVENT_RES)
res["id"] = subevent.pk
resp = token_client.get('/api/v1/organizers/{}/events/{}/subevents/'.format(organizer.slug, event.slug))
assert resp.status_code == 200
print(dict(resp.data['results'][0]))
assert [res] == resp.data['results']
resp = token_client.get(
'/api/v1/organizers/{}/events/{}/subevents/?active=false'.format(organizer.slug, event.slug))
assert [res] == resp.data['results']
resp = token_client.get(
'/api/v1/organizers/{}/events/{}/subevents/?active=true'.format(organizer.slug, event.slug))
assert [] == resp.data['results']
@pytest.mark.django_db
def test_subevent_detail(token_client, organizer, event, subevent):
res = dict(TEST_SUBEVENT_RES)
res["id"] = subevent.pk
resp = token_client.get('/api/v1/organizers/{}/events/{}/subevents/{}/'.format(organizer.slug, event.slug,
subevent.pk))
assert resp.status_code == 200
assert res == resp.data

View File

@@ -35,12 +35,13 @@ TEST_VOUCHER_RES = {
'variation': None,
'quota': None,
'tag': 'Foo',
'comment': ''
'comment': '',
'subevent': None
}
@pytest.mark.django_db
def test_voucher_list(token_client, organizer, event, voucher, item, quota):
def test_voucher_list(token_client, organizer, event, voucher, item, quota, subevent):
res = dict(TEST_VOUCHER_RES)
res['item'] = item.pk
res['id'] = voucher.pk
@@ -187,6 +188,18 @@ def test_voucher_list(token_client, organizer, event, voucher, item, quota):
)
assert [res] == resp.data['results']
voucher.subevent = subevent
voucher.save()
res['subevent'] = subevent.pk
resp = token_client.get(
'/api/v1/organizers/{}/events/{}/vouchers/?subevent={}'.format(organizer.slug, event.slug, subevent.pk))
assert [res] == resp.data['results']
resp = token_client.get(
'/api/v1/organizers/{}/events/{}/vouchers/?subevent={}'.format(organizer.slug, event.slug,
subevent.pk + 1))
assert [] == resp.data['results']
@pytest.mark.django_db
def test_voucher_detail(token_client, organizer, event, voucher, item):

View File

@@ -28,12 +28,13 @@ TEST_WLE_RES = {
"voucher": None,
"item": 2,
"variation": None,
"locale": "en"
"locale": "en",
"subevent": None,
}
@pytest.mark.django_db
def test_wle_list(token_client, organizer, event, wle, item):
def test_wle_list(token_client, organizer, event, wle, item, subevent):
var = item.variations.create(value="Children")
res = dict(TEST_WLE_RES)
wle.variation = var
@@ -91,6 +92,18 @@ def test_wle_list(token_client, organizer, event, wle, item):
'/api/v1/organizers/{}/events/{}/waitinglistentries/?has_voucher=true'.format(organizer.slug, event.slug))
assert [res] == resp.data['results']
wle.subevent = subevent
wle.save()
res['subevent'] = subevent.pk
resp = token_client.get(
'/api/v1/organizers/{}/events/{}/waitinglistentries/?subevent={}'.format(organizer.slug, event.slug, subevent.pk))
assert [res] == resp.data['results']
resp = token_client.get(
'/api/v1/organizers/{}/events/{}/waitinglistentries/?subevent={}'.format(organizer.slug, event.slug,
subevent.pk + 1))
assert [] == resp.data['results']
@pytest.mark.django_db
def test_wle_detail(token_client, organizer, event, wle, item):

View File

@@ -1,9 +1,10 @@
import datetime
import sys
from datetime import timedelta
from datetime import date, timedelta
from decimal import Decimal
import pytest
import pytz
from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.files.storage import default_storage
@@ -15,6 +16,9 @@ from pretix.base.models import (
CachedFile, CartPosition, Event, Item, ItemCategory, ItemVariation, Order,
OrderPosition, Organizer, Question, Quota, User, Voucher, WaitingListEntry,
)
from pretix.base.models.event import SubEvent
from pretix.base.models.items import SubEventItem, SubEventItemVariation
from pretix.base.reldate import RelativeDate, RelativeDateWrapper
from pretix.base.services.orders import (
OrderError, cancel_order, mark_order_paid, perform_order,
)
@@ -378,6 +382,64 @@ class QuotaTestCase(BaseQuotaTestCase):
with self.assertNumQueries(1):
self.assertEqual(self.var1.check_quotas(_cache=cache, count_waitinglist=False), (Quota.AVAILABILITY_OK, 1))
def test_subevent_isolation(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(date_from=now(), name="SE 1")
se2 = self.event.subevents.create(date_from=now(), name="SE 2")
q1 = self.event.quotas.create(name="Q1", subevent=se1, size=50)
q2 = self.event.quotas.create(name="Q2", subevent=se2, size=50)
q1.items.add(self.item1)
q2.items.add(self.item1)
# Create orders
order = Order.objects.create(event=self.event, status=Order.STATUS_PAID,
expires=now() + timedelta(days=3),
total=6)
OrderPosition.objects.create(order=order, item=self.item1, subevent=se1, price=2)
OrderPosition.objects.create(order=order, item=self.item1, subevent=se1, price=2)
OrderPosition.objects.create(order=order, item=self.item1, subevent=se2, price=2)
order = Order.objects.create(event=self.event, status=Order.STATUS_PENDING,
expires=now() + timedelta(days=3),
total=8)
OrderPosition.objects.create(order=order, item=self.item1, subevent=se1, price=2)
OrderPosition.objects.create(order=order, item=self.item1, subevent=se1, price=2)
OrderPosition.objects.create(order=order, item=self.item1, subevent=se1, price=2)
OrderPosition.objects.create(order=order, item=self.item1, subevent=se2, price=2)
Voucher.objects.create(item=self.item1, event=self.event, valid_until=now() + timedelta(days=5),
block_quota=True, max_usages=6, subevent=se1)
Voucher.objects.create(item=self.item1, event=self.event, valid_until=now() + timedelta(days=5),
block_quota=True, max_usages=4, subevent=se2)
for i in range(8):
CartPosition.objects.create(event=self.event, item=self.item1, price=2, subevent=se1,
expires=now() + timedelta(days=3))
for i in range(5):
CartPosition.objects.create(event=self.event, item=self.item1, price=2, subevent=se2,
expires=now() + timedelta(days=3))
for i in range(16):
WaitingListEntry.objects.create(
event=self.event, item=self.item1, email='foo@bar.com', subevent=se1
)
for i in range(13):
WaitingListEntry.objects.create(
event=self.event, item=self.item1, email='foo@bar.com', subevent=se2
)
with self.assertRaises(TypeError):
self.item1.check_quotas()
self.assertEqual(self.item1.check_quotas(subevent=se1), (Quota.AVAILABILITY_OK, 50 - 5 - 6 - 8 - 16))
self.assertEqual(self.item1.check_quotas(subevent=se2), (Quota.AVAILABILITY_OK, 50 - 2 - 4 - 5 - 13))
self.assertEqual(q1.availability(), (Quota.AVAILABILITY_OK, 50 - 5 - 6 - 8 - 16))
self.assertEqual(q2.availability(), (Quota.AVAILABILITY_OK, 50 - 2 - 4 - 5 - 13))
self.event.has_subevents = False
self.event.save()
class WaitingListTestCase(BaseQuotaTestCase):
@@ -496,27 +558,27 @@ class VoucherTestCase(BaseQuotaTestCase):
def test_calculate_price_none(self):
v = Voucher.objects.create(event=self.event, price_mode='none', value=Decimal('10.00'))
v.calculate_price(Decimal('23.42')) == Decimal('23.42')
assert v.calculate_price(Decimal('23.42')) == Decimal('23.42')
def test_calculate_price_set_empty(self):
v = Voucher.objects.create(event=self.event, price_mode='set')
v.calculate_price(Decimal('23.42')) == Decimal('23.42')
assert v.calculate_price(Decimal('23.42')) == Decimal('23.42')
def test_calculate_price_set(self):
v = Voucher.objects.create(event=self.event, price_mode='set', value=Decimal('10.00'))
v.calculate_price(Decimal('23.42')) == Decimal('10.00')
assert v.calculate_price(Decimal('23.42')) == Decimal('10.00')
def test_calculate_price_set_zero(self):
v = Voucher.objects.create(event=self.event, price_mode='set', value=Decimal('0.00'))
v.calculate_price(Decimal('23.42')) == Decimal('0.00')
assert v.calculate_price(Decimal('23.42')) == Decimal('0.00')
def test_calculate_price_subtract(self):
v = Voucher.objects.create(event=self.event, price_mode='subtract', value=Decimal('10.00'))
v.calculate_price(Decimal('23.42')) == Decimal('13.42')
assert v.calculate_price(Decimal('23.42')) == Decimal('13.42')
def test_calculate_price_percent(self):
v = Voucher.objects.create(event=self.event, price_mode='percent', value=Decimal('23.00'))
v.calculate_price(Decimal('100.00')) == Decimal('77.00')
assert v.calculate_price(Decimal('100.00')) == Decimal('77.00')
class OrderTestCase(BaseQuotaTestCase):
@@ -529,10 +591,10 @@ class OrderTestCase(BaseQuotaTestCase):
expires=now() + timedelta(days=5), total=46
)
self.quota.items.add(self.item1)
OrderPosition.objects.create(order=self.order, item=self.item1,
variation=None, price=23)
OrderPosition.objects.create(order=self.order, item=self.item1,
variation=None, price=23)
self.op1 = OrderPosition.objects.create(order=self.order, item=self.item1,
variation=None, price=23)
self.op2 = OrderPosition.objects.create(order=self.order, item=self.item1,
variation=None, price=23)
def test_paid_in_time(self):
self.quota.size = 0
@@ -560,6 +622,29 @@ class OrderTestCase(BaseQuotaTestCase):
self.order = Order.objects.get(id=self.order.id)
self.assertEqual(self.order.status, Order.STATUS_EXPIRED)
def test_paid_expired_after_last_date_subevent_relative(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="SE1", date_from=now() + timedelta(days=10))
se2 = self.event.subevents.create(name="SE2", date_from=now() + timedelta(days=1))
self.op1.subevent = se1
self.op1.save()
self.op2.subevent = se2
self.op2.save()
self.event.settings.set('payment_term_last', RelativeDateWrapper(
RelativeDate(days_before=2, time=None, base_date_name='date_from')
))
self.order.status = Order.STATUS_EXPIRED
self.order.expires = now() - timedelta(days=2)
self.order.save()
with self.assertRaises(Quota.QuotaExceededException):
mark_order_paid(self.order)
self.order = Order.objects.get(id=self.order.id)
self.assertEqual(self.order.status, Order.STATUS_EXPIRED)
self.event.has_subevents = False
self.event.save()
def test_paid_expired_late_not_allowed(self):
self.event.settings.payment_term_accept_late = False
self.order.status = Order.STATUS_EXPIRED
@@ -615,6 +700,86 @@ class OrderTestCase(BaseQuotaTestCase):
self.event.settings.set('last_order_modification_date', now() - timedelta(days=1))
assert not self.order.can_modify_answers
def test_can_modify_answers_subevent(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="SE1", date_from=now() + timedelta(days=10))
se2 = self.event.subevents.create(name="SE2", date_from=now() + timedelta(days=8))
se3 = self.event.subevents.create(name="SE2", date_from=now() + timedelta(days=1))
self.op1.subevent = se1
self.op1.save()
self.op2.subevent = se2
self.op2.save()
self.event.settings.set('last_order_modification_date', RelativeDateWrapper(
RelativeDate(days_before=2, time=None, base_date_name='date_from')
))
assert self.order.can_modify_answers
self.op2.subevent = se3
self.op2.save()
assert not self.order.can_modify_answers
self.event.has_subevents = False
self.event.save()
def test_payment_term_last_relative(self):
self.event.settings.set('payment_term_last', date(2017, 5, 3))
assert self.order.payment_term_last == datetime.datetime(2017, 5, 3, 23, 59, 59, tzinfo=pytz.UTC)
self.event.date_from = datetime.datetime(2017, 5, 3, 12, 0, 0, tzinfo=pytz.UTC)
self.event.save()
self.event.settings.set('payment_term_last', RelativeDateWrapper(
RelativeDate(days_before=2, time=None, base_date_name='date_from')
))
assert self.order.payment_term_last == datetime.datetime(2017, 5, 1, 23, 59, 59, tzinfo=pytz.UTC)
def test_payment_term_last_subevent(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="SE1", date_from=now() + timedelta(days=10))
se2 = self.event.subevents.create(name="SE2", date_from=now() + timedelta(days=8))
se3 = self.event.subevents.create(name="SE2", date_from=now() + timedelta(days=1))
self.op1.subevent = se1
self.op1.save()
self.op2.subevent = se2
self.op2.save()
self.event.settings.set('payment_term_last', RelativeDateWrapper(
RelativeDate(days_before=2, time=None, base_date_name='date_from')
))
assert self.order.payment_term_last > now()
self.op2.subevent = se3
self.op2.save()
assert self.order.payment_term_last < now()
self.event.has_subevents = False
self.event.save()
def test_ticket_download_date_relative(self):
self.event.settings.set('ticket_download_date', datetime.datetime(2017, 5, 3, 12, 59, 59, tzinfo=pytz.UTC))
assert self.order.ticket_download_date == datetime.datetime(2017, 5, 3, 12, 59, 59, tzinfo=pytz.UTC)
self.event.date_from = datetime.datetime(2017, 5, 3, 12, 0, 0, tzinfo=pytz.UTC)
self.event.save()
self.event.settings.set('ticket_download_date', RelativeDateWrapper(
RelativeDate(days_before=2, time=None, base_date_name='date_from')
))
assert self.order.ticket_download_date == datetime.datetime(2017, 5, 1, 12, 0, 0, tzinfo=pytz.UTC)
def test_ticket_download_date_subevent(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="SE1", date_from=now() + timedelta(days=10))
se2 = self.event.subevents.create(name="SE2", date_from=now() + timedelta(days=8))
se3 = self.event.subevents.create(name="SE2", date_from=now() + timedelta(days=1))
self.op1.subevent = se1
self.op1.save()
self.op2.subevent = se2
self.op2.save()
self.event.settings.set('ticket_download_date', RelativeDateWrapper(
RelativeDate(days_before=2, time=None, base_date_name='date_from')
))
assert self.order.ticket_download_date > now()
self.op2.subevent = se3
self.op2.save()
assert self.order.ticket_download_date < now()
self.event.has_subevents = False
self.event.save()
def test_can_cancel_order(self):
item1 = Item.objects.create(event=self.event, name="Ticket", default_price=23,
admission=True, allow_cancel=True)
@@ -771,6 +936,41 @@ class EventTest(TestCase):
self.assertIn('slug', str(context.exception))
class SubEventTest(TestCase):
@classmethod
def setUpTestData(cls):
cls.organizer = Organizer.objects.create(name='Dummy', slug='dummy')
cls.event = Event.objects.create(
organizer=cls.organizer, name='Dummy', slug='dummy',
date_from=now(), date_to=now() - timedelta(hours=1),
has_subevents=True
)
cls.se = SubEvent.objects.create(
name='Testsub', date_from=now(), event=cls.event
)
def test_override_prices(self):
i = Item.objects.create(
event=self.event, name="Ticket", default_price=23,
active=True, available_until=now() + timedelta(days=1),
)
SubEventItem.objects.create(item=i, subevent=self.se, price=Decimal('30.00'))
assert self.se.item_price_overrides == {
i.pk: Decimal('30.00')
}
def test_override_var_prices(self):
i = Item.objects.create(
event=self.event, name="Ticket", default_price=23,
active=True, available_until=now() + timedelta(days=1),
)
v = i.variations.create(value='Type 1')
SubEventItemVariation.objects.create(variation=v, subevent=self.se, price=Decimal('30.00'))
assert self.se.var_price_overrides == {
v.pk: Decimal('30.00')
}
class CachedFileTestCase(TestCase):
def test_file_handling(self):
cf = CachedFile()

View File

@@ -7,8 +7,12 @@ from django.test import TestCase
from django.utils.timezone import make_aware, now
from pretix.base.decimal import round_decimal
from pretix.base.models import Event, Item, Order, OrderPosition, Organizer
from pretix.base.models import (
CartPosition, Event, Item, Order, OrderPosition, Organizer,
)
from pretix.base.models.items import SubEventItem
from pretix.base.payment import FreeOrderProvider
from pretix.base.reldate import RelativeDate, RelativeDateWrapper
from pretix.base.services.orders import (
OrderChangeManager, OrderError, _create_order, expire_orders,
)
@@ -71,6 +75,52 @@ def test_expiry_last(event):
assert (order.expires - today).days == 5
@pytest.mark.django_db
def test_expiry_last_relative(event):
today = now()
event.settings.set('payment_term_days', 5)
event.settings.set('payment_term_weekdays', False)
event.date_from = now() + timedelta(days=5)
event.save()
event.settings.set('payment_term_last', RelativeDateWrapper(
RelativeDate(days_before=2, time=None, base_date_name='date_from')
))
order = _create_order(event, email='dummy@example.org', positions=[],
now_dt=today, payment_provider=FreeOrderProvider(event),
locale='de')
assert (order.expires - today).days == 3
@pytest.mark.django_db
def test_expiry_last_relative_subevents(event):
today = now()
event.settings.set('payment_term_days', 100)
event.settings.set('payment_term_weekdays', False)
event.date_from = now() + timedelta(days=5)
event.has_subevents = True
event.save()
ticket = Item.objects.create(event=event, name='Early-bird ticket', tax_rate=Decimal('7.00'),
default_price=Decimal('23.00'), admission=True)
se1 = event.subevents.create(name="SE1", date_from=now() + timedelta(days=10))
se2 = event.subevents.create(name="SE2", date_from=now() + timedelta(days=8))
cp1 = CartPosition.objects.create(
item=ticket, price=23, expires=now() + timedelta(days=1), subevent=se1, event=event, cart_id="123"
)
cp2 = CartPosition.objects.create(
item=ticket, price=23, expires=now() + timedelta(days=1), subevent=se2, event=event, cart_id="123"
)
event.settings.set('payment_term_last', RelativeDateWrapper(
RelativeDate(days_before=2, time=None, base_date_name='date_from')
))
order = _create_order(event, email='dummy@example.org', positions=[cp1, cp2],
now_dt=today, payment_provider=FreeOrderProvider(event),
locale='de')
assert (order.expires - today).days == 6
@pytest.mark.django_db
def test_expiry_dst(event):
event.settings.set('timezone', 'Europe/Berlin')
@@ -153,6 +203,63 @@ class OrderChangeManagerTests(TestCase):
price=Decimal("23.00"), attendee_name="Dieter", positionid=2
)
self.ocm = OrderChangeManager(self.order, None)
self.quota = self.event.quotas.create(name='Test', size=None)
self.quota.items.add(self.ticket)
self.quota.items.add(self.ticket2)
self.quota.items.add(self.shirt)
def test_change_subevent_quota_required(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="Foo", date_from=now())
se2 = self.event.subevents.create(name="Bar", date_from=now())
self.op1.subevent = se1
self.op1.save()
self.quota.subevent = se1
self.quota.save()
with self.assertRaises(OrderError):
self.ocm.change_subevent(self.op1, se2)
def test_change_subevent_success(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="Foo", date_from=now())
se2 = self.event.subevents.create(name="Bar", date_from=now())
SubEventItem.objects.create(subevent=se2, item=self.ticket, price=12)
self.op1.subevent = se1
self.op1.save()
self.quota.subevent = se2
self.quota.save()
self.ocm.change_subevent(self.op1, se2)
self.ocm.commit()
self.op1.refresh_from_db()
self.order.refresh_from_db()
assert self.op1.subevent == se2
assert self.op1.price == 12
assert self.order.total == self.op1.price + self.op2.price
def test_change_subevent_sold_out(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="Foo", date_from=now())
se2 = self.event.subevents.create(name="Bar", date_from=now())
self.op1.subevent = se1
self.op1.save()
self.quota.subevent = se2
self.quota.size = 0
self.quota.save()
self.ocm.change_subevent(self.op1, se2)
with self.assertRaises(OrderError):
self.ocm.commit()
self.op1.refresh_from_db()
assert self.op1.subevent == se1
def test_change_item_quota_required(self):
self.quota.delete()
with self.assertRaises(OrderError):
self.ocm.change_item(self.op1, self.shirt, None)
def test_change_item_success(self):
self.ocm.change_item(self.op1, self.shirt, None)
@@ -325,6 +432,11 @@ class OrderChangeManagerTests(TestCase):
assert self.order.total == 46
assert self.order.status == Order.STATUS_PAID
def test_add_item_quota_required(self):
self.quota.delete()
with self.assertRaises(OrderError):
self.ocm.add_position(self.shirt, None, None, None)
def test_add_item_success(self):
self.ocm.add_position(self.shirt, None, None, None)
self.ocm.commit()
@@ -375,3 +487,26 @@ class OrderChangeManagerTests(TestCase):
self.shirt.category = self.event.categories.create(name='Add-ons', is_addon=True)
with self.assertRaises(OrderError):
self.ocm.add_position(self.shirt, None, Decimal('13.00'), None)
def test_add_item_subevent_required(self):
self.event.has_subevents = True
self.event.save()
with self.assertRaises(OrderError):
self.ocm.add_position(self.ticket, None, None, None)
def test_add_item_subevent_price(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="Foo", date_from=now())
SubEventItem.objects.create(subevent=se1, item=self.ticket, price=12)
self.quota.subevent = se1
self.quota.save()
self.ocm.add_position(self.ticket, None, None, subevent=se1)
self.ocm.commit()
self.order.refresh_from_db()
assert self.order.positions.count() == 3
nop = self.order.positions.last()
assert nop.item == self.ticket
assert nop.price == Decimal('12.00')
assert nop.subevent == se1

View File

@@ -6,7 +6,10 @@ import pytz
from django.utils.timezone import now
from tests.testdummy.payment import DummyPaymentProvider
from pretix.base.models import Event, Organizer
from pretix.base.models import (
CartPosition, Event, Item, Order, OrderPosition, Organizer,
)
from pretix.base.reldate import RelativeDate, RelativeDateWrapper
@pytest.fixture
@@ -70,6 +73,23 @@ def test_availability_date_not_available(event):
assert not result
@pytest.mark.django_db
def test_availability_date_relative(event):
event.settings.set('timezone', 'US/Pacific')
tz = pytz.timezone('US/Pacific')
event.date_from = tz.localize(datetime.datetime(2016, 12, 3, 12, 0, 0))
event.save()
prov = DummyPaymentProvider(event)
prov.settings.set('_availability_date', RelativeDateWrapper(
RelativeDate(days_before=2, time=None, base_date_name='date_from')
))
utc = pytz.timezone('UTC')
assert prov._is_still_available(tz.localize(datetime.datetime(2016, 11, 30, 23, 0, 0)).astimezone(utc))
assert prov._is_still_available(tz.localize(datetime.datetime(2016, 12, 1, 23, 59, 0)).astimezone(utc))
assert not prov._is_still_available(tz.localize(datetime.datetime(2016, 12, 2, 0, 0, 1)).astimezone(utc))
@pytest.mark.django_db
def test_availability_date_timezones(event):
event.settings.set('timezone', 'US/Pacific')
@@ -81,3 +101,71 @@ def test_availability_date_timezones(event):
assert prov._is_still_available(tz.localize(datetime.datetime(2016, 11, 30, 23, 0, 0)).astimezone(utc))
assert prov._is_still_available(tz.localize(datetime.datetime(2016, 12, 1, 23, 59, 0)).astimezone(utc))
assert not prov._is_still_available(tz.localize(datetime.datetime(2016, 12, 2, 0, 0, 1)).astimezone(utc))
@pytest.mark.django_db
def test_availability_date_cart_relative_subevents(event):
event.date_from = now() + datetime.timedelta(days=5)
event.has_subevents = True
event.save()
ticket = Item.objects.create(event=event, name='Early-bird ticket', tax_rate=Decimal('7.00'),
default_price=Decimal('23.00'), admission=True)
se1 = event.subevents.create(name="SE1", date_from=now() + datetime.timedelta(days=10))
se2 = event.subevents.create(name="SE2", date_from=now() + datetime.timedelta(days=3))
CartPosition.objects.create(
item=ticket, price=23, expires=now() + datetime.timedelta(days=1), subevent=se1, event=event, cart_id="123"
)
CartPosition.objects.create(
item=ticket, price=23, expires=now() + datetime.timedelta(days=1), subevent=se2, event=event, cart_id="123"
)
prov = DummyPaymentProvider(event)
prov.settings.set('_availability_date', RelativeDateWrapper(
RelativeDate(days_before=3, time=None, base_date_name='date_from')
))
assert prov._is_still_available(cart_id="123")
prov.settings.set('_availability_date', RelativeDateWrapper(
RelativeDate(days_before=4, time=None, base_date_name='date_from')
))
assert not prov._is_still_available(cart_id="123")
@pytest.mark.django_db
def test_availability_date_order_relative_subevents(event):
event.date_from = now() + datetime.timedelta(days=5)
event.has_subevents = True
event.save()
ticket = Item.objects.create(event=event, name='Early-bird ticket', tax_rate=Decimal('7.00'),
default_price=Decimal('23.00'), admission=True)
se1 = event.subevents.create(name="SE1", date_from=now() + datetime.timedelta(days=10))
se2 = event.subevents.create(name="SE2", date_from=now() + datetime.timedelta(days=3))
order = Order.objects.create(
code='FOO', event=event, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now() + datetime.timedelta(days=10),
total=Decimal('46.00'), payment_provider='dummtest'
)
OrderPosition.objects.create(
order=order, item=ticket, variation=None, subevent=se1,
price=Decimal("23.00"), attendee_name="Peter", positionid=1
)
OrderPosition.objects.create(
order=order, item=ticket, variation=None, subevent=se2,
price=Decimal("23.00"), attendee_name="Dieter", positionid=2
)
prov = DummyPaymentProvider(event)
prov.settings.set('_availability_date', RelativeDateWrapper(
RelativeDate(days_before=3, time=None, base_date_name='date_from')
))
assert prov._is_still_available(order=order)
prov.settings.set('_availability_date', RelativeDateWrapper(
RelativeDate(days_before=4, time=None, base_date_name='date_from')
))
assert not prov._is_still_available(order=order)

View File

@@ -0,0 +1,195 @@
from decimal import Decimal
import pytest
from django.utils.timezone import now
from pretix.base.models import Event, Organizer
from pretix.base.models.items import SubEventItem, SubEventItemVariation
from pretix.base.services.pricing import get_price
@pytest.fixture
def event():
o = Organizer.objects.create(name='Dummy', slug='dummy')
event = Event.objects.create(
organizer=o, name='Dummy', slug='dummy',
date_from=now()
)
return event
@pytest.fixture
def item(event):
return event.items.create(name='Ticket', default_price=Decimal('23.00'))
@pytest.fixture
def variation(item):
return item.variations.create(value='Premium', default_price=None)
@pytest.fixture
def voucher(event):
return event.vouchers.create()
@pytest.fixture
def subevent(event):
event.has_subevents = True
event.save()
return event.subevents.create(name='Foobar', date_from=now())
@pytest.mark.django_db
def test_base_item_default(item):
assert get_price(item) == Decimal('23.00')
@pytest.mark.django_db
def test_base_item_subevent_no_entry(item, subevent):
assert get_price(item, subevent=subevent) == Decimal('23.00')
@pytest.mark.django_db
def test_base_item_subevent_no_override(item, subevent):
SubEventItem.objects.create(item=item, subevent=subevent, price=None)
assert get_price(item, subevent=subevent) == Decimal('23.00')
@pytest.mark.django_db
def test_base_item_subevent_override(item, subevent):
SubEventItem.objects.create(item=item, subevent=subevent, price=Decimal('24.00'))
assert get_price(item, subevent=subevent) == Decimal('24.00')
@pytest.mark.django_db
def test_variation_with_default_item_price(item, variation):
assert get_price(item, variation=variation) == Decimal('23.00')
@pytest.mark.django_db
def test_variation_with_specific_price(item, variation):
variation.default_price = Decimal('24.00')
assert get_price(item, variation=variation) == Decimal('24.00')
@pytest.mark.django_db
def test_variation_with_default_subevent_and_default_price(item, subevent, variation):
SubEventItemVariation.objects.create(variation=variation, subevent=subevent, price=None)
assert get_price(item, variation=variation, subevent=subevent) == Decimal('23.00')
@pytest.mark.django_db
def test_variation_with_subevent_and_default_price(item, subevent, variation):
SubEventItemVariation.objects.create(variation=variation, subevent=subevent, price=Decimal('24.00'))
assert get_price(item, variation=variation, subevent=subevent) == Decimal('24.00')
@pytest.mark.django_db
def test_variation_with_no_subevent_and_specific_price(item, subevent, variation):
variation.default_price = Decimal('24.00')
assert get_price(item, variation=variation, subevent=subevent) == Decimal('24.00')
@pytest.mark.django_db
def test_variation_with_default_subevent_and_specific_price(item, subevent, variation):
variation.default_price = Decimal('24.00')
SubEventItemVariation.objects.create(variation=variation, subevent=subevent, price=None)
assert get_price(item, variation=variation, subevent=subevent) == Decimal('24.00')
@pytest.mark.django_db
def test_variation_with_subevent_and_specific_price(item, subevent, variation):
variation.default_price = Decimal('24.00')
SubEventItemVariation.objects.create(variation=variation, subevent=subevent, price=Decimal('26.00'))
assert get_price(item, variation=variation, subevent=subevent) == Decimal('26.00')
@pytest.mark.django_db
def test_voucher_no_override(item, subevent, voucher):
assert get_price(item, subevent=subevent, voucher=voucher) == Decimal('23.00')
@pytest.mark.django_db
def test_voucher_set_price(item, subevent, voucher):
voucher.price_mode = 'set'
voucher.value = Decimal('12.00')
assert get_price(item, subevent=subevent, voucher=voucher) == Decimal('12.00')
@pytest.mark.django_db
def test_voucher_subtract(item, subevent, voucher):
voucher.price_mode = 'subtract'
voucher.value = Decimal('12.00')
assert get_price(item, subevent=subevent, voucher=voucher) == Decimal('11.00')
@pytest.mark.django_db
def test_voucher_percent(item, subevent, voucher):
voucher.price_mode = 'percent'
voucher.value = Decimal('10.00')
assert get_price(item, subevent=subevent, voucher=voucher) == Decimal('20.70')
@pytest.mark.django_db
def test_free_price_ignored_if_disabled(item):
assert get_price(item, custom_price=Decimal('42.00')) == Decimal('23.00')
@pytest.mark.django_db
def test_free_price_ignored_if_lower(item):
item.free_price = True
assert get_price(item, custom_price=Decimal('12.00')) == Decimal('23.00')
@pytest.mark.django_db
def test_free_price_ignored_if_lower_than_voucher(item, voucher):
voucher.price_mode = 'set'
voucher.value = Decimal('50.00')
assert get_price(item, voucher=voucher, custom_price=Decimal('40.00')) == Decimal('50.00')
@pytest.mark.django_db
def test_free_price_ignored_if_lower_than_subevent(item, subevent):
item.free_price = True
SubEventItem.objects.create(item=item, subevent=subevent, price=Decimal('50.00'))
assert get_price(item, subevent=subevent, custom_price=Decimal('40.00')) == Decimal('50.00')
@pytest.mark.django_db
def test_free_price_ignored_if_lower_than_variation(item, variation):
variation.default_price = Decimal('50.00')
item.free_price = True
assert get_price(item, variation=variation, custom_price=Decimal('40.00')) == Decimal('50.00')
@pytest.mark.django_db
def test_free_price_accepted(item):
item.free_price = True
assert get_price(item, custom_price=Decimal('42.00')) == Decimal('42.00')
@pytest.mark.django_db
def test_free_price_string(item):
item.free_price = True
assert get_price(item, custom_price='42,00') == Decimal('42.00')
@pytest.mark.django_db
def test_free_price_float(item):
item.free_price = True
assert get_price(item, custom_price=42.00) == Decimal('42.00')
@pytest.mark.django_db
def test_free_price_limit(item):
item.free_price = True
with pytest.raises(ValueError):
get_price(item, custom_price=Decimal('200000000'))
@pytest.mark.django_db
def test_free_price_net(item):
item.free_price = True
item.tax_rate = 19
assert get_price(item, custom_price=Decimal('100.00'), custom_price_is_net=True) == Decimal('119.00')

View File

@@ -0,0 +1,82 @@
from datetime import datetime, time
import pytest
import pytz
from pretix.base.models import Event, Organizer
from pretix.base.reldate import RelativeDate, RelativeDateWrapper
TOKYO = pytz.timezone('Asia/Tokyo')
@pytest.fixture
def event():
o = Organizer.objects.create(name='Dummy', slug='dummy')
event = Event.objects.create(
organizer=o, name='Dummy', slug='dummy',
date_from=datetime(2017, 12, 27, 5, 0, 0, tzinfo=TOKYO),
presale_start=datetime(2017, 12, 1, 5, 0, 0, tzinfo=TOKYO),
plugins='pretix.plugins.banktransfer'
)
event.settings.timezone = "Asia/Tokyo"
return event
@pytest.mark.django_db
def test_absolute_date(event):
d = datetime(2017, 12, 25, 5, 0, 0, tzinfo=TOKYO)
rdw = RelativeDateWrapper(d)
assert rdw.datetime(event) == d
assert rdw.to_string() == d.isoformat()
@pytest.mark.django_db
def test_relative_date_without_time(event):
rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=None, base_date_name='date_from'))
assert rdw.datetime(event).astimezone(TOKYO) == datetime(2017, 12, 26, 5, 0, 0, tzinfo=TOKYO)
assert rdw.to_string() == 'RELDATE/1/-/date_from/'
@pytest.mark.django_db
def test_relative_date_other_base_point(event):
rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=None, base_date_name='presale_start'))
assert rdw.datetime(event) == datetime(2017, 11, 30, 5, 0, 0, tzinfo=TOKYO)
assert rdw.to_string() == 'RELDATE/1/-/presale_start/'
# presale_end is unset, defaults to date_from
rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=None, base_date_name='presale_end'))
assert rdw.datetime(event) == datetime(2017, 12, 26, 5, 0, 0, tzinfo=TOKYO)
assert rdw.to_string() == 'RELDATE/1/-/presale_end/'
# subevent base
se = event.subevents.create(name="SE1", date_from=datetime(2017, 11, 27, 5, 0, 0, tzinfo=TOKYO))
rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=None, base_date_name='date_from'))
assert rdw.datetime(se) == datetime(2017, 11, 26, 5, 0, 0, tzinfo=TOKYO)
# presale_start is unset on subevent, default to event
rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=None, base_date_name='presale_start'))
assert rdw.datetime(se) == datetime(2017, 11, 30, 5, 0, 0, tzinfo=TOKYO)
# presale_end is unset on all, default to date_from of subevent
rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=None, base_date_name='presale_end'))
assert rdw.datetime(se) == datetime(2017, 11, 26, 5, 0, 0, tzinfo=TOKYO)
@pytest.mark.django_db
def test_relative_date_with_time(event):
rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=time(8, 5, 13), base_date_name='date_from'))
assert rdw.to_string() == 'RELDATE/1/08:05:13/date_from/'
assert rdw.datetime(event) == datetime(2017, 12, 26, 8, 5, 13, tzinfo=TOKYO)
def test_unserialize():
d = datetime(2017, 12, 25, 10, 0, 0, tzinfo=TOKYO)
rdw = RelativeDateWrapper.from_string(d.isoformat())
assert rdw.data == d
rdw = RelativeDateWrapper.from_string('RELDATE/1/-/date_from/')
assert rdw.data == RelativeDate(days_before=1, time=None, base_date_name='date_from')
rdw = RelativeDateWrapper.from_string('RELDATE/1/18:05:13/date_from/')
assert rdw.data == RelativeDate(days_before=1, time=time(18, 5, 13), base_date_name='date_from')

View File

@@ -2,11 +2,15 @@ import datetime
from decimal import Decimal
import pytz
from django.utils.timezone import now
from i18nfield.strings import LazyI18nString
from pytz import timezone
from tests.base import SoupTest, extract_form_fields
from pretix.base.models import Event, Organizer, Team, User
from pretix.base.models import (
Event, Order, OrderPosition, Organizer, Team, User,
)
from pretix.base.models.items import SubEventItem
from pretix.testutils.mock import mocker_context
@@ -164,7 +168,10 @@ class EventsTest(SoupTest):
'payment_banktransfer__fee_abs': '12.23',
'payment_banktransfer_bank_details_0': 'Test',
'settings-payment_term_days': '2',
'settings-payment_term_last': (self.event1.presale_end - datetime.timedelta(1)).strftime('%Y-%m-%d'),
'settings-payment_term_last_0': 'absolute',
'settings-payment_term_last_1': (self.event1.presale_end - datetime.timedelta(1)).strftime('%Y-%m-%d'),
'settings-payment_term_last_2': '0',
'settings-payment_term_last_3': 'date_from',
'settings-tax_rate_default': '19.00',
})
assert doc.select('.alert-danger')
@@ -327,6 +334,41 @@ class EventsTest(SoupTest):
assert ev.presale_start == berlin_tz.localize(datetime.datetime(2016, 11, 1, 10, 0, 0)).astimezone(pytz.utc)
assert ev.presale_end == berlin_tz.localize(datetime.datetime(2016, 11, 30, 18, 0, 0)).astimezone(pytz.utc)
def test_create_event_with_subevents_success(self):
doc = self.get_doc('/control/events/add')
tabletext = doc.select("form")[0].text
self.assertIn("CCC", tabletext)
self.assertNotIn("MRM", tabletext)
doc = self.post_doc('/control/events/add', {
'event_wizard-current_step': 'foundation',
'foundation-organizer': self.orga1.pk,
'foundation-locales': ('en', 'de'),
'foundation-has_subevents': 'on',
})
doc = self.post_doc('/control/events/add', {
'event_wizard-current_step': 'basics',
'basics-name_0': '33C3',
'basics-name_1': '33C3',
'basics-slug': '33c3',
'basics-date_from': '2016-12-27 10:00:00',
'basics-date_to': '2016-12-30 19:00:00',
'basics-location_0': 'Hamburg',
'basics-location_1': 'Hamburg',
'basics-currency': 'EUR',
'basics-locale': 'en',
'basics-timezone': 'Europe/Berlin',
'basics-presale_start': '2016-11-01 10:00:00',
'basics-presale_end': '2016-11-30 18:00:00',
})
self.post_doc('/control/events/add', {
'event_wizard-current_step': 'copy',
'copy-copy_from_event': ''
})
ev = Event.objects.get(slug='33c3')
assert ev.has_subevents
assert ev.subevents.count() == 1
def test_create_event_only_date_from(self):
# date_to, presale_start & presale_end are optional fields
self.post_doc('/control/events/add', {
@@ -431,3 +473,132 @@ class EventsTest(SoupTest):
'basics-presale_end': '2016-11-30 18:00:00',
})
assert doc.select(".alert-danger")
class SubEventsTest(SoupTest):
def setUp(self):
super().setUp()
self.user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
self.orga1 = Organizer.objects.create(name='CCC', slug='ccc')
self.event1 = Event.objects.create(
organizer=self.orga1, name='30C3', slug='30c3',
date_from=datetime.datetime(2013, 12, 26, tzinfo=datetime.timezone.utc),
plugins='pretix.plugins.banktransfer,tests.testdummy',
has_subevents=True
)
t = Team.objects.create(organizer=self.orga1, can_create_events=True, can_change_event_settings=True,
can_change_items=True)
t.members.add(self.user)
t.limit_events.add(self.event1)
self.ticket = self.event1.items.create(name='Early-bird ticket',
category=None, default_price=23,
admission=True)
self.client.login(email='dummy@dummy.dummy', password='dummy')
self.subevent1 = self.event1.subevents.create(name='SE1', date_from=now())
def test_list(self):
doc = self.get_doc('/control/event/ccc/30c3/subevents/')
tabletext = doc.select("#page-wrapper .table")[0].text
self.assertIn("SE1", tabletext)
def test_create(self):
doc = self.get_doc('/control/event/ccc/30c3/subevents/add')
assert doc.select("input[name=quotas-TOTAL_FORMS]")
doc = self.post_doc('/control/event/ccc/30c3/subevents/add', {
'name_0': 'SE2',
'active': 'on',
'date_from': '2017-07-01 10:00:00',
'date_to': '2017-07-01 12:00:00',
'location_0': 'Hamburg',
'presale_start': '2017-06-20 10:00:00',
'quotas-TOTAL_FORMS': '1',
'quotas-INITIAL_FORMS': '0',
'quotas-MIN_NUM_FORMS': '0',
'quotas-MAX_NUM_FORMS': '1000',
'quotas-0-name': 'Q1',
'quotas-0-size': '50',
'quotas-0-itemvars': str(self.ticket.pk),
'item-%d-price' % self.ticket.pk: '12'
})
assert doc.select(".alert-success")
se = self.event1.subevents.first()
assert str(se.name) == "SE2"
assert se.active
assert se.date_from.isoformat() == "2017-07-01T10:00:00+00:00"
assert se.date_to.isoformat() == "2017-07-01T12:00:00+00:00"
assert str(se.location) == "Hamburg"
assert se.presale_start.isoformat() == "2017-06-20T10:00:00+00:00"
assert not se.presale_end
assert se.quotas.count() == 1
q = se.quotas.last()
assert q.name == "Q1"
assert q.size == 50
assert list(q.items.all()) == [self.ticket]
sei = SubEventItem.objects.get(subevent=se, item=self.ticket)
assert sei.price == 12
def test_modify(self):
doc = self.get_doc('/control/event/ccc/30c3/subevents/%d/' % self.subevent1.pk)
assert doc.select("input[name=quotas-TOTAL_FORMS]")
doc = self.post_doc('/control/event/ccc/30c3/subevents/%d/' % self.subevent1.pk, {
'name_0': 'SE2',
'active': 'on',
'date_from': '2017-07-01 10:00:00',
'date_to': '2017-07-01 12:00:00',
'location_0': 'Hamburg',
'presale_start': '2017-06-20 10:00:00',
'quotas-TOTAL_FORMS': '1',
'quotas-INITIAL_FORMS': '0',
'quotas-MIN_NUM_FORMS': '0',
'quotas-MAX_NUM_FORMS': '1000',
'quotas-0-name': 'Q1',
'quotas-0-size': '50',
'quotas-0-itemvars': str(self.ticket.pk),
'item-%d-price' % self.ticket.pk: '12'
})
assert doc.select(".alert-success")
self.subevent1.refresh_from_db()
se = self.subevent1
assert str(se.name) == "SE2"
assert se.active
assert se.date_from.isoformat() == "2017-07-01T10:00:00+00:00"
assert se.date_to.isoformat() == "2017-07-01T12:00:00+00:00"
assert str(se.location) == "Hamburg"
assert se.presale_start.isoformat() == "2017-06-20T10:00:00+00:00"
assert not se.presale_end
assert se.quotas.count() == 1
q = se.quotas.last()
assert q.name == "Q1"
assert q.size == 50
assert list(q.items.all()) == [self.ticket]
sei = SubEventItem.objects.get(subevent=se, item=self.ticket)
assert sei.price == 12
def test_delete(self):
doc = self.get_doc('/control/event/ccc/30c3/subevents/%d/delete' % self.subevent1.pk)
assert doc.select("button")
doc = self.post_doc('/control/event/ccc/30c3/subevents/%d/delete' % self.subevent1.pk, {})
assert doc.select(".alert-success")
assert not SubEventItem.objects.filter(pk=self.subevent1.pk).exists()
def test_delete_with_orders(self):
o = Order.objects.create(
code='FOO', event=self.event1, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now() + datetime.timedelta(days=10),
total=14, payment_provider='banktransfer', locale='en'
)
OrderPosition.objects.create(
order=o,
item=self.ticket,
subevent=self.subevent1,
price=Decimal("14"),
)
doc = self.get_doc('/control/event/ccc/30c3/subevents/%d/delete' % self.subevent1.pk, follow=True)
assert doc.select(".alert-danger")
doc = self.post_doc('/control/event/ccc/30c3/subevents/%d/delete' % self.subevent1.pk, {}, follow=True)
assert doc.select(".alert-danger")
assert self.event1.subevents.filter(pk=self.subevent1.pk).exists()

View File

@@ -231,7 +231,7 @@ class QuotaTest(ItemFormTest):
ItemVariation.objects.create(item=item2, value="Silver")
ItemVariation.objects.create(item=item2, value="Gold")
doc = self.get_doc('/control/event/%s/%s/quotas/%s/change' % (self.orga1.slug, self.event1.slug, c.id))
doc.select('[name=item_%s]' % item1.id)[0]['checked'] = 'checked'
[i for i in doc.select('[name=itemvars]') if i.get('value') == str(item1.id)][0]['checked'] = 'checked'
form_data = extract_form_fields(doc.select('.container-fluid form')[0])
form_data['size'] = '350'
doc = self.post_doc('/control/event/%s/%s/quotas/%s/change' % (self.orga1.slug, self.event1.slug, c.id),
@@ -242,6 +242,19 @@ class QuotaTest(ItemFormTest):
assert Quota.objects.get(id=c.id).size == 350
assert item1 in Quota.objects.get(id=c.id).items.all()
def test_update_subevent(self):
self.event1.has_subevents = True
self.event1.save()
se1 = self.event1.subevents.create(name="Foo", date_from=now())
se2 = self.event1.subevents.create(name="Bar", date_from=now())
c = Quota.objects.create(event=self.event1, name="Full house", size=500, subevent=se1)
doc = self.get_doc('/control/event/%s/%s/quotas/%s/change' % (self.orga1.slug, self.event1.slug, c.id))
form_data = extract_form_fields(doc.select('.container-fluid form')[0])
form_data['subevent'] = se2.pk
self.post_doc('/control/event/%s/%s/quotas/%s/change' % (self.orga1.slug, self.event1.slug, c.id),
form_data)
assert Quota.objects.get(id=c.id).subevent == se2
def test_delete(self):
c = Quota.objects.create(event=self.event1, name="Full house", size=500)
doc = self.get_doc('/control/event/%s/%s/quotas/%s/delete' % (self.orga1.slug, self.event1.slug, c.id))

View File

@@ -476,6 +476,9 @@ class OrderChangeTests(SoupTest):
order=self.order, item=self.ticket, variation=None,
price=Decimal("23.00"), attendee_name="Dieter"
)
self.quota = self.event.quotas.create(name="All", size=100)
self.quota.items.add(self.ticket)
self.quota.items.add(self.shirt)
user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
t = Team.objects.create(organizer=o, can_view_orders=True, can_change_orders=True)
t.members.add(user)
@@ -499,6 +502,38 @@ class OrderChangeTests(SoupTest):
assert self.op1.tax_rate == self.shirt.tax_rate
assert self.order.total == self.op1.price + self.op2.price
def test_change_subevent_success(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name='Foo', date_from=now())
se2 = self.event.subevents.create(name='Bar', date_from=now())
self.op1.subevent = se1
self.op1.save()
self.op2.subevent = se1
self.op2.save()
self.quota.subevent = se1
self.quota.save()
q2 = self.event.quotas.create(name='Q2', size=100, subevent=se2)
q2.items.add(self.ticket)
q2.items.add(self.shirt)
self.client.post('/control/event/{}/{}/orders/{}/change'.format(
self.event.organizer.slug, self.event.slug, self.order.code
), {
'op-{}-operation'.format(self.op1.pk): 'subevent',
'op-{}-subevent'.format(self.op1.pk): str(se2.pk),
'op-{}-itemvar'.format(self.op1.pk): str(self.ticket.pk),
'op-{}-operation'.format(self.op2.pk): '',
'op-{}-itemvar'.format(self.op2.pk): str(self.ticket.pk),
'op-{}-subevent'.format(self.op2.pk): str(se1.pk),
'add-itemvar'.format(self.op2.pk): str(self.ticket.pk),
'add-subevent'.format(self.op2.pk): str(se1.pk),
})
self.op1.refresh_from_db()
self.op2.refresh_from_db()
self.order.refresh_from_db()
assert self.op1.subevent == se2
assert self.op2.subevent == se1
def test_change_price_success(self):
self.client.post('/control/event/{}/{}/orders/{}/change'.format(
self.event.organizer.slug, self.event.slug, self.order.code

View File

@@ -64,6 +64,10 @@ event_urls = [
"vouchers/add",
"vouchers/bulk_add",
"vouchers/rng",
"subevents/",
"subevents/add",
"subevents/2/delete",
"subevents/2/",
"quotas/",
"quotas/2/delete",
"quotas/2/change",
@@ -182,6 +186,10 @@ event_permission_urls = [
("can_change_items", "quotas/2/change", 404),
("can_change_items", "quotas/2/delete", 404),
("can_change_items", "quotas/add", 200),
("can_change_event_settings", "subevents/", 200),
("can_change_event_settings", "subevents/2/", 404),
("can_change_event_settings", "subevents/2/delete", 404),
("can_change_event_settings", "subevents/add", 200),
("can_view_orders", "orders/overview/", 200),
("can_view_orders", "orders/export/", 200),
("can_view_orders", "orders/", 200),

View File

@@ -425,3 +425,53 @@ class VoucherFormTest(SoupTest):
doc = self.post_doc('/control/event/%s/%s/vouchers/%s/delete' % (self.orga.slug, self.event.slug, v.pk),
{}, follow=True)
assert doc.select(".alert-danger")
def test_subevent_optional(self):
self.event.has_subevents = True
self.event.save()
self._create_voucher({
'itemvar': '%d' % self.ticket.pk,
})
def test_subevent_required_for_blocking(self):
self.event.has_subevents = True
self.event.save()
self._create_voucher({
'itemvar': '%d' % self.ticket.pk,
'block_quota': 'on'
}, expected_failure=True)
def test_subevent_blocking_quota_free(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="Foo", date_from=now())
se2 = self.event.subevents.create(name="Bar", date_from=now())
self.quota_tickets.subevent = se1
self.quota_tickets.save()
q2 = Quota.objects.create(event=self.event, name='Tickets', size=0, subevent=se2)
q2.items.add(self.ticket)
self._create_voucher({
'itemvar': '%d' % self.ticket.pk,
'block_quota': 'on',
'subevent': se1.pk
})
def test_subevent_blocking_quota_full(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="Foo", date_from=now())
se2 = self.event.subevents.create(name="Bar", date_from=now())
self.quota_tickets.subevent = se1
self.quota_tickets.size = 0
self.quota_tickets.save()
q2 = Quota.objects.create(event=self.event, name='Tickets', size=5, subevent=se2)
q2.items.add(self.ticket)
self._create_voucher({
'itemvar': '%d' % self.ticket.pk,
'block_quota': 'on',
'subevent': se1.pk
}, expected_failure=True)

View File

@@ -20,7 +20,7 @@ def env():
)
user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
t = Team.objects.create(organizer=o, can_change_event_settings=True, can_change_items=True)
t = Team.objects.create(organizer=o, can_change_event_settings=True, can_change_orders=True)
t.members.add(user)
t.limit_events.add(event)
@@ -47,14 +47,15 @@ def env():
@pytest.mark.django_db
def test_flush_key(client, env):
env[0].settings.set('pretixdroid_key', 'abcdefg')
client.login(email='dummy@dummy.dummy', password='dummy')
client.get('/control/event/%s/%s/pretixdroid/' % (env[0].organizer.slug, env[0].slug))
env[0].settings.flush()
env[0].settings.get('pretixdroid_key') == 'abcdefg'
assert env[0].settings.get('pretixdroid_key') == 'abcdefg'
client.get('/control/event/%s/%s/pretixdroid/?flush_key=1' % (env[0].organizer.slug, env[0].slug))
env[0].settings.flush()
env[0].settings.get('pretixdroid_key') != 'abcdefg'
assert env[0].settings.get('pretixdroid_key') != 'abcdefg'
@pytest.mark.django_db

View File

@@ -0,0 +1,172 @@
import json
from datetime import timedelta
import pytest
from django.utils.timezone import now
from pretix.base.models import (
Checkin, Event, Item, ItemVariation, Order, OrderPosition, Organizer, Team,
User,
)
from pretix.plugins.pretixdroid.views import API_VERSION
@pytest.fixture
def env():
o = Organizer.objects.create(name='Dummy', slug='dummy')
event = Event.objects.create(
organizer=o, name='Dummy', slug='dummy',
date_from=now(), plugins='pretix.plugins.banktransfer,pretix.plugins.pretixdroid',
has_subevents=True
)
se1 = event.subevents.create(name='Foo', date_from=now())
se2 = event.subevents.create(name='Bar', date_from=now())
user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
t = Team.objects.create(organizer=o, can_change_event_settings=True, can_change_orders=True)
t.members.add(user)
t.limit_events.add(event)
shirt = Item.objects.create(event=event, name='T-Shirt', default_price=12)
shirt_red = ItemVariation.objects.create(item=shirt, default_price=14, value="Red")
ItemVariation.objects.create(item=shirt, value="Blue")
ticket = Item.objects.create(event=event, name='Ticket', default_price=23)
o1 = Order.objects.create(
code='FOO', event=event, status=Order.STATUS_PAID,
datetime=now(), expires=now() + timedelta(days=10),
total=0, payment_provider='banktransfer'
)
op1 = OrderPosition.objects.create(
order=o1, item=shirt, variation=shirt_red,
price=12, attendee_name=None, secret='1234', subevent=se1
)
op2 = OrderPosition.objects.create(
order=o1, item=ticket,
price=23, attendee_name="Peter", secret='5678910', subevent=se2
)
return event, user, o1, op1, op2, se1, se2
@pytest.mark.django_db
def test_config(client, env):
env[0].settings.set('pretixdroid_key', 'abcdefg')
client.login(email='dummy@dummy.dummy', password='dummy')
r = client.get('/control/event/%s/%s/pretixdroid/' % (env[0].organizer.slug, env[0].slug))
print(r.content)
assert 'qrcodeCanvas' not in r.rendered_content
r = client.get('/control/event/%s/%s/pretixdroid/?subevent=%d' % (env[0].organizer.slug, env[0].slug, env[5].pk))
assert 'qrcodeCanvas' in r.rendered_content
assert '/%d/' % env[5].pk in r.rendered_content
@pytest.mark.django_db
def test_custom_datetime(client, env):
env[0].settings.set('pretixdroid_key', 'abcdefg')
dt = now() - timedelta(days=1)
dt = dt.replace(microsecond=0)
resp = client.post('/pretixdroid/api/%s/%s/%d/redeem/?key=%s' % (
env[0].organizer.slug, env[0].slug, env[5].pk, 'abcdefg'
), data={'secret': '1234', 'datetime': dt.isoformat()})
jdata = json.loads(resp.content.decode("utf-8"))
assert jdata['version'] == API_VERSION
assert jdata['status'] == 'ok'
assert Checkin.objects.last().datetime == dt
@pytest.mark.django_db
def test_wrong_subevent(client, env):
env[0].settings.set('pretixdroid_key', 'abcdefg')
resp = client.post('/pretixdroid/api/%s/%s/%d/redeem/?key=%s' % (
env[0].organizer.slug, env[0].slug, env[5].pk, 'abcdefg'
), data={'secret': '5678910'})
jdata = json.loads(resp.content.decode("utf-8"))
assert jdata['status'] == 'error'
assert jdata['reason'] == 'unknown_ticket'
resp = client.post('/pretixdroid/api/%s/%s/%d/redeem/?key=%s' % (
env[0].organizer.slug, env[0].slug, env[6].pk, 'abcdefg'
), data={'secret': '5678910'})
jdata = json.loads(resp.content.decode("utf-8"))
assert jdata['status'] == 'ok'
@pytest.mark.django_db
def test_unknown_subevent(client, env):
env[0].settings.set('pretixdroid_key', 'abcdefg')
resp = client.post('/pretixdroid/api/%s/%s/%d/redeem/?key=%s' % (
env[0].organizer.slug, env[0].slug, env[6].pk + 1000, 'abcdefg'
), data={'secret': '5678910'})
assert resp.status_code == 404
@pytest.mark.django_db
def test_no_subevent(client, env):
resp = client.post('/pretixdroid/api/%s/%s/redeem/?key=%s' % (
env[0].organizer.slug, env[0].slug, 'abcdefg'
), data={'secret': '5678910'})
assert resp.status_code == 403
@pytest.mark.django_db
def test_search(client, env):
env[0].settings.set('pretixdroid_key', 'abcdefg')
resp = client.get('/pretixdroid/api/%s/%s/%d/search/?key=%s&query=%s' % (
env[0].organizer.slug, env[0].slug, env[5].pk, 'abcdefg', '567891'))
jdata = json.loads(resp.content.decode("utf-8"))
assert len(jdata['results']) == 0
resp = client.get('/pretixdroid/api/%s/%s/%d/search/?key=%s&query=%s' % (
env[0].organizer.slug, env[0].slug, env[6].pk, 'abcdefg', '567891'))
jdata = json.loads(resp.content.decode("utf-8"))
assert len(jdata['results']) == 1
assert jdata['results'][0]['secret'] == '5678910'
@pytest.mark.django_db
def test_download_all_data(client, env):
env[0].settings.set('pretixdroid_key', 'abcdefg')
resp = client.get('/pretixdroid/api/%s/%s/%d/download/?key=%s' % (
env[0].organizer.slug, env[0].slug, env[5].pk, 'abcdefg'))
jdata = json.loads(resp.content.decode("utf-8"))
assert len(jdata['results']) == 1
assert jdata['results'][0]['secret'] == '1234'
@pytest.mark.django_db
def test_status(client, env):
env[0].settings.set('pretixdroid_key', 'abcdefg')
Checkin.objects.create(position=env[3])
resp = client.get('/pretixdroid/api/%s/%s/%d/status/?key=%s' % (
env[0].organizer.slug, env[0].slug, env[5].pk, 'abcdefg'))
jdata = json.loads(resp.content.decode("utf-8"))
assert jdata['checkins'] == 1
assert jdata['total'] == 1
assert jdata['items'] == [
{'name': 'T-Shirt',
'id': env[3].item.pk,
'checkins': 1,
'admission': False,
'total': 1,
'variations': [
{'name': 'Red',
'id': env[3].variation.pk,
'checkins': 1,
'total': 1
},
{'name': 'Blue',
'id': env[3].item.variations.get(value='Blue').pk,
'checkins': 0,
'total': 0
}
]
},
{'name': 'Ticket',
'id': env[4].item.pk,
'checkins': 0,
'admission': False,
'total': 0,
'variations': []
}
]

View File

@@ -174,3 +174,42 @@ def test_sendmail_multi_locales(logged_in_client, sendmail_url, event, item):
assert response.status_code == 200
assert 'Benutzer' in response.rendered_content
assert 'Test nachricht' in response.rendered_content
@pytest.mark.django_db
def test_sendmail_subevents(logged_in_client, sendmail_url, event, order):
event.has_subevents = True
event.save()
se1 = event.subevents.create(name='Subevent FOO', date_from=now())
se2 = event.subevents.create(name='Bar', date_from=now())
op = order.positions.last()
op.subevent = se1
op.save()
djmail.outbox = []
response = logged_in_client.post(sendmail_url,
{'sendto': 'n',
'subject_0': 'Test subject',
'message_0': 'This is a test file for sending mails.',
'subevent': se1.pk
},
follow=True)
assert response.status_code == 200
assert 'alert-success' in response.rendered_content
assert len(djmail.outbox) == 1
djmail.outbox = []
response = logged_in_client.post(sendmail_url,
{'sendto': 'n',
'subject_0': 'Test subject',
'message_0': 'This is a test file for sending mails.',
'subevent': se2.pk
},
follow=True)
assert len(djmail.outbox) == 0
url = sendmail_url + 'history/'
response = logged_in_client.get(url)
assert response.status_code == 200
assert 'Subevent FOO' in response.rendered_content

View File

@@ -11,7 +11,9 @@ from pretix.base.models import (
CartPosition, Event, Item, ItemCategory, ItemVariation, Organizer,
Question, QuestionAnswer, Quota, Voucher,
)
from pretix.base.models.items import ItemAddOn
from pretix.base.models.items import (
ItemAddOn, SubEventItem, SubEventItemVariation,
)
from pretix.base.services.cart import CartError, CartManager
@@ -88,6 +90,195 @@ class CartTest(CartTestMixin, TestCase):
self.assertIsNone(objs[0].variation)
self.assertEqual(objs[0].price, 23)
def test_subevent_missing(self):
self.event.has_subevents = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now(), active=True)
self.quota_tickets.subevent = se
self.quota_tickets.save()
q = se.quotas.create(name="foo", size=None, event=self.event)
q.items.add(self.ticket)
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'item_%d' % self.ticket.id: '1',
}, follow=False)
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
self.assertEqual(len(objs), 0)
def test_voucher_subevent(self):
self.event.has_subevents = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now(), active=True)
self.quota_tickets.subevent = se
self.quota_tickets.save()
v = Voucher.objects.create(item=self.ticket, event=self.event, subevent=se)
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'item_%d' % self.ticket.id: '1',
'_voucher_code': v.code,
'subevent': se.pk
}, follow=True)
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
self.assertEqual(len(objs), 1)
self.assertEqual(objs[0].item, self.ticket)
self.assertIsNone(objs[0].variation)
self.assertEqual(objs[0].price, 23)
self.assertEqual(objs[0].subevent, se)
def test_voucher_any_subevent(self):
v = Voucher.objects.create(item=self.ticket, event=self.event)
self.event.has_subevents = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now(), active=True)
self.quota_tickets.subevent = se
self.quota_tickets.save()
print(self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'item_%d' % self.ticket.id: '1',
'_voucher_code': v.code,
'subevent': se.pk
}, follow=True).rendered_content)
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
self.assertEqual(len(objs), 1)
self.assertEqual(objs[0].item, self.ticket)
self.assertIsNone(objs[0].variation)
self.assertEqual(objs[0].price, 23)
self.assertEqual(objs[0].subevent, se)
def test_voucher_wrong_subevent(self):
self.event.has_subevents = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now(), active=True)
se2 = self.event.subevents.create(name='Foo', date_from=now(), active=True)
v = Voucher.objects.create(item=self.ticket, event=self.event, subevent=se2)
self.quota_tickets.subevent = se
self.quota_tickets.save()
se = self.event.subevents.create(name='Foo', date_from=now(), active=True)
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'item_%d' % self.ticket.id: '1',
'_voucher_code': v.code,
'subevent': se.pk
}, follow=True)
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
self.assertEqual(len(objs), 0)
def test_inactive_subevent(self):
self.event.has_subevents = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now(), active=False)
q = se.quotas.create(name="foo", size=None, event=self.event)
q.items.add(self.ticket)
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'item_%d' % self.ticket.id: '1',
'subevent': se.pk
}, follow=False)
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
self.assertEqual(len(objs), 0)
def test_subevent_sale_over(self):
self.event.has_subevents = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now(), active=True,
presale_end=now() - timedelta(days=1))
q = se.quotas.create(name="foo", size=None, event=self.event)
q.items.add(self.ticket)
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'item_%d' % self.ticket.id: '1',
'subevent': se.pk
}, follow=False)
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
self.assertEqual(len(objs), 0)
def test_subevent_sale_not_yet(self):
self.event.has_subevents = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now(), active=True,
presale_start=now() + timedelta(days=1))
q = se.quotas.create(name="foo", size=None, event=self.event)
q.items.add(self.ticket)
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'item_%d' % self.ticket.id: '1',
'subevent': se.pk
}, follow=False)
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
self.assertEqual(len(objs), 0)
def test_simple_subevent(self):
self.event.has_subevents = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now(), active=True)
q = se.quotas.create(name="foo", size=None, event=self.event)
q.items.add(self.ticket)
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'item_%d' % self.ticket.id: '1',
'subevent': se.pk
}, follow=False)
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
self.assertEqual(len(objs), 1)
self.assertEqual(objs[0].item, self.ticket)
self.assertIsNone(objs[0].variation)
self.assertEqual(objs[0].price, 23)
self.assertEqual(objs[0].subevent, se)
def test_subevent_sold_out(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name='Foo', date_from=now(), active=True)
self.event.subevents.create(name='Foo', date_from=now(), active=True)
q = se1.quotas.create(name="foo", size=0, event=self.event)
q.items.add(self.ticket)
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'item_%d' % self.ticket.id: '1',
'subevent': se1.pk
}, follow=False)
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
self.assertEqual(len(objs), 0)
def test_other_subevent_sold_out(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name='Foo', date_from=now(), active=True)
se2 = self.event.subevents.create(name='Foo', date_from=now(), active=True)
q = se1.quotas.create(name="foo", size=0, event=self.event)
q.items.add(self.ticket)
q = se2.quotas.create(name="foo", size=100, event=self.event)
q.items.add(self.ticket)
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'item_%d' % self.ticket.id: '1',
'subevent': se2.pk
}, follow=False)
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
self.assertEqual(len(objs), 1)
def test_subevent_no_quota(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name='Foo', date_from=now(), active=True)
se2 = self.event.subevents.create(name='Foo', date_from=now(), active=True)
q = se1.quotas.create(name="foo", size=None, event=self.event)
q.items.add(self.ticket)
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'item_%d' % self.ticket.id: '1',
'subevent': se2.pk
}, follow=False)
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
self.assertEqual(len(objs), 0)
def test_subevent_price(self):
self.event.has_subevents = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now(), active=True)
q = se.quotas.create(name="foo", size=None, event=self.event)
q.items.add(self.ticket)
SubEventItem.objects.create(subevent=se, item=self.ticket, price=42)
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'item_%d' % self.ticket.id: '1',
'subevent': se.pk
}, follow=False)
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
self.assertEqual(len(objs), 1)
self.assertEqual(objs[0].item, self.ticket)
self.assertIsNone(objs[0].variation)
self.assertEqual(objs[0].price, 42)
self.assertEqual(objs[0].subevent, se)
def test_free_price(self):
self.ticket.free_price = True
self.ticket.save()
@@ -198,6 +389,24 @@ class CartTest(CartTestMixin, TestCase):
self.assertEqual(objs[0].variation, self.shirt_red)
self.assertEqual(objs[0].price, 16)
def test_subevent_variation_price(self):
self.event.has_subevents = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now(), active=True)
q = se.quotas.create(name="foo", size=None, event=self.event)
q.variations.add(self.shirt_red)
SubEventItemVariation.objects.create(subevent=se, variation=self.shirt_red, price=42)
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'variation_%d_%d' % (self.shirt.id, self.shirt_red.id): '1',
'subevent': se.pk
}, follow=False)
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
self.assertEqual(len(objs), 1)
self.assertEqual(objs[0].item, self.shirt)
self.assertEqual(objs[0].variation, self.shirt_red)
self.assertEqual(objs[0].price, 42)
self.assertEqual(objs[0].subevent, se)
def test_count(self):
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'item_%d' % self.ticket.id: '2'
@@ -438,6 +647,30 @@ class CartTest(CartTestMixin, TestCase):
self.assertIsNone(objs[0].variation)
self.assertEqual(objs[0].price, 23)
def test_subevent_quota_partly(self):
self.event.has_subevents = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now(), active=True)
self.quota_tickets.size = 1
self.quota_tickets.subevent = se
self.quota_tickets.save()
q2 = self.event.quotas.create(name='Foo', size=15)
q2.items.add(self.ticket)
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'item_%d' % self.ticket.id: '2',
'subevent': se.pk
}, follow=True)
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
target_status_code=200)
doc = BeautifulSoup(response.rendered_content, "lxml")
self.assertIn('no longer available', doc.select('.alert-danger')[0].text)
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
self.assertEqual(len(objs), 1)
self.assertEqual(objs[0].item, self.ticket)
self.assertIsNone(objs[0].variation)
self.assertEqual(objs[0].price, 23)
def test_renew_in_time(self):
cp = CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket,
@@ -496,6 +729,48 @@ class CartTest(CartTestMixin, TestCase):
self.assertIn('no longer available', doc.select('.alert-danger')[0].text)
self.assertFalse(CartPosition.objects.filter(id=cp1.id).exists())
def test_subevent_renew_expired_successfully(self):
self.event.has_subevents = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now(), active=True)
self.quota_tickets.subevent = se
self.quota_tickets.save()
self.quota_shirts.subevent = se
self.quota_shirts.save()
cp1 = CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket,
price=23, expires=now() - timedelta(minutes=10), subevent=se
)
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'variation_%d_%d' % (self.shirt.id, self.shirt_red.id): '1',
'subevent': se.pk,
}, follow=True)
obj = CartPosition.objects.get(id=cp1.id)
self.assertEqual(obj.item, self.ticket)
self.assertIsNone(obj.variation)
self.assertEqual(obj.price, 23)
self.assertEqual(obj.subevent, se)
self.assertGreater(obj.expires, now())
def test_subevent_renew_expired_failed(self):
self.event.has_subevents = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now(), active=True)
self.quota_tickets.subevent = se
self.quota_tickets.size = 0
self.quota_tickets.save()
cp1 = CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket,
price=23, expires=now() - timedelta(minutes=10), subevent=se
)
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'item_%d' % self.ticket.id: '1',
'subevent': se.pk,
}, follow=True)
doc = BeautifulSoup(response.rendered_content, "lxml")
self.assertIn('no longer available', doc.select('.alert-danger')[0].text)
self.assertFalse(CartPosition.objects.filter(id=cp1.id).exists())
def test_remove_simple(self):
cp = CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket,
@@ -1048,6 +1323,50 @@ class CartAddonTest(CartTestMixin, TestCase):
cp2 = cp1.addons.first()
assert cp2.item == self.workshop1
def test_cart_subevent_set_simple_addon(self):
self.event.has_subevents = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now(), active=True)
self.workshopquota.subevent = se
self.workshopquota.save()
cp1 = CartPosition.objects.create(
expires=now() + timedelta(minutes=10), item=self.ticket, price=Decimal('23.00'),
event=self.event, cart_id=self.session_key, subevent=se
)
self.cm.set_addons([
{
'addon_to': cp1.pk,
'item': self.workshop1.pk,
'variation': None
}
])
self.cm.commit()
cp2 = cp1.addons.first()
assert cp2.item == self.workshop1
assert cp2.subevent == se
def test_cart_subevent_set_addon_for_wrong_subevent(self):
self.event.has_subevents = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now(), active=True)
se2 = self.event.subevents.create(name='Foo', date_from=now(), active=True)
self.workshopquota.subevent = se2
self.workshopquota.save()
cp1 = CartPosition.objects.create(
expires=now() + timedelta(minutes=10), item=self.ticket, price=Decimal('23.00'),
event=self.event, cart_id=self.session_key, subevent=se
)
with self.assertRaises(CartError):
self.cm.set_addons([
{
'addon_to': cp1.pk,
'item': self.workshop1.pk,
'variation': None
}
])
def test_wrong_category(self):
cp1 = CartPosition.objects.create(
expires=now() + timedelta(minutes=10), item=self.ticket, price=Decimal('23.00'),

View File

@@ -13,7 +13,7 @@ from pretix.base.models import (
CartPosition, Event, Item, ItemCategory, Order, OrderPosition, Organizer,
Question, Quota, Voucher,
)
from pretix.base.models.items import ItemAddOn, ItemVariation
from pretix.base.models.items import ItemAddOn, ItemVariation, SubEventItem
class CheckoutTestCase(TestCase):
@@ -300,6 +300,26 @@ class CheckoutTestCase(TestCase):
session[key] = value
session.save()
def test_subevent(self):
self.event.has_subevents = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now())
q = se.quotas.create(name="foo", size=None, event=self.event)
q.items.add(self.ticket)
cr1 = CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket,
price=23, expires=now() + timedelta(minutes=10), subevent=se
)
self._set_session('payment', 'banktransfer')
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
doc = BeautifulSoup(response.rendered_content, "lxml")
self.assertEqual(len(doc.select(".thank-you")), 1)
self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists())
self.assertEqual(Order.objects.count(), 1)
self.assertEqual(OrderPosition.objects.count(), 1)
self.assertEqual(OrderPosition.objects.first().subevent, se)
def test_free_price(self):
self.ticket.free_price = True
self.ticket.save()
@@ -331,6 +351,29 @@ class CheckoutTestCase(TestCase):
self.assertEqual(Order.objects.count(), 1)
self.assertEqual(OrderPosition.objects.count(), 1)
def test_subevent_confirm_expired_available(self):
self.event.has_subevents = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now())
se2 = self.event.subevents.create(name='Foo', date_from=now())
self.quota_tickets.size = 0
self.quota_tickets.subevent = se2
self.quota_tickets.save()
q2 = se.quotas.create(event=self.event, size=1, name='Bar')
q2.items.add(self.ticket)
cr1 = CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket,
price=23, expires=now() - timedelta(minutes=10), subevent=se
)
self._set_session('payment', 'banktransfer')
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
doc = BeautifulSoup(response.rendered_content, "lxml")
self.assertEqual(len(doc.select(".thank-you")), 1)
self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists())
self.assertEqual(Order.objects.count(), 1)
self.assertEqual(OrderPosition.objects.count(), 1)
def test_confirm_expired_available(self):
cr1 = CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket,
@@ -345,6 +388,25 @@ class CheckoutTestCase(TestCase):
self.assertEqual(Order.objects.count(), 1)
self.assertEqual(OrderPosition.objects.count(), 1)
def test_subevent_confirm_price_changed(self):
self.event.has_subevents = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now())
q = se.quotas.create(name="foo", size=None, event=self.event)
q.items.add(self.ticket)
SubEventItem.objects.create(subevent=se, item=self.ticket, price=24)
cr1 = CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket,
price=23, expires=now() - timedelta(minutes=10), subevent=se
)
self._set_session('payment', 'banktransfer')
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
doc = BeautifulSoup(response.rendered_content, "lxml")
self.assertEqual(len(doc.select(".alert-danger")), 1)
cr1 = CartPosition.objects.get(id=cr1.id)
self.assertEqual(cr1.price, 24)
def test_confirm_price_changed(self):
self.ticket.default_price = 24
self.ticket.save()
@@ -663,6 +725,35 @@ class CheckoutTestCase(TestCase):
self.assertEqual(Order.objects.count(), 1)
self.assertEqual(OrderPosition.objects.count(), 1)
def test_subevent_confirm_expired_partial(self):
self.event.has_subevents = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now())
se2 = self.event.subevents.create(name='Foo', date_from=now())
self.quota_tickets.size = 10
self.quota_tickets.subevent = se2
self.quota_tickets.save()
q2 = se.quotas.create(event=self.event, size=1, name='Bar')
q2.items.add(self.ticket)
CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket,
price=23, expires=now() - timedelta(minutes=10), subevent=se
)
CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket,
price=23, expires=now() - timedelta(minutes=10), subevent=se
)
CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket,
price=23, expires=now() - timedelta(minutes=10), subevent=se2
)
self._set_session('payment', 'banktransfer')
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
doc = BeautifulSoup(response.rendered_content, "lxml")
self.assertEqual(len(doc.select(".alert-danger")), 1)
self.assertEqual(CartPosition.objects.filter(cart_id=self.session_key).count(), 2)
def test_confirm_expired_partial(self):
self.quota_tickets.size = 1
self.quota_tickets.save()
@@ -862,3 +953,83 @@ class CheckoutTestCase(TestCase):
response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug))
self.assertRedirects(response, '/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug),
target_status_code=200)
def test_set_addons_subevent(self):
self.event.has_subevents = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now())
self.workshopquota.size = 1
self.workshopquota.subevent = se
self.workshopquota.save()
SubEventItem.objects.create(subevent=se, item=self.workshop1, price=42)
ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat, min_count=1)
CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket,
price=23, expires=now() - timedelta(minutes=10), subevent=se
)
response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True)
self.assertRedirects(response, '/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug),
target_status_code=200)
assert 'Workshop 1 (+ EUR 42.00)' in response.rendered_content
def test_set_addons_subevent_net_prices(self):
self.event.has_subevents = True
self.event.settings.display_net_prices = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now())
self.workshopquota.size = 1
self.workshopquota.subevent = se
self.workshopquota.save()
self.workshop1.tax_rate = 19
self.workshop1.save()
self.workshop2.tax_rate = 19
self.workshop2.save()
SubEventItem.objects.create(subevent=se, item=self.workshop1, price=42)
ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat, min_count=1)
CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket,
price=23, expires=now() - timedelta(minutes=10), subevent=se
)
response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True)
self.assertRedirects(response, '/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug),
target_status_code=200)
assert 'Workshop 1 (+ EUR 35.29 plus 19.00% taxes)' in response.rendered_content
assert 'A (+ EUR 10.08 plus 19.00% taxes)' in response.rendered_content
def test_confirm_subevent_presale_not_yet(self):
self.event.has_subevents = True
self.event.settings.display_net_prices = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now(), presale_start=now() + datetime.timedelta(days=1))
CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket,
price=23, expires=now() + timedelta(minutes=10), subevent=se
)
self._set_session('payment', 'banktransfer')
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
doc = BeautifulSoup(response.rendered_content, "lxml")
self.assertGreaterEqual(len(doc.select(".alert-danger")), 1)
assert 'presale period for one of the events in your cart has not yet started.' in response.rendered_content
assert not CartPosition.objects.filter(cart_id=self.session_key).exists()
def test_confirm_subevent_presale_over(self):
self.event.has_subevents = True
self.event.settings.display_net_prices = True
self.event.save()
se = self.event.subevents.create(name='Foo', date_from=now(), presale_end=now() - datetime.timedelta(days=1))
CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket,
price=23, expires=now() + timedelta(minutes=10), subevent=se
)
self._set_session('payment', 'banktransfer')
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
doc = BeautifulSoup(response.rendered_content, "lxml")
self.assertGreaterEqual(len(doc.select(".alert-danger")), 1)
assert 'presale period for one of the events in your cart has ended.' in response.rendered_content
assert not CartPosition.objects.filter(cart_id=self.session_key).exists()

View File

@@ -14,6 +14,7 @@ from pretix.base.models import (
Event, Item, ItemCategory, ItemVariation, Order, Organizer, Quota, Team,
User, WaitingListEntry,
)
from pretix.base.models.items import SubEventItem, SubEventItemVariation
class EventTestMixin:
@@ -130,6 +131,94 @@ class ItemDisplayTest(EventTestMixin, SoupTest):
resp = self.client.get('/%s/%s/' % (self.orga.slug, self.event.slug))
self.assertNotIn("Early-bird", resp.rendered_content)
def test_subevents_inactive_unknown(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name='Foo', date_from=now(), active=False)
resp = self.client.get('/%s/%s/%d/' % (self.orga.slug, self.event.slug, se1.pk))
assert resp.status_code == 404
resp = self.client.get('/%s/%s/%d/' % (self.orga.slug, self.event.slug, se1.pk + 1000))
assert resp.status_code == 404
def test_subevent_list(self):
self.event.has_subevents = True
self.event.save()
self.event.subevents.create(name='Foo SE1', date_from=now() + datetime.timedelta(days=1), active=True)
self.event.subevents.create(name='Foo SE2', date_from=now() + datetime.timedelta(days=1), active=False)
resp = self.client.get('/%s/%s/' % (self.orga.slug, self.event.slug))
self.assertIn("Foo SE1", resp.rendered_content)
self.assertNotIn("Foo SE2", resp.rendered_content)
def test_subevent_calendar(self):
self.event.settings.event_list_type = 'calendar'
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name='Foo SE1', date_from=now() + datetime.timedelta(days=64), active=True)
self.event.subevents.create(name='Foo SE2', date_from=now() + datetime.timedelta(days=32), active=True)
resp = self.client.get('/%s/%s/' % (self.orga.slug, self.event.slug))
self.assertIn("Foo SE2", resp.rendered_content)
self.assertNotIn("Foo SE1", resp.rendered_content)
resp = self.client.get('/%s/%s/?year=%d&month=%d' % (self.orga.slug, self.event.slug, se1.date_from.year,
se1.date_from.month))
self.assertIn("Foo SE1", resp.rendered_content)
self.assertNotIn("Foo SE2", resp.rendered_content)
def test_subevents(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name='Foo', date_from=now(), active=True)
se2 = self.event.subevents.create(name='Foo', date_from=now(), active=True)
q = Quota.objects.create(event=self.event, name='Quota', size=2, subevent=se1)
item = Item.objects.create(event=self.event, name='Early-bird ticket', default_price=0)
q.items.add(item)
resp = self.client.get('/%s/%s/%d/' % (self.orga.slug, self.event.slug, se1.pk))
self.assertIn("Early-bird", resp.rendered_content)
resp = self.client.get('/%s/%s/%d/' % (self.orga.slug, self.event.slug, se2.pk))
self.assertNotIn("Early-bird", resp.rendered_content)
def test_subevent_prices(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name='Foo', date_from=now(), active=True)
se2 = self.event.subevents.create(name='Foo', date_from=now(), active=True)
item = Item.objects.create(event=self.event, name='Early-bird ticket', default_price=15)
q = Quota.objects.create(event=self.event, name='Quota', size=2, subevent=se1)
q.items.add(item)
q = Quota.objects.create(event=self.event, name='Quota', size=2, subevent=se2)
q.items.add(item)
SubEventItem.objects.create(subevent=se1, item=item, price=12)
resp = self.client.get('/%s/%s/%d/' % (self.orga.slug, self.event.slug, se1.pk))
self.assertIn("12.00", resp.rendered_content)
self.assertNotIn("15.00", resp.rendered_content)
resp = self.client.get('/%s/%s/%d/' % (self.orga.slug, self.event.slug, se2.pk))
self.assertIn("15.00", resp.rendered_content)
self.assertNotIn("12.00", resp.rendered_content)
def test_subevent_net_prices(self):
self.event.has_subevents = True
self.event.save()
self.event.settings.display_net_prices = True
se1 = self.event.subevents.create(name='Foo', date_from=now(), active=True)
se2 = self.event.subevents.create(name='Foo', date_from=now(), active=True)
item = Item.objects.create(event=self.event, name='Early-bird ticket', default_price=15,
tax_rate=19)
q = Quota.objects.create(event=self.event, name='Quota', size=2, subevent=se1)
q.items.add(item)
q = Quota.objects.create(event=self.event, name='Quota', size=2, subevent=se2)
q.items.add(item)
SubEventItem.objects.create(subevent=se1, item=item, price=12)
resp = self.client.get('/%s/%s/%d/' % (self.orga.slug, self.event.slug, se1.pk))
self.assertIn("10.08", resp.rendered_content)
self.assertNotIn("12.00", resp.rendered_content)
self.assertNotIn("15.00", resp.rendered_content)
resp = self.client.get('/%s/%s/%d/' % (self.orga.slug, self.event.slug, se2.pk))
self.assertIn("12.61", resp.rendered_content)
self.assertNotIn("12.00", resp.rendered_content)
self.assertNotIn("15.00", resp.rendered_content)
def test_no_variations_in_quota(self):
c = ItemCategory.objects.create(event=self.event, name="Entry tickets", position=0)
q = Quota.objects.create(event=self.event, name='Quota', size=2)
@@ -357,6 +446,99 @@ class VoucherRedeemItemDisplayTest(EventTestMixin, SoupTest):
html = self.client.get('/%s/%s/redeem?voucher=%s' % (self.orga.slug, self.event.slug, 'ABC'), follow=True)
assert "alert-danger" in html.rendered_content
def test_subevent_net_prices(self):
self.event.settings.display_net_prices = True
self.event.has_subevents = True
self.event.save()
self.item.tax_rate = 19
self.item.save()
se1 = self.event.subevents.create(name='SE1', date_from=now(), active=True)
q = Quota.objects.create(event=self.event, name='Quota', size=2, subevent=se1)
var1 = ItemVariation.objects.create(item=self.item, value='Red', position=1)
var2 = ItemVariation.objects.create(item=self.item, value='Black', position=2)
q.variations.add(var1)
q.variations.add(var2)
SubEventItemVariation.objects.create(subevent=se1, variation=var1, price=10)
self.v.value = Decimal("2.00")
self.v.price_mode = 'subtract'
self.v.save()
html = self.client.get('/%s/%s/redeem?voucher=%s&subevent=%s' % (
self.orga.slug, self.event.slug, self.v.code, se1.pk
))
assert "SE1" in html.rendered_content
assert "Early-bird" in html.rendered_content
assert "8.40" in html.rendered_content
assert "6.72" in html.rendered_content
def test_subevent_prices(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name='SE1', date_from=now(), active=True)
q = Quota.objects.create(event=self.event, name='Quota', size=2, subevent=se1)
var1 = ItemVariation.objects.create(item=self.item, value='Red', position=1)
var2 = ItemVariation.objects.create(item=self.item, value='Black', position=2)
q.variations.add(var1)
q.variations.add(var2)
SubEventItemVariation.objects.create(subevent=se1, variation=var1, price=10)
self.v.value = Decimal("2.00")
self.v.price_mode = 'subtract'
self.v.save()
html = self.client.get('/%s/%s/redeem?voucher=%s&subevent=%s' % (
self.orga.slug, self.event.slug, self.v.code, se1.pk
))
assert "SE1" in html.rendered_content
assert "Early-bird" in html.rendered_content
assert "10.00" in html.rendered_content
assert "8.00" in html.rendered_content
assert "variation_%d_%d" % (self.item.pk, var1.pk) in html.rendered_content
assert "variation_%d_%d" % (self.item.pk, var2.pk) in html.rendered_content
def test_voucher_ignore_other_subevent(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name='SE1', date_from=now(), active=True)
se2 = self.event.subevents.create(name='SE2', date_from=now(), active=True)
q = Quota.objects.create(event=self.event, name='Quota', size=2, subevent=se1)
var1 = ItemVariation.objects.create(item=self.item, value='Red', position=1)
var2 = ItemVariation.objects.create(item=self.item, value='Black', position=2)
q.variations.add(var1)
q.variations.add(var2)
self.v.subevent = se1
self.v.save()
html = self.client.get('/%s/%s/redeem?voucher=%s&subevent=%s' % (
self.orga.slug, self.event.slug, self.v.code, se2.pk
))
assert "SE1" in html.rendered_content
def test_voucher_quota(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name='SE1', date_from=now(), active=True)
se2 = self.event.subevents.create(name='SE2', date_from=now(), active=True)
q = Quota.objects.create(event=self.event, name='Quota', size=0, subevent=se1)
q2 = Quota.objects.create(event=self.event, name='Quota', size=2, subevent=se2)
var1 = ItemVariation.objects.create(item=self.item, value='Red', position=1)
var2 = ItemVariation.objects.create(item=self.item, value='Black', position=2)
q.variations.add(var1)
q2.variations.add(var1)
q.variations.add(var2)
q2.variations.add(var1)
self.v.save()
html = self.client.get('/%s/%s/redeem?voucher=%s&subevent=%s' % (
self.orga.slug, self.event.slug, self.v.code, se1.pk
))
assert "SE1" in html.rendered_content
assert "variation_%d_%d" % (self.item.pk, var1.pk) not in html.rendered_content
assert "variation_%d_%d" % (self.item.pk, var2.pk) not in html.rendered_content
class WaitingListTest(EventTestMixin, SoupTest):
def setUp(self):
@@ -376,7 +558,7 @@ class WaitingListTest(EventTestMixin, SoupTest):
self.assertEqual(response.status_code, 200)
self.assertNotIn('waitinglist', response.rendered_content)
response = self.client.get(
'/%s/%s/waitinglist?item=%d' % (self.orga.slug, self.event.slug, self.item.pk + 1)
'/%s/%s/waitinglist/?item=%d' % (self.orga.slug, self.event.slug, self.item.pk + 1)
)
self.assertEqual(response.status_code, 302)
@@ -389,12 +571,12 @@ class WaitingListTest(EventTestMixin, SoupTest):
def test_submit_form(self):
response = self.client.get(
'/%s/%s/waitinglist?item=%d' % (self.orga.slug, self.event.slug, self.item.pk)
'/%s/%s/waitinglist/?item=%d' % (self.orga.slug, self.event.slug, self.item.pk)
)
self.assertEqual(response.status_code, 200)
self.assertIn('waiting list', response.rendered_content)
response = self.client.post(
'/%s/%s/waitinglist?item=%d' % (self.orga.slug, self.event.slug, self.item.pk), {
'/%s/%s/waitinglist/?item=%d' % (self.orga.slug, self.event.slug, self.item.pk), {
'email': 'foo@bar.com'
}
)
@@ -406,17 +588,77 @@ class WaitingListTest(EventTestMixin, SoupTest):
assert wle.voucher is None
assert wle.locale == 'en'
def test_invalid_item(self):
def test_subevent_valid(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="Foo", date_from=now(), active=True)
se2 = self.event.subevents.create(name="Foobar", date_from=now(), active=True)
self.q.subevent = se1
self.q.save()
q2 = self.event.quotas.create(name="Foobar", size=100, subevent=se2)
q2.items.add(self.item)
response = self.client.get(
'/%s/%s/waitinglist?item=%d' % (self.orga.slug, self.event.slug, self.item.pk + 1)
'/%s/%s/waitinglist/?item=%d&subevent=%d' % (self.orga.slug, self.event.slug, self.item.pk, se1.pk)
)
self.assertEqual(response.status_code, 200)
self.assertIn('waiting list', response.rendered_content)
response = self.client.post(
'/%s/%s/waitinglist/?item=%d&subevent=%d' % (self.orga.slug, self.event.slug, self.item.pk, se1.pk), {
'email': 'foo@bar.com'
}
)
self.assertEqual(response.status_code, 302)
wle = WaitingListEntry.objects.get(email='foo@bar.com')
assert wle.event == self.event
assert wle.item == self.item
assert wle.subevent == se1
def test_invalid_item(self):
response = self.client.get(
'/%s/%s/waitinglist/?item=%d' % (self.orga.slug, self.event.slug, self.item.pk + 1)
)
self.assertEqual(response.status_code, 302)
def test_invalid_subevent(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="Foo", date_from=now(), active=False)
response = self.client.get(
'/%s/%s/waitinglist/?item=%d' % (self.orga.slug, self.event.slug, self.item.pk)
)
self.assertEqual(response.status_code, 302)
response = self.client.get(
'/%s/%s/waitinglist/?item=%d&subevent=%d' % (self.orga.slug, self.event.slug, self.item.pk, se1.pk + 100)
)
self.assertEqual(response.status_code, 404)
response = self.client.get(
'/%s/%s/waitinglist/?item=%d&subevent=%d' % (self.orga.slug, self.event.slug, self.item.pk, se1.pk)
)
self.assertEqual(response.status_code, 404)
def test_available(self):
self.q.size = 1
self.q.save()
response = self.client.post(
'/%s/%s/waitinglist?item=%d' % (self.orga.slug, self.event.slug, self.item.pk), {
'/%s/%s/waitinglist/?item=%d' % (self.orga.slug, self.event.slug, self.item.pk), {
'email': 'foo@bar.com'
}
)
self.assertEqual(response.status_code, 302)
self.assertFalse(WaitingListEntry.objects.filter(email='foo@bar.com').exists())
def test_subevent_available(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="Foo", date_from=now(), active=True)
se2 = self.event.subevents.create(name="Foobar", date_from=now(), active=True)
self.q.size = 1
self.q.subevent = se1
self.q.save()
q2 = self.event.quotas.create(name="Foobar", size=0, subevent=se2)
q2.items.add(self.item)
response = self.client.post(
'/%s/%s/waitinglist/?item=%d&subevent=%d' % (self.orga.slug, self.event.slug, self.item.pk, se1.pk), {
'email': 'foo@bar.com'
}
)
@@ -575,14 +817,14 @@ class EventIcalDownloadTest(EventTestMixin, SoupTest):
self.event.save()
def test_response_type(self):
ical = self.client.get('/%s/%s/ical' % (self.orga.slug, self.event.slug))
ical = self.client.get('/%s/%s/ical/' % (self.orga.slug, self.event.slug))
self.assertEqual(ical['Content-Type'], 'text/calendar')
self.assertEqual(ical['Content-Disposition'], 'attachment; filename="{}-{}.ics"'.format(
self.assertEqual(ical['Content-Disposition'], 'attachment; filename="{}-{}-0.ics"'.format(
self.orga.slug, self.event.slug
))
def test_header_footer(self):
ical = self.client.get('/%s/%s/ical' % (self.orga.slug, self.event.slug)).content.decode()
ical = self.client.get('/%s/%s/ical/' % (self.orga.slug, self.event.slug)).content.decode()
self.assertTrue(ical.startswith('BEGIN:VCALENDAR'), 'missing VCALENDAR header')
self.assertTrue(ical.strip().endswith('END:VCALENDAR'), 'missing VCALENDAR footer')
self.assertIn('BEGIN:VEVENT', ical, 'missing VEVENT header')
@@ -591,7 +833,7 @@ class EventIcalDownloadTest(EventTestMixin, SoupTest):
def test_timezone_header_footer(self):
self.event.settings.timezone = 'Asia/Tokyo'
self.event.save()
ical = self.client.get('/%s/%s/ical' % (self.orga.slug, self.event.slug)).content.decode()
ical = self.client.get('/%s/%s/ical/' % (self.orga.slug, self.event.slug)).content.decode()
self.assertTrue(ical.startswith('BEGIN:VCALENDAR'), 'missing VCALENDAR header')
self.assertTrue(ical.strip().endswith('END:VCALENDAR'), 'missing VCALENDAR footer')
self.assertIn('BEGIN:VEVENT', ical, 'missing VEVENT header')
@@ -600,20 +842,20 @@ class EventIcalDownloadTest(EventTestMixin, SoupTest):
self.assertIn('END:VTIMEZONE', ical, 'missing VTIMEZONE footer')
def test_metadata(self):
ical = self.client.get('/%s/%s/ical' % (self.orga.slug, self.event.slug)).content.decode()
ical = self.client.get('/%s/%s/ical/' % (self.orga.slug, self.event.slug)).content.decode()
self.assertIn('VERSION:2.0', ical, 'incorrect version tag - 2.0')
self.assertIn('-//pretix//%s//' % settings.PRETIX_INSTANCE_NAME, ical, 'incorrect PRODID')
def test_event_info(self):
ical = self.client.get('/%s/%s/ical' % (self.orga.slug, self.event.slug)).content.decode()
ical = self.client.get('/%s/%s/ical/' % (self.orga.slug, self.event.slug)).content.decode()
self.assertIn('SUMMARY:%s' % self.event.name, ical, 'incorrect correct summary')
self.assertIn('LOCATION:DUMMY ARENA', ical, 'incorrect location')
self.assertIn('ORGANIZER:%s' % self.event.organizer.name, ical, 'incorrect organizer')
self.assertTrue(re.search(r'DTSTAMP:\d{8}T\d{6}Z', ical), 'incorrect timestamp')
self.assertTrue(re.search(r'UID:\w*-\w*-\d{20}', ical), 'missing UID key')
self.assertTrue(re.search(r'UID:\w*-\w*-0-\d{20}', ical), 'missing UID key')
def test_utc_timezone(self):
ical = self.client.get('/%s/%s/ical' % (self.orga.slug, self.event.slug)).content.decode()
ical = self.client.get('/%s/%s/ical/' % (self.orga.slug, self.event.slug)).content.decode()
# according to icalendar spec, timezone must NOT be shown if it is UTC
self.assertIn('DTSTART:%s' % self.event.date_from.strftime('%Y%m%dT%H%M%SZ'), ical, 'incorrect start time')
self.assertIn('DTEND:%s' % self.event.date_to.strftime('%Y%m%dT%H%M%SZ'), ical, 'incorrect end time')
@@ -621,7 +863,7 @@ class EventIcalDownloadTest(EventTestMixin, SoupTest):
def test_include_timezone(self):
self.event.settings.timezone = 'Asia/Tokyo'
self.event.save()
ical = self.client.get('/%s/%s/ical' % (self.orga.slug, self.event.slug)).content.decode()
ical = self.client.get('/%s/%s/ical/' % (self.orga.slug, self.event.slug)).content.decode()
# according to icalendar spec, timezone must be shown if it is not UTC
fmt = '%Y%m%dT%H%M%S'
self.assertIn('DTSTART;TZID=%s:%s' %
@@ -637,7 +879,7 @@ class EventIcalDownloadTest(EventTestMixin, SoupTest):
def test_no_time(self):
self.event.settings.show_times = False
self.event.save()
ical = self.client.get('/%s/%s/ical' % (self.orga.slug, self.event.slug)).content.decode()
ical = self.client.get('/%s/%s/ical/' % (self.orga.slug, self.event.slug)).content.decode()
self.assertIn('DTSTART;VALUE=DATE:%s' % self.event.date_from.strftime('%Y%m%d'), ical, 'incorrect start date')
self.assertIn('DTEND;VALUE=DATE:%s' % self.event.date_to.strftime('%Y%m%d'), ical, 'incorrect end date')
@@ -645,7 +887,7 @@ class EventIcalDownloadTest(EventTestMixin, SoupTest):
self.event.settings.timezone = 'Asia/Tokyo'
self.event.settings.show_date_to = False
self.event.save()
ical = self.client.get('/%s/%s/ical' % (self.orga.slug, self.event.slug)).content.decode()
ical = self.client.get('/%s/%s/ical/' % (self.orga.slug, self.event.slug)).content.decode()
fmt = '%Y%m%dT%H%M%S'
self.assertIn('DTSTART;TZID=%s:%s' %
(self.event.settings.timezone,
@@ -657,7 +899,7 @@ class EventIcalDownloadTest(EventTestMixin, SoupTest):
self.event.settings.show_date_to = False
self.event.settings.show_times = False
self.event.save()
ical = self.client.get('/%s/%s/ical' % (self.orga.slug, self.event.slug)).content.decode()
ical = self.client.get('/%s/%s/ical/' % (self.orga.slug, self.event.slug)).content.decode()
self.assertIn('DTSTART;VALUE=DATE:%s' % self.event.date_from.strftime('%Y%m%d'), ical, 'incorrect start date')
self.assertNotIn('DTEND', ical, 'unexpected end time attribute')
@@ -667,10 +909,35 @@ class EventIcalDownloadTest(EventTestMixin, SoupTest):
self.event.settings.timezone = 'Asia/Tokyo'
self.event.settings.show_times = False
self.event.save()
ical = self.client.get('/%s/%s/ical' % (self.orga.slug, self.event.slug)).content.decode()
ical = self.client.get('/%s/%s/ical/' % (self.orga.slug, self.event.slug)).content.decode()
self.assertIn('DTSTART;VALUE=DATE:20131227', ical, 'incorrect start date')
self.assertIn('DTEND;VALUE=DATE:20131229', ical, 'incorrect end date')
def test_subevent_required(self):
self.event.has_subevents = True
self.event.save()
resp = self.client.get('/%s/%s/ical/' % (self.orga.slug, self.event.slug))
assert resp.status_code == 404
resp = self.client.get('/%s/%s/ical/100/' % (self.orga.slug, self.event.slug))
assert resp.status_code == 404
def test_subevent(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(
name='My fancy subevent',
location='Heeeeeere',
date_from=datetime.datetime(2014, 12, 26, 21, 57, 58, tzinfo=datetime.timezone.utc),
date_to=datetime.datetime(2014, 12, 28, 21, 57, 58, tzinfo=datetime.timezone.utc),
active=True
)
self.event.settings.show_times = False
ical = self.client.get('/%s/%s/ical/%d/' % (self.orga.slug, self.event.slug, se1.pk)).content.decode()
self.assertIn('DTSTART;VALUE=DATE:20141226', ical, 'incorrect start date')
self.assertIn('DTEND;VALUE=DATE:20141228', ical, 'incorrect end date')
self.assertIn('SUMMARY:%s' % se1.name, ical, 'incorrect correct summary')
self.assertIn('LOCATION:Heeeeeere', ical, 'incorrect location')
class EventSlugBlacklistValidatorTest(EventTestMixin, SoupTest):
def test_slug_validation(self):

View File

@@ -9,6 +9,7 @@ from pretix.base.models import (
Event, Item, ItemCategory, ItemVariation, Order, OrderPosition, Organizer,
Question, Quota,
)
from pretix.base.reldate import RelativeDate, RelativeDateWrapper
from pretix.base.services.invoices import generate_invoice
@@ -350,6 +351,21 @@ class OrdersTest(TestCase):
self.order.secret),
target_status_code=200)
self.event.date_from = now() + datetime.timedelta(days=3)
self.event.save()
self.event.settings.set('ticket_download_date', RelativeDateWrapper(RelativeDate(
base_date_name='date_from', days_before=2, time=None
)))
response = self.client.get(
'/%s/%s/order/%s/%s/download/%d/testdummy' % (self.orga.slug, self.event.slug, self.order.code,
self.order.secret, self.ticket_pos.pk),
follow=True
)
self.assertRedirects(response,
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
self.order.secret),
target_status_code=200)
del self.event.settings['ticket_download_date']
response = self.client.get(
'/%s/%s/order/%s/%s/download/%d/testdummy' % (self.orga.slug, self.event.slug, self.order.code,

View File

@@ -1,7 +1,8 @@
from datetime import timedelta
from datetime import datetime, timedelta
import pytest
from django.utils.timezone import now
from pytz import UTC
from pretix.base.models import Event, Organizer
@@ -95,3 +96,26 @@ def test_different_organizer_not_shown(env, client):
)
r = client.get('/mrmcd/')
assert '32C3' not in r.rendered_content
@pytest.mark.django_db
def test_calendar(env, client):
env[0].settings.event_list_type = 'calendar'
e = Event.objects.create(
organizer=env[0], name='MRMCD2017', slug='2017',
date_from=datetime(now().year + 1, 9, 1, tzinfo=UTC),
live=True
)
r = client.get('/mrmcd/')
assert 'MRMCD2017' not in r.rendered_content
e.is_public = True
e.save()
r = client.get('/mrmcd/')
assert 'MRMCD2017' in r.rendered_content
assert 'September %d' % (now().year + 1) in r.rendered_content
r = client.get('/mrmcd/events/2017/10/')
assert 'MRMCD2017' not in r.rendered_content
assert 'October 2017' in r.rendered_content
r = client.get('/mrmcd/events/?month=10&year=2017')
assert 'MRMCD2017' not in r.rendered_content
assert 'October 2017' in r.rendered_content