diff --git a/libpretixsync/src/main/java/eu/pretix/libpretixsync/check/AsyncCheckProvider.kt b/libpretixsync/src/main/java/eu/pretix/libpretixsync/check/AsyncCheckProvider.kt index 91b042be..b37f653c 100644 --- a/libpretixsync/src/main/java/eu/pretix/libpretixsync/check/AsyncCheckProvider.kt +++ b/libpretixsync/src/main/java/eu/pretix/libpretixsync/check/AsyncCheckProvider.kt @@ -328,9 +328,9 @@ class AsyncCheckProvider(private val config: ConfigStore, private val db: SyncDa } if (!list.allItems) { - val is_in_list = db.checkInListQueries.checkIfItemIsInList( + val is_in_list = db.checkInListQueries.checkIfItemIsInListByServerId( checkin_list_id = list.id, - item_id = decoded.item, + item_server_id = decoded.item, ).executeAsOne() if (is_in_list == 0L) { storeFailedCheckin(eventSlug, listId, "product", ticketid, type, item = decoded.item, variation = decoded.variation, subevent = decoded.subevent, nonce = nonce) @@ -757,9 +757,9 @@ class AsyncCheckProvider(private val config: ConfigStore, private val db: SyncDa } if (!list.allItems) { - val is_in_list = db.checkInListQueries.checkIfItemIsInList( + val is_in_list = db.checkInListQueries.checkIfItemIsInListByServerId( checkin_list_id = list.id, - item_id = item.id, + item_server_id = item.serverId, ).executeAsOne() if (is_in_list == 0L) { storeFailedCheckin(eventSlug, list.serverId, "product", position.secret!!, type, position = position.serverId, item = positionItem.serverId, variation = position.variationServerId, subevent = position.subEventServerId, nonce = nonce) diff --git a/libpretixsync/src/main/sqldelight/common/eu/pretix/libpretixsync/sqldelight/CheckInList.sq b/libpretixsync/src/main/sqldelight/common/eu/pretix/libpretixsync/sqldelight/CheckInList.sq index d0bbefc7..07f227d2 100644 --- a/libpretixsync/src/main/sqldelight/common/eu/pretix/libpretixsync/sqldelight/CheckInList.sq +++ b/libpretixsync/src/main/sqldelight/common/eu/pretix/libpretixsync/sqldelight/CheckInList.sq @@ -86,10 +86,11 @@ SELECT ItemId AS id, CheckInListId FROM CheckInList_Item WHERE CheckInListId = :checkin_list_id; -checkIfItemIsInList: +checkIfItemIsInListByServerId: SELECT COUNT(*) FROM CheckInList_Item -WHERE CheckInListId = :checkin_list_id AND ItemId = :item_id; +LEFT JOIN Item ON CheckInList_Item.ItemId = Item.id +WHERE CheckInListId = :checkin_list_id AND Item.server_id = :item_server_id; -- for tests only: testUpdateJsonData: diff --git a/libpretixsync/src/test/java/eu/pretix/libpretixsync/check/AsyncCheckProviderTest.kt b/libpretixsync/src/test/java/eu/pretix/libpretixsync/check/AsyncCheckProviderTest.kt index b20e1c3e..bd22efe0 100644 --- a/libpretixsync/src/test/java/eu/pretix/libpretixsync/check/AsyncCheckProviderTest.kt +++ b/libpretixsync/src/test/java/eu/pretix/libpretixsync/check/AsyncCheckProviderTest.kt @@ -38,6 +38,7 @@ class AsyncCheckProviderTest : BaseDatabaseTest() { EventSyncAdapter(db, "demo2", "demo2", fakeApi!!, "", null).standaloneRefreshFromJSON(jsonResource("events/event2.json")) ItemSyncAdapter(db, FakeFileStorage(), "demo", fakeApi!!, "", null).standaloneRefreshFromJSON(jsonResource("items/item1.json")) ItemSyncAdapter(db, FakeFileStorage(), "demo", fakeApi!!, "", null).standaloneRefreshFromJSON(jsonResource("items/item2.json")) + ItemSyncAdapter(db, FakeFileStorage(), "demo", fakeApi!!, "", null).standaloneRefreshFromJSON(jsonResource("items/item3.json")) ItemSyncAdapter(db, FakeFileStorage(), "demo2", fakeApi!!, "", null).standaloneRefreshFromJSON(jsonResource("items/event2-item3.json")) CheckInListSyncAdapter(db, FakeFileStorage(), "demo", fakeApi!!, "", null, 0).standaloneRefreshFromJSON( jsonResource("checkinlists/list1.json") @@ -57,6 +58,9 @@ class AsyncCheckProviderTest : BaseDatabaseTest() { CheckInListSyncAdapter(db, FakeFileStorage(), "demo", fakeApi!!, "", null, 0).standaloneRefreshFromJSON( jsonResource("checkinlists/list6.json") ) + CheckInListSyncAdapter(db, FakeFileStorage(), "demo", fakeApi!!, "", null, 0).standaloneRefreshFromJSON( + jsonResource("checkinlists/list10.json") + ) CheckInListSyncAdapter(db, FakeFileStorage(), "demo2", fakeApi!!, "", null, 0).standaloneRefreshFromJSON( jsonResource("checkinlists/event2-list7.json") ) @@ -72,6 +76,7 @@ class AsyncCheckProviderTest : BaseDatabaseTest() { osa.standaloneRefreshFromJSON(jsonResource("orders/order7.json")) osa.standaloneRefreshFromJSON(jsonResource("orders/order8.json")) osa.standaloneRefreshFromJSON(jsonResource("orders/order9.json")) + osa.standaloneRefreshFromJSON(jsonResource("orders/order10.json")) val osa2 = OrderSyncAdapter(db, FakeFileStorage(), "demo2", 0, true, false, fakeApi!!, "", null) osa2.standaloneRefreshFromJSON(jsonResource("orders/event2-order1.json")) } @@ -90,6 +95,18 @@ class AsyncCheckProviderTest : BaseDatabaseTest() { assertEquals("kfndgffgyw4tdgcacx6bb3bgemq69cxj", qciList[0].secret) } + @Test + fun testSimpleSuccessFilteredList() { + val r = p!!.check(mapOf("demo" to 10L), "order10_item1_secret_verysecret") + assertEquals(TicketCheckProvider.CheckResult.Type.VALID, r.type) + assertEquals("Regular ticket", r.ticket) + assertEquals(null, r.variation) + + val qciList = db.queuedCheckInQueries.selectAll().executeAsList() + assertEquals(1, qciList.size.toLong()) + assertEquals("order10_item1_secret_verysecret", qciList[0].secret) + } + @Test fun testEciClean() { val r = p!!.check(mapOf("demo" to 1L), "\\000001kfndgffgyw4tdgcacx6bb3bgemq69cxj") @@ -1013,9 +1030,9 @@ class AsyncCheckProviderTest : BaseDatabaseTest() { fun testStatusInfo() { val sr = p!!.status("demo", 1L) assertEquals("All", sr.eventName) - assertEquals(19, sr.totalTickets) + assertEquals(20, sr.totalTickets) assertEquals(2, sr.alreadyScanned) - assertEquals(2, sr.items!!.size) + assertEquals(3, sr.items!!.size) val i = sr.items!![0] assertEquals(1, i.id) assertEquals(8, i.total) @@ -1031,6 +1048,14 @@ class AsyncCheckProviderTest : BaseDatabaseTest() { assertEquals(db.queuedCheckInQueries.count().executeAsOne(), 1L) } + @Test + fun testSignedAndValidServerId() { + // Regression test: make sure that signed checkin passes even if local item id != server item id + val r = p!!.check(mapOf("demo" to 10L), "==QCiFO1apLFypH1BXorPlbn4efNA0aCAsO1Z2hsmY/hKTI3/UVSlON/Vw8WSYof/1tz5tepBltUAg4GnzpOBSSlqARYhFWYhFWYhFWCKAEANAQA") + assertEquals(TicketCheckProvider.CheckResult.Type.VALID, r.type) + assertEquals(db.queuedCheckInQueries.count().executeAsOne(), 1L) + } + @Test fun testSignedAndNotYetValid() { val p2 = AsyncCheckProvider(configStore!!, db) diff --git a/libpretixsync/src/testFixtures/resources/checkinlists/list10.json b/libpretixsync/src/testFixtures/resources/checkinlists/list10.json new file mode 100644 index 00000000..f69d8da1 --- /dev/null +++ b/libpretixsync/src/testFixtures/resources/checkinlists/list10.json @@ -0,0 +1,13 @@ +{ + "id": 10, + "name": "Only product 42", + "all_products": false, + "limit_products": [42], + "subevent": null, + "checkin_count": 24, + "position_count": 49180, + "include_pending": false, + "allow_multiple_entries": false, + "allow_entry_after_exit": true, + "rules": {} +} diff --git a/libpretixsync/src/testFixtures/resources/items/item3.json b/libpretixsync/src/testFixtures/resources/items/item3.json new file mode 100644 index 00000000..0535018d --- /dev/null +++ b/libpretixsync/src/testFixtures/resources/items/item3.json @@ -0,0 +1,37 @@ +{ + "id": 42, + "category": null, + "name": { + "en": "Regular ticket" + }, + "internal_name": null, + "active": true, + "sales_channels": [ + "web" + ], + "description": {}, + "default_price": "23.00", + "free_price": false, + "tax_rate": "0.00", + "tax_rule": null, + "admission": true, + "position": 0, + "picture": null, + "available_from": null, + "available_until": null, + "require_voucher": false, + "hide_without_voucher": false, + "allow_cancel": true, + "require_bundling": false, + "min_per_order": null, + "max_per_order": null, + "checkin_attention": true, + "has_variations": false, + "variations": [], + "addons": [], + "bundles": [], + "original_price": null, + "require_approval": false, + "generate_tickets": null, + "issue_giftcard": false +} diff --git a/libpretixsync/src/testFixtures/resources/orders/order10.json b/libpretixsync/src/testFixtures/resources/orders/order10.json new file mode 100644 index 00000000..5236ae11 --- /dev/null +++ b/libpretixsync/src/testFixtures/resources/orders/order10.json @@ -0,0 +1,76 @@ +{ + "code": "ABC10", + "status": "p", + "testmode": false, + "secret": "order10secret_verysecret", + "email": "order10@examples.com", + "locale": "en", + "datetime": "2019-01-01T00:00:55Z", + "expires": "2020-03-03T16:50:46.773161Z", + "payment_date": null, + "payment_provider": "banktransfer", + "fees": [], + "total": "35.00", + "comment": "", + "invoice_address": null, + "positions": [ + { + "id": 27427, + "order": "ABC10", + "positionid": 1, + "item": 42, + "variation": 2, + "price": "23.00", + "attendee_name": "Attendee 10", + "attendee_name_parts": { + "_scheme": "full", + "full_name": "Attendee 10" + }, + "attendee_email": null, + "voucher": null, + "tax_rate": "0.00", + "tax_value": "0.00", + "secret": "order10_item1_secret_verysecret", + "addon_to": null, + "subevent": null, + "checkins": [ + { + "id": 1, + "datetime": "2019-03-04T17:34:38.221090Z", + "list": 9 + } + ], + "downloads": [], + "answers": [], + "tax_rule": null, + "pseudonymization_id": "ORDER10IT1" + } + ], + "downloads": [], + "checkin_attention": false, + "last_modified": "2019-04-02T07:01:49.735632Z", + "payments": [ + { + "local_id": 1, + "state": "confirmed", + "amount": "35.00", + "created": "2019-03-04T16:30:39.084282Z", + "payment_date": null, + "provider": "banktransfer" + } + ], + "refunds": [ + { + "local_id": 1, + "state": "created", + "source": "admin", + "amount": "35.00", + "payment": null, + "created": "2019-04-02T07:01:51.449258Z", + "execution_date": null, + "provider": "manual" + } + ], + "require_approval": false, + "sales_channel": "web" +}