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

@@ -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()