forked from CGM_Public/pretix_original
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:
@@ -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'),
|
||||
|
||||
Reference in New Issue
Block a user