from datetime import datetime, timedelta, timezone from decimal import Decimal import pytest from django.utils.timezone import now from django_scopes import scopes_disabled from freezegun import freeze_time from pretix.base.models import ( Checkin, Event, Item, ItemAddOn, ItemCategory, LogEntry, Order, OrderPosition, Organizer, Team, User, ) from pretix.control.views.dashboards import checkin_widget from ..base import SoupTest, extract_form_fields @pytest.fixture def dashboard_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,tests.testdummy' ) event.settings.set('ticketoutput_testdummy__enabled', True) user = User.objects.create_user('dummy@dummy.dummy', 'dummy') item_ticket = Item.objects.create(event=event, name="Ticket", default_price=23, admission=True) item_mascot = Item.objects.create(event=event, name="Mascot", default_price=10, admission=False) t = Team.objects.create(organizer=o, can_view_orders=True, can_change_orders=True) t.members.add(user) t.limit_events.add(event) cl = event.checkin_lists.create(name="Default", all_products=True) event.settings.set('attendee_names_asked', True) event.settings.set('locales', ['en', 'de']) order_paid = Order.objects.create( code='FOO', event=event, email='dummy@dummy.test', status=Order.STATUS_PAID, datetime=now(), expires=now() + timedelta(days=10), total=33, locale='en' ) OrderPosition.objects.create( order=order_paid, item=item_ticket, variation=None, price=Decimal("23"), attendee_name_parts={"full_name": "Peter"} ) OrderPosition.objects.create( order=order_paid, item=item_mascot, variation=None, price=Decimal("10") ) return event, user, o, order_paid, item_ticket, item_mascot, cl @pytest.mark.django_db @scopes_disabled() def test_dashboard(dashboard_env): c = checkin_widget(dashboard_env[0]) assert '0/2' in c[0]['content'] @pytest.mark.django_db @scopes_disabled() def test_dashboard_pending_not_count(dashboard_env): c = checkin_widget(dashboard_env[0]) order_pending = Order.objects.create( code='FOO', event=dashboard_env[0], email='dummy@dummy.test', status=Order.STATUS_PENDING, datetime=now(), expires=now() + timedelta(days=10), total=23, locale='en' ) OrderPosition.objects.create( order=order_pending, item=dashboard_env[4], variation=None, price=Decimal("23"), attendee_name_parts={'full_name': "NotPaid"} ) assert '0/2' in c[0]['content'] @pytest.mark.django_db @scopes_disabled() def test_dashboard_with_checkin(dashboard_env): op = OrderPosition.objects.get( order=dashboard_env[3], item=dashboard_env[4] ) Checkin.objects.create(position=op, list=dashboard_env[6]) c = checkin_widget(dashboard_env[0]) assert '1/2' in c[0]['content'] @pytest.fixture def checkin_list_env(): # permission orga = Organizer.objects.create(name='Dummy', slug='dummy') user = User.objects.create_user('dummy@dummy.dummy', 'dummy') team = Team.objects.create(organizer=orga, can_view_orders=True, can_change_orders=True) team.members.add(user) # event event = Event.objects.create( organizer=orga, name='Dummy', slug='dummy', date_from=now(), plugins='pretix.plugins.banktransfer,tests.testdummy' ) event.settings.set('ticketoutput_testdummy__enabled', True) event.settings.set('attendee_names_asked', True) event.settings.set('locales', ['en', 'de']) team.limit_events.add(event) cl = event.checkin_lists.create(name="Default", all_products=True) # item item_ticket = Item.objects.create(event=event, name="Ticket", default_price=23, admission=True, position=0) item_mascot = Item.objects.create(event=event, name="Mascot", default_price=10, admission=False, position=1) # order order_pending = Order.objects.create( code='PENDING', event=event, email='dummy@dummy.test', status=Order.STATUS_PENDING, datetime=now(), expires=now() + timedelta(days=10), total=23, locale='en' ) order_a1 = Order.objects.create( code='A1', event=event, email='a1dummy@dummy.test', status=Order.STATUS_PAID, datetime=now(), expires=now() + timedelta(days=10), total=33, locale='en' ) order_a2 = Order.objects.create( code='A2', event=event, email='a2dummy@dummy.test', status=Order.STATUS_PAID, datetime=now(), expires=now() + timedelta(days=10), total=23, locale='en' ) order_a3 = Order.objects.create( code='A3', event=event, email='a3dummy@dummy.test', status=Order.STATUS_PAID, datetime=now(), expires=now() + timedelta(days=10), total=23, locale='en' ) # order position op_pending_ticket = OrderPosition.objects.create( order=order_pending, item=item_ticket, variation=None, price=Decimal("23"), attendee_name_parts={'full_name': "Pending"} ) op_a1_ticket = OrderPosition.objects.create( order=order_a1, item=item_ticket, variation=None, price=Decimal("23"), attendee_name_parts={'full_name': "A1"} ) op_a1_mascot = OrderPosition.objects.create( order=order_a1, item=item_mascot, variation=None, price=Decimal("10") ) op_a2_ticket = OrderPosition.objects.create( order=order_a2, item=item_ticket, variation=None, price=Decimal("23"), attendee_name_parts={'full_name': "A2"} ) op_a3_ticket = OrderPosition.objects.create( order=order_a3, item=item_ticket, variation=None, price=Decimal("23"), attendee_name_parts={'full_name': "a4"}, # a3 attendee is a4 attendee_email="a3company@dummy.test" ) # checkin Checkin.objects.create(position=op_a1_ticket, datetime=now() + timedelta(minutes=1), list=cl) Checkin.objects.create(position=op_a3_ticket, list=cl) return event, user, orga, [item_ticket, item_mascot], [order_pending, order_a1, order_a2, order_a3], \ [op_pending_ticket, op_a1_ticket, op_a1_mascot, op_a2_ticket, op_a3_ticket], cl @pytest.mark.django_db @pytest.mark.parametrize("order_key, expected", [ ('', ['A1Ticket', 'A1Mascot', 'A2Ticket', 'A3Ticket']), ('-code', ['A3Ticket', 'A2Ticket', 'A1Ticket', 'A1Mascot']), ('code', ['A1Mascot', 'A1Ticket', 'A2Ticket', 'A3Ticket']), ('-email', ['A3Ticket', 'A2Ticket', 'A1Ticket', 'A1Mascot']), ('email', ['A1Mascot', 'A1Ticket', 'A2Ticket', 'A3Ticket']), ('-status', ['A3Ticket', 'A1Ticket', 'A2Ticket', 'A1Mascot']), ('status', ['A1Mascot', 'A2Ticket', 'A1Ticket', 'A3Ticket']), ('-timestamp', ['A1Ticket', 'A3Ticket', 'A2Ticket', 'A1Mascot']), # A1 checkin date > A3 checkin date ('timestamp', ['A1Mascot', 'A2Ticket', 'A3Ticket', 'A1Ticket']), ('-name', ['A3Ticket', 'A2Ticket', 'A1Ticket', 'A1Mascot']), ('name', ['A1Mascot', 'A1Ticket', 'A2Ticket', 'A3Ticket']), # mascot doesn't include attendee name ('-item', ['A3Ticket', 'A2Ticket', 'A1Ticket', 'A1Mascot']), ('item', ['A1Mascot', 'A1Ticket', 'A2Ticket', 'A3Ticket']), ]) def test_checkins_list_ordering(client, checkin_list_env, order_key, expected): client.login(email='dummy@dummy.dummy', password='dummy') response = client.get('/control/event/dummy/dummy/checkinlists/{}/?ordering='.format(checkin_list_env[6].pk) + order_key) qs = response.context['entries'] item_keys = [q.order.code + str(q.item.name) for q in qs] assert item_keys == expected @pytest.mark.django_db @pytest.mark.parametrize("query, expected", [ ('status=&item=&user=', ['A1Ticket', 'A1Mascot', 'A2Ticket', 'A3Ticket']), ('status=1&item=&user=', ['A1Ticket', 'A3Ticket']), ('status=0&item=&user=', ['A1Mascot', 'A2Ticket']), ('status=&item=&user=a3dummy', ['A3Ticket']), # match order email ('status=&item=&user=a3dummy', ['A3Ticket']), # match order email, ('status=&item=&user=a4', ['A3Ticket']), # match attendee name ('status=&item=&user=a3company', ['A3Ticket']), # match attendee email ('status=1&item=&user=a3company', ['A3Ticket']), ]) def test_checkins_list_filter(client, checkin_list_env, query, expected): client.login(email='dummy@dummy.dummy', password='dummy') response = client.get('/control/event/dummy/dummy/checkinlists/{}/?'.format(checkin_list_env[6].pk) + query) qs = response.context['entries'] item_keys = [q.order.code + str(q.item.name) for q in qs] assert item_keys == expected @pytest.mark.django_db def test_checkins_item_filter(client, checkin_list_env): client.login(email='dummy@dummy.dummy', password='dummy') for item in checkin_list_env[3]: response = client.get('/control/event/dummy/dummy/checkinlists/{}/?item={}'.format(checkin_list_env[6].pk, item.pk)) assert all(i.item.id == item.id for i in response.context['entries']) @pytest.mark.django_db @pytest.mark.parametrize("query, expected", [ ('status=&item=&user=&ordering=', ['A1Ticket', 'A1Mascot', 'A2Ticket', 'A3Ticket']), ('status=1&item=&user=&ordering=timestamp', ['A3Ticket', 'A1Ticket']), ('status=0&item=&user=&ordering=-name', ['A2Ticket', 'A1Mascot']), ]) def test_checkins_list_mixed(client, checkin_list_env, query, expected): client.login(email='dummy@dummy.dummy', password='dummy') response = client.get('/control/event/dummy/dummy/checkinlists/{}/?{}'.format(checkin_list_env[6].pk, query)) qs = response.context['entries'] item_keys = [q.order.code + str(q.item.name) for q in qs] assert item_keys == expected @pytest.mark.django_db def test_manual_checkins(client, checkin_list_env): client.login(email='dummy@dummy.dummy', password='dummy') with scopes_disabled(): assert not checkin_list_env[5][3].checkins.exists() client.post('/control/event/dummy/dummy/checkinlists/{}/'.format(checkin_list_env[6].pk), { 'checkin': [checkin_list_env[5][3].pk] }) with scopes_disabled(): assert checkin_list_env[5][3].checkins.exists() assert LogEntry.objects.filter( action_type='pretix.event.checkin', object_id=checkin_list_env[5][3].order.pk ).exists() @pytest.mark.django_db def test_manual_checkins_revert(client, checkin_list_env): client.login(email='dummy@dummy.dummy', password='dummy') with scopes_disabled(): assert not checkin_list_env[5][3].checkins.exists() client.post('/control/event/dummy/dummy/checkinlists/{}/'.format(checkin_list_env[6].pk), { 'checkin': [checkin_list_env[5][3].pk] }) client.post('/control/event/dummy/dummy/checkinlists/{}/'.format(checkin_list_env[6].pk), { 'checkin': [checkin_list_env[5][3].pk], 'revert': 'true' }) with scopes_disabled(): assert not checkin_list_env[5][3].checkins.exists() assert LogEntry.objects.filter( action_type='pretix.event.checkin', object_id=checkin_list_env[5][3].order.pk ).exists() assert LogEntry.objects.filter( action_type='pretix.event.checkin.reverted', object_id=checkin_list_env[5][3].order.pk ).exists() @pytest.fixture def checkin_list_with_addon_env(): # permission orga = Organizer.objects.create(name='Dummy', slug='dummy') user = User.objects.create_user('dummy@dummy.dummy', 'dummy') team = Team.objects.create(organizer=orga, can_view_orders=True, can_change_orders=True) team.members.add(user) # event event = Event.objects.create( organizer=orga, name='Dummy', slug='dummy', date_from=now(), plugins='pretix.plugins.banktransfer,tests.testdummy' ) event.settings.set('ticketoutput_testdummy__enabled', True) event.settings.set('attendee_names_asked', True) event.settings.set('locales', ['en', 'de']) team.limit_events.add(event) cl = event.checkin_lists.create(name="Default", all_products=True) # item cat_adm = ItemCategory.objects.create(event=event, name="Admission") cat_workshop = ItemCategory.objects.create(event=event, name="Admission", is_addon=True) item_ticket = Item.objects.create(event=event, name="Ticket", default_price=23, admission=True, category=cat_adm) item_workshop = Item.objects.create(event=event, name="Workshop", default_price=10, admission=False, category=cat_workshop) ItemAddOn.objects.create(base_item=item_ticket, addon_category=cat_workshop, min_count=0, max_count=2) # order order_pending = Order.objects.create( code='PENDING', event=event, email='dummy@dummy.test', status=Order.STATUS_PENDING, datetime=now(), expires=now() + timedelta(days=10), total=23, locale='en' ) order_a1 = Order.objects.create( code='A1', event=event, email='a1dummy@dummy.test', status=Order.STATUS_PAID, datetime=now(), expires=now() + timedelta(days=10), total=33, locale='en' ) order_a2 = Order.objects.create( code='A2', event=event, email='a2dummy@dummy.test', status=Order.STATUS_PAID, datetime=now(), expires=now() + timedelta(days=10), total=23, locale='en' ) # order position op_pending_ticket = OrderPosition.objects.create( order=order_pending, item=item_ticket, variation=None, price=Decimal("23"), attendee_name_parts={'full_name': "Pending"} ) op_a1_ticket = OrderPosition.objects.create( order=order_a1, item=item_ticket, variation=None, price=Decimal("23"), attendee_name_parts={'full_name': "A1"} ) op_a1_workshop = OrderPosition.objects.create( order=order_a1, item=item_workshop, variation=None, price=Decimal("10"), addon_to=op_a1_ticket ) op_a2_ticket = OrderPosition.objects.create( order=order_a2, item=item_ticket, variation=None, price=Decimal("23"), attendee_name_parts={'full_name': "A2"} ) # checkin Checkin.objects.create(position=op_a1_ticket, datetime=now() + timedelta(minutes=1), list=cl) return event, user, orga, [item_ticket, item_workshop], [order_pending, order_a1, order_a2], \ [op_pending_ticket, op_a1_ticket, op_a1_workshop, op_a2_ticket], cl @pytest.mark.django_db def test_checkins_attendee_name_from_addon_available(client, checkin_list_with_addon_env): client.login(email='dummy@dummy.dummy', password='dummy') response = client.get('/control/event/dummy/dummy/checkinlists/{}/'.format(checkin_list_with_addon_env[6].pk)) qs = response.context['entries'] item_keys = [q.order.code + str(q.item.name) + (str(q.addon_to.attendee_name) if q.addon_to is not None else str(q.attendee_name)) for q in qs] assert item_keys == ['A1TicketA1', 'A1WorkshopA1', 'A2TicketA2'] # A1Workshop comes from addon_to position class CheckinListFormTest(SoupTest): @scopes_disabled() 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(2013, 12, 26, tzinfo=timezone.utc), ) self.event1.settings.timezone = 'Europe/Berlin' t = Team.objects.create(organizer=self.orga1, can_change_event_settings=True, can_view_orders=True) t.members.add(self.user) t.limit_events.add(self.event1) self.client.login(email='dummy@dummy.dummy', password='dummy') self.item_ticket = Item.objects.create(event=self.event1, name="Ticket", default_price=23, admission=True) def test_create(self): doc = self.get_doc('/control/event/%s/%s/checkinlists/add' % (self.orga1.slug, self.event1.slug)) form_data = extract_form_fields(doc.select('.container-fluid form')[0]) form_data['name'] = 'All' form_data['all_products'] = 'on' doc = self.post_doc('/control/event/%s/%s/checkinlists/add' % (self.orga1.slug, self.event1.slug), form_data) assert doc.select(".alert-success") self.assertIn("All", doc.select("#page-wrapper table")[0].text) with scopes_disabled(): assert self.event1.checkin_lists.get( name='All', all_products=True ) def test_update(self): with scopes_disabled(): cl = self.event1.checkin_lists.create(name='All', all_products=True) doc = self.get_doc('/control/event/%s/%s/checkinlists/%s/change' % (self.orga1.slug, self.event1.slug, cl.id)) form_data = extract_form_fields(doc.select('.container-fluid form')[0]) form_data['all_products'] = '' form_data['limit_products'] = str(self.item_ticket.pk) doc = self.post_doc('/control/event/%s/%s/checkinlists/%s/change' % (self.orga1.slug, self.event1.slug, cl.id), form_data) assert doc.select(".alert-success") cl.refresh_from_db() assert not cl.all_products with scopes_disabled(): assert list(cl.limit_products.all()) == [self.item_ticket] @freeze_time("2020-01-02 02:55:00+01:00") def test_update_exit_all_at_current_day(self): with scopes_disabled(): cl = self.event1.checkin_lists.create(name='All', all_products=True) doc = self.get_doc('/control/event/%s/%s/checkinlists/%s/change' % (self.orga1.slug, self.event1.slug, cl.id)) form_data = extract_form_fields(doc.select('.container-fluid form')[0]) form_data['exit_all_at'] = '03:00:00' doc = self.post_doc('/control/event/%s/%s/checkinlists/%s/change' % (self.orga1.slug, self.event1.slug, cl.id), form_data) assert doc.select(".alert-success") cl.refresh_from_db() assert cl.exit_all_at == self.event1.timezone.localize(datetime(2020, 1, 2, 3, 0)) @freeze_time("2020-01-02 03:05:00+01:00") def test_update_exit_all_at_next_day(self): with scopes_disabled(): cl = self.event1.checkin_lists.create(name='All', all_products=True) doc = self.get_doc('/control/event/%s/%s/checkinlists/%s/change' % (self.orga1.slug, self.event1.slug, cl.id)) form_data = extract_form_fields(doc.select('.container-fluid form')[0]) form_data['exit_all_at'] = '03:00:00' doc = self.post_doc('/control/event/%s/%s/checkinlists/%s/change' % (self.orga1.slug, self.event1.slug, cl.id), form_data) assert doc.select(".alert-success") cl.refresh_from_db() assert cl.exit_all_at == self.event1.timezone.localize(datetime(2020, 1, 3, 3, 0)) def test_delete(self): with scopes_disabled(): cl = self.event1.checkin_lists.create(name='All', all_products=True) doc = self.get_doc('/control/event/%s/%s/checkinlists/%s/delete' % (self.orga1.slug, self.event1.slug, cl.id)) form_data = extract_form_fields(doc.select('.container-fluid form')[0]) doc = self.post_doc('/control/event/%s/%s/checkinlists/%s/delete' % (self.orga1.slug, self.event1.slug, cl.id), form_data) assert doc.select(".alert-success") self.assertNotIn("VAT", doc.select("#page-wrapper")[0].text) with scopes_disabled(): assert not self.event1.checkin_lists.exists()