Auto-check-in for specific sales channels (#1409)

* Autocheckin data model/cosmetics

* Expose automatically checked-in OrderPositions

* Expose automatically checked-in OrderPositions in CSV/PDF Exports

* Fix some tests, try to fix MultiStringField/CheckboxSelectMultiple

* Actually fix MultiStringField/CheckboxSelectMultiple.
(Not pretty, but it works)

* Fix more tests

* Squash migration

* Also fix CSV/nameparts-test

* Changes for Autocheckin code-review

* Perform Auto-Checkins through new core plugin

* Update config-doc to reflect also checkinlists

* Explicitly output AutoCheckin Yes/No for CSV-Export (+ fix test)

* Move autocheckin from plugin to service

* API-doc

* Fix API-doc spelling

* Checkinlist-API and autocheckin order tests

* Performance improvement when reading checkinlists for autocheckin

Co-Authored-By: Raphael Michel <michel@rami.io>

* Autocheckin test for order created through API

* Resolve migration conflict
This commit is contained in:
Martin Gross
2019-10-08 15:50:22 +02:00
committed by Raphael Michel
co-authored by Raphael Michel
parent 05a1df244b
commit 748a389acb
20 changed files with 306 additions and 37 deletions
+59 -2
View File
@@ -147,6 +147,7 @@ def test_list_list(token_client, organizer, event, clist, item, subevent):
res = dict(TEST_LIST_RES)
res["id"] = clist.pk
res["limit_products"] = [item.pk]
res["auto_checkin_sales_channels"] = []
resp = token_client.get('/api/v1/organizers/{}/events/{}/checkinlists/'.format(organizer.slug, event.slug))
assert resp.status_code == 200
@@ -171,6 +172,7 @@ def test_list_detail(token_client, organizer, event, clist, item):
res["id"] = clist.pk
res["limit_products"] = [item.pk]
res["auto_checkin_sales_channels"] = []
resp = token_client.get('/api/v1/organizers/{}/events/{}/checkinlists/{}/'.format(organizer.slug, event.slug,
clist.pk))
assert resp.status_code == 200
@@ -196,6 +198,27 @@ def test_list_create(token_client, organizer, event, item, item_on_wrong_event):
assert cl.limit_products.count() == 1
assert not cl.all_products
resp = token_client.post(
'/api/v1/organizers/{}/events/{}/checkinlists/'.format(organizer.slug, event.slug),
{
"name": "VIP",
"limit_products": [item.pk],
"all_products": False,
"subevent": None,
"auto_checkin_sales_channels": [
"web"
]
},
format='json'
)
assert resp.status_code == 201
with scopes_disabled():
cl = CheckinList.objects.get(pk=resp.data['id'])
assert cl.name == "VIP"
assert cl.limit_products.count() == 1
assert not cl.all_products
assert "web" in cl.auto_checkin_sales_channels
resp = token_client.post(
'/api/v1/organizers/{}/events/{}/checkinlists/'.format(organizer.slug, event.slug),
{
@@ -224,6 +247,24 @@ def test_list_create_with_subevent(token_client, organizer, event, event3, item,
)
assert resp.status_code == 201
resp = token_client.post(
'/api/v1/organizers/{}/events/{}/checkinlists/'.format(organizer.slug, event.slug),
{
"name": "VIP",
"limit_products": [item.pk],
"all_products": True,
"subevent": subevent.pk,
"auto_checkin_sales_channels": [
"web"
]
},
format='json'
)
assert resp.status_code == 201
with scopes_disabled():
cl = CheckinList.objects.get(pk=resp.data['id'])
assert "web" in cl.auto_checkin_sales_channels
resp = token_client.post(
'/api/v1/organizers/{}/events/{}/checkinlists/'.format(organizer.slug, event.slug),
{
@@ -278,6 +319,20 @@ def test_list_update(token_client, organizer, event, clist):
cl = CheckinList.objects.get(pk=resp.data['id'])
assert cl.name == "VIP"
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/checkinlists/{}/'.format(organizer.slug, event.slug, clist.pk),
{
"auto_checkin_sales_channels": [
"web"
],
},
format='json'
)
assert resp.status_code == 200
with scopes_disabled():
cl = CheckinList.objects.get(pk=resp.data['id'])
assert "web" in cl.auto_checkin_sales_channels
@pytest.mark.django_db
def test_list_all_items_positions(token_client, organizer, event, clist, clist_all, item, other_item, order):
@@ -316,7 +371,8 @@ def test_list_all_items_positions(token_client, organizer, event, clist, clist_a
p1['checkins'] = [
{
'list': clist_all.pk,
'datetime': c.datetime.isoformat().replace('+00:00', 'Z')
'datetime': c.datetime.isoformat().replace('+00:00', 'Z'),
'auto_checked_in': False
}
]
resp = token_client.get('/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/?has_checkin=1'.format(
@@ -353,7 +409,8 @@ def test_list_all_items_positions(token_client, organizer, event, clist, clist_a
p2['checkins'] = [
{
'list': clist_all.pk,
'datetime': c.datetime.isoformat().replace('+00:00', 'Z')
'datetime': c.datetime.isoformat().replace('+00:00', 'Z'),
'auto_checked_in': False
}
]
resp = token_client.get(
+38 -1
View File
@@ -122,6 +122,12 @@ def order(event, item, taxrule, question):
return o
@pytest.fixture
def clist_autocheckin(event):
c = event.checkin_lists.create(name="Default", all_products=True, auto_checkin_sales_channels=['web'])
return c
TEST_ORDERPOSITION_RES = {
"id": 1,
"order": "FOO",
@@ -699,7 +705,7 @@ def test_orderposition_list(token_client, organizer, event, order, item, subeven
with scopes_disabled():
cl = event.checkin_lists.create(name="Default")
op.checkins.create(datetime=datetime.datetime(2017, 12, 26, 10, 0, 0, tzinfo=UTC), list=cl)
res['checkins'] = [{'datetime': '2017-12-26T10:00:00Z', 'list': cl.pk}]
res['checkins'] = [{'datetime': '2017-12-26T10:00:00Z', 'list': cl.pk, 'auto_checked_in': False}]
resp = token_client.get(
'/api/v1/organizers/{}/events/{}/orderpositions/?has_checkin=true'.format(organizer.slug, event.slug))
assert [res] == resp.data['results']
@@ -1432,6 +1438,37 @@ def test_order_create(token_client, organizer, event, item, quota, question):
assert answ.answer == "S"
@pytest.mark.django_db
def test_order_create_autocheckin(token_client, organizer, event, item, quota, question, clist_autocheckin):
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
res['positions'][0]['item'] = item.pk
res['positions'][0]['answers'][0]['question'] = question.pk
resp = token_client.post(
'/api/v1/organizers/{}/events/{}/orders/'.format(
organizer.slug, event.slug
), format='json', data=res
)
assert resp.status_code == 201
with scopes_disabled():
o = Order.objects.get(code=resp.data['code'])
assert "web" in clist_autocheckin.auto_checkin_sales_channels
assert o.positions.first().checkins.first().auto_checked_in
clist_autocheckin.auto_checkin_sales_channels = []
clist_autocheckin.save()
resp = token_client.post(
'/api/v1/organizers/{}/events/{}/orders/'.format(
organizer.slug, event.slug
), format='json', data=res
)
assert resp.status_code == 201
with scopes_disabled():
o = Order.objects.get(code=resp.data['code'])
assert clist_autocheckin.auto_checkin_sales_channels == []
assert o.positions.first().checkins.count() == 0
@pytest.mark.django_db
def test_order_create_invoice_address_optional(token_client, organizer, event, item, quota, question):
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
+31
View File
@@ -38,6 +38,12 @@ def event():
yield event
@pytest.fixture
def clist_autocheckin(event):
c = event.checkin_lists.create(name="Default", all_products=True, auto_checkin_sales_channels=['web'])
return c
@pytest.mark.django_db
def test_expiry_days(event):
today = now()
@@ -1848,3 +1854,28 @@ class OrderChangeManagerTests(TestCase):
self.ocm.change_subevent(self.op1, se2)
with self.assertRaises(OrderError):
self.ocm.add_position(self.ticket, None, price=Decimal('13.00'), subevent=se2, seat=self.seat_a1)
@pytest.mark.django_db
def test_autocheckin(clist_autocheckin, event):
today = now()
tr7 = event.tax_rules.create(rate=Decimal('17.00'))
ticket = Item.objects.create(event=event, name='Early-bird ticket', tax_rule=tr7,
default_price=Decimal('23.00'), admission=True)
cp1 = CartPosition.objects.create(
item=ticket, price=23, expires=now() + timedelta(days=1), event=event, cart_id="123"
)
order = _create_order(event, email='dummy@example.org', positions=[cp1],
now_dt=today, payment_provider=FreeOrderProvider(event),
locale='de')[0]
assert "web" in clist_autocheckin.auto_checkin_sales_channels
assert order.positions.first().checkins.first().auto_checked_in
clist_autocheckin.auto_checkin_sales_channels = []
clist_autocheckin.save()
order = _create_order(event, email='dummy@example.org', positions=[cp1],
now_dt=today, payment_provider=FreeOrderProvider(event),
locale='de')[0]
assert clist_autocheckin.auto_checkin_sales_channels == []
assert order.positions.first().checkins.count() == 0
+12 -10
View File
@@ -66,11 +66,11 @@ def test_csv_simple(event):
'questions': []
})
assert clean(content.decode()) == clean(""""Order code","Attendee name","Attendee name: Title","Attendee name:
First name","Attendee name: Middle name","Attendee name: Family name","Product","Price","Checked in","Secret",
"E-mail","Company","Voucher code","Order date","Requires special attention","Comment"
"FOO","Mr Peter A Jones","Mr","Peter","A","Jones","Ticket","23.00","","hutjztuxhkbtwnesv2suqv26k6ttytxx",
First name","Attendee name: Middle name","Attendee name: Family name","Product","Price","Checked in","Automatically
checked in","Secret","E-mail","Company","Voucher code","Order date","Requires special attention","Comment"
"FOO","Mr Peter A Jones","Mr","Peter","A","Jones","Ticket","23.00","","No","hutjztuxhkbtwnesv2suqv26k6ttytxx",
"dummy@dummy.test","","","2019-02-22","No",""
"FOO","Mrs Andrea J Zulu","Mrs","Andrea","J","Zulu","Ticket","13.00","","ggsngqtnmhx74jswjngw3fk8pfwz2a7k",
"FOO","Mrs Andrea J Zulu","Mrs","Andrea","J","Zulu","Ticket","13.00","","No","ggsngqtnmhx74jswjngw3fk8pfwz2a7k",
"dummy@dummy.test","","","2019-02-22","No",""
""")
@@ -90,10 +90,11 @@ def test_csv_order_by_name_parts(event): # noqa
})
assert clean(content.decode()) == clean(""""Order code","Attendee name","Attendee name: Title",
"Attendee name: First name","Attendee name: Middle name","Attendee name: Family name","Product","Price",
"Checked in","Secret","E-mail","Company","Voucher code","Order date","Requires special attention","Comment"
"FOO","Mrs Andrea J Zulu","Mrs","Andrea","J","Zulu","Ticket","13.00","","ggsngqtnmhx74jswjngw3fk8pfwz2a7k",
"Checked in","Automatically checked in","Secret","E-mail","Company","Voucher code","Order date","Requires special
attention","Comment"
"FOO","Mrs Andrea J Zulu","Mrs","Andrea","J","Zulu","Ticket","13.00","","No","ggsngqtnmhx74jswjngw3fk8pfwz2a7k",
"dummy@dummy.test","","","2019-02-22","No",""
"FOO","Mr Peter A Jones","Mr","Peter","A","Jones","Ticket","23.00","","hutjztuxhkbtwnesv2suqv26k6ttytxx",
"FOO","Mr Peter A Jones","Mr","Peter","A","Jones","Ticket","23.00","","No","hutjztuxhkbtwnesv2suqv26k6ttytxx",
"dummy@dummy.test","","","2019-02-22","No",""
""")
c = CSVCheckinList(event)
@@ -106,9 +107,10 @@ def test_csv_order_by_name_parts(event): # noqa
})
assert clean(content.decode()) == clean(""""Order code","Attendee name","Attendee name: Title",
"Attendee name: First name","Attendee name: Middle name","Attendee name: Family name","Product","Price",
"Checked in","Secret","E-mail","Company","Voucher code","Order date","Requires special attention","Comment"
"FOO","Mr Peter A Jones","Mr","Peter","A","Jones","Ticket","23.00","","hutjztuxhkbtwnesv2suqv26k6ttytxx",
"Checked in","Automatically checked in","Secret","E-mail","Company","Voucher code","Order date","Requires special
attention","Comment"
"FOO","Mr Peter A Jones","Mr","Peter","A","Jones","Ticket","23.00","","No","hutjztuxhkbtwnesv2suqv26k6ttytxx",
"dummy@dummy.test","","","2019-02-22","No",""
"FOO","Mrs Andrea J Zulu","Mrs","Andrea","J","Zulu","Ticket","13.00","","ggsngqtnmhx74jswjngw3fk8pfwz2a7k",
"FOO","Mrs Andrea J Zulu","Mrs","Andrea","J","Zulu","Ticket","13.00","","No","ggsngqtnmhx74jswjngw3fk8pfwz2a7k",
"dummy@dummy.test","","","2019-02-22","No",""
""")