text: #
  # This file is part of pretix (Community Edition).
  #
  # Copyright (C) 2014-2020 Raphael Michel and contributors
  # Copyright (C) 2020-2021 rami.io GmbH and contributors
  #
  # This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
  # Public License as published by the Free Software Foundation in version 3 of the License.
  #
  # ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
  # applicable granting you additional permissions and placing additional restrictions on your usage of this software.
  # Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
  # this file, see <https://pretix.eu/about/en/license>.
  #
  # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
  # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
  # details.
  #
  # You should have received a copy of the GNU Affero General Public License along with this program.  If not, see
  # <https://www.gnu.org/licenses/>.
  #
  
  # This file is based on an earlier version of pretix which was released under the Apache License 2.0. The full text of
  # the Apache License 2.0 can be obtained at <http://www.apache.org/licenses/LICENSE-2.0>.
  #
  # This file may have since been changed and any changes are released under the terms of AGPLv3 as described above. A
  # full history of changes and contributors is available at <https://github.com/pretix/pretix>.
  #
  # This file contains Apache-licensed contributions copyrighted by: Patrick Arminio, Ture Gjørup, pajowu
  #
  # Unless required by applicable law or agreed to in writing, software distributed under the Apache License 2.0 is
  # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  # License for the specific language governing permissions and limitations under the License.
  
  import time
  from datetime import datetime, timedelta
  from decimal import Decimal
  from unittest import mock
  
  import pytest
  from django_countries.fields import Country
  from django_scopes import scopes_disabled
  from pytz import UTC
  
  from pretix.base.models import (
      CartPosition, InvoiceAddress, Item, ItemAddOn, ItemBundle, ItemCategory,
      ItemVariation, Order, OrderPosition, Question, QuestionOption, Quota,
  )
  from pretix.base.models.orders import OrderFee
  
  
  @pytest.fixture
  def category(event):
      return event.categories.create(name="Tickets")
  
  
  @pytest.fixture
  def category2(event2):
      return event2.categories.create(name="Tickets2")
  
  
  @pytest.fixture
  def category3(event, item):
      cat = event.categories.create(name="Tickets")
      item.category = cat
      item.save()
      return cat
  
  
  @pytest.fixture
  def order(event, item, taxrule):
      testtime = datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC)
  
      with mock.patch('django.utils.timezone.now') as mock_now:
          mock_now.return_value = testtime
          o = Order.objects.create(
              code='FOO', event=event, email='dummy@dummy.test',
              status=Order.STATUS_PENDING, secret="k24fiuwvu8kxz3y1",
              datetime=datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC),
              expires=datetime(2017, 12, 10, 10, 0, 0, tzinfo=UTC),
              total=23, locale='en'
          )
          o.fees.create(fee_type=OrderFee.FEE_TYPE_PAYMENT, value=Decimal('0.25'), tax_rate=Decimal('19.00'),
                        tax_value=Decimal('0.05'), tax_rule=taxrule)
          InvoiceAddress.objects.create(order=o, company="Sample company", country=Country('NZ'))
          return o
  
  
  @pytest.fixture
  def order_position(item, order, taxrule, variations):
      op = OrderPosition.objects.create(
          order=order,
          item=item,
          variation=variations[0],
          tax_rule=taxrule,
          tax_rate=taxrule.rate,
          tax_value=Decimal("3"),
          price=Decimal("23"),
          attendee_name_parts={'full_name': "Peter"},
          secret="z3fsn8jyufm5kpk768q69gkbyr5f4h6w"
      )
      return op
  
  
  @pytest.fixture
  def cart_position(event, item, variations):
      testtime = datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC)
  
      with mock.patch('django.utils.timezone.now') as mock_now:
          mock_now.return_value = testtime
          c = CartPosition.objects.create(
              event=event,
              item=item,
              datetime=testtime,
              expires=testtime + timedelta(days=1),
              variation=variations[0],
              price=Decimal("23"),
              cart_id="z3fsn8jyufm5kpk768q69gkbyr5f4h6w"
          )
          return c
  
  
  TEST_CATEGORY_RES = {
      "name": {"en": "Tickets"},
      "description": {"en": ""},
      "internal_name": None,
      "position": 0,
      "is_addon": False
  }
  
  
  @pytest.mark.django_db
  def test_category_list(token_client, organizer, event, team, category):
      res = dict(TEST_CATEGORY_RES)
      res["id"] = category.pk
      resp = token_client.get('/api/v1/organizers/{}/events/{}/categories/'.format(organizer.slug, event.slug))
      assert resp.status_code == 200
      assert [res] == resp.data['results']
      resp = token_client.get('/api/v1/organizers/{}/events/{}/categories/?is_addon=false'.format(
          organizer.slug, event.slug))
      assert resp.status_code == 200
      assert [res] == resp.data['results']
      resp = token_client.get('/api/v1/organizers/{}/events/{}/categories/?is_addon=true'.format(
          organizer.slug, event.slug))
      assert resp.status_code == 200
      assert [] == resp.data['results']
      category.is_addon = True
      category.save()
      res["is_addon"] = True
      resp = token_client.get('/api/v1/organizers/{}/events/{}/categories/?is_addon=true'.format(
          organizer.slug, event.slug))
      assert resp.status_code == 200
      assert [res] == resp.data['results']
  
      category.log_action('foo')
      resp = token_client.get('/api/v1/organizers/{}/events/{}/categories/'.format(
          organizer.slug, event.slug))
      assert resp.status_code == 200
      lmd = resp['Last-Modified']
      assert lmd
      time.sleep(1)
      resp = token_client.get('/api/v1/organizers/{}/events/{}/categories/'.format(
          organizer.slug, event.slug), HTTP_IF_MODIFIED_SINCE=lmd)
      assert resp.status_code == 304
      time.sleep(1)
      category.log_action('foo')
      resp = token_client.get('/api/v1/organizers/{}/events/{}/categories/'.format(
          organizer.slug, event.slug), HTTP_IF_MODIFIED_SINCE=lmd)
      assert resp.status_code == 200
  
  
  @pytest.mark.django_db
  def test_category_detail(token_client, organizer, event, team, category):
      res = dict(TEST_CATEGORY_RES)
      res["id"] = category.pk
      resp = token_client.get('/api/v1/organizers/{}/events/{}/categories/{}/'.format(organizer.slug, event.slug,
                                                                                      category.pk))
      assert resp.status_code == 200
      assert res == resp.data
  
  
  @pytest.mark.django_db
  def test_category_create(token_client, organizer, event, team):
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/categories/'.format(organizer.slug, event.slug),
          {
              "name": {"en": "Tickets"},
              "description": {"en": ""},
              "position": 0,
              "is_addon": False
          },
          format='json'
      )
      assert resp.status_code == 201
  
  
  @pytest.mark.django_db
  def test_category_update(token_client, organizer, event, team, category):
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/categories/{}/'.format(organizer.slug, event.slug, category.pk),
          {
              "name": {"en": "Test"},
          },
          format='json'
      )
      assert resp.status_code == 200
      with scopes_disabled():
          assert ItemCategory.objects.get(pk=category.pk).name == {"en": "Test"}
  
  
  @pytest.mark.django_db
  def test_category_update_wrong_event(token_client, organizer, event2, category):
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/categories/{}/'.format(organizer.slug, event2.slug, category.pk),
          {
              "name": {"en": "Test"},
          },
          format='json'
      )
      assert resp.status_code == 404
  
  
  @pytest.mark.django_db
  def test_category_delete(token_client, organizer, event, category3, item):
      resp = token_client.delete(
          '/api/v1/organizers/{}/events/{}/categories/{}/'.format(organizer.slug, event.slug, category3.pk))
      assert resp.status_code == 204
      with scopes_disabled():
          assert not event.categories.filter(pk=category3.id).exists()
          assert Item.objects.get(pk=item.pk).category is None
  
  
  @pytest.fixture
  def item(event):
      item = event.items.create(name="Budget Ticket", default_price=23)
      item.meta_values.create(property=event.item_meta_properties.first(), value="Tuesday")
      return item
  
  
  @pytest.fixture
  def item2(event2):
      return event2.items.create(name="Budget Ticket", default_price=23)
  
  
  @pytest.fixture
  def item3(event):
      return event.items.create(name="Budget Ticket", default_price=23)
  
  
  TEST_ITEM_RES = {
      "name": {"en": "Budget Ticket"},
      "internal_name": None,
      "default_price": "23.00",
      "sales_channels": ["web"],
      "category": None,
      "active": True,
      "description": None,
      "free_price": False,
      "tax_rate": "0.00",
      "tax_rule": None,
      "admission": False,
      "issue_giftcard": False,
      "position": 0,
      "generate_tickets": None,
      "allow_waitinglist": True,
      "picture": None,
      "available_from": None,
      "available_until": None,
      "require_bundling": False,
      "require_voucher": False,
      "hide_without_voucher": False,
      "allow_cancel": True,
      "min_per_order": None,
      "max_per_order": None,
      "hidden_if_available": None,
      "checkin_attention": False,
      "has_variations": False,
      "require_approval": False,
      "variations": [],
      "addons": [],
      "bundles": [],
      "show_quota_left": None,
      "original_price": None,
      "meta_data": {
          "day": "Tuesday"
      },
      "require_membership": False,
      "require_membership_types": [],
      "grant_membership_type": None,
      "grant_membership_duration_like_event": True,
      "grant_membership_duration_days": 0,
      "grant_membership_duration_months": 0,
  }
  
  
  @pytest.mark.django_db
  def test_item_list(token_client, organizer, event, team, item):
      cat = event.categories.create(name="foo")
      res = dict(TEST_ITEM_RES)
      res["id"] = item.pk
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/'.format(organizer.slug, event.slug))
      assert resp.status_code == 200
      assert [res] == resp.data['results']
  
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/?active=true'.format(organizer.slug, event.slug))
      assert resp.status_code == 200
      assert [res] == resp.data['results']
  
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/?active=false'.format(organizer.slug, event.slug))
      assert resp.status_code == 200
      assert [] == resp.data['results']
  
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/?category={}'.format(organizer.slug, event.slug,
                                                                                          cat.pk))
      assert resp.status_code == 200
      assert [] == resp.data['results']
  
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/?admission=true'.format(organizer.slug, event.slug))
      assert resp.status_code == 200
      assert [] == resp.data['results']
  
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/?admission=false'.format(organizer.slug, event.slug))
      assert resp.status_code == 200
      assert [res] == resp.data['results']
  
      item.admission = True
      item.save()
      res['admission'] = True
  
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/?admission=true'.format(organizer.slug, event.slug))
      assert resp.status_code == 200
      assert [res] == resp.data['results']
  
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/?admission=false'.format(organizer.slug, event.slug))
      assert resp.status_code == 200
      assert [] == resp.data['results']
  
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/?tax_rate=0'.format(organizer.slug, event.slug))
      assert resp.status_code == 200
      assert [res] == resp.data['results']
  
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/?tax_rate=19'.format(organizer.slug, event.slug))
      assert resp.status_code == 200
      assert [] == resp.data['results']
  
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/?free_price=true'.format(organizer.slug, event.slug))
      assert resp.status_code == 200
      assert [] == resp.data['results']
  
  
  @pytest.mark.django_db
  def test_item_detail(token_client, organizer, event, team, item):
      res = dict(TEST_ITEM_RES)
      res["id"] = item.pk
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/{}/'.format(organizer.slug, event.slug,
                                                                                 item.pk))
      assert resp.status_code == 200
      assert res == resp.data
  
  
  @pytest.mark.django_db
  def test_item_detail_variations(token_client, organizer, event, team, item):
      with scopes_disabled():
          var = item.variations.create(value="Children")
      res = dict(TEST_ITEM_RES)
      res["id"] = item.pk
      res["variations"] = [{
          "id": var.pk,
          "value": {"en": "Children"},
          "default_price": None,
          "price": "23.00",
          "active": True,
          "description": None,
          "position": 0,
          "require_membership": False,
          "require_membership_types": [],
          "original_price": None
      }]
      res["has_variations"] = True
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/{}/'.format(organizer.slug, event.slug,
                                                                                 item.pk))
      assert resp.status_code == 200
      assert res['variations'] == resp.data['variations']
  
  
  @pytest.mark.django_db
  def test_item_detail_addons(token_client, organizer, event, team, item, category):
      item.addons.create(addon_category=category)
      res = dict(TEST_ITEM_RES)
  
      res["id"] = item.pk
      res["addons"] = [{
          "addon_category": category.pk,
          "min_count": 0,
          "max_count": 1,
          "position": 0,
          "multi_allowed": False,
          "price_included": False,
      }]
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/{}/'.format(organizer.slug, event.slug,
                                                                                 item.pk))
      assert resp.status_code == 200
      assert res == resp.data
  
  
  @pytest.mark.django_db
  def test_item_detail_bundles(token_client, organizer, event, team, item, category):
      with scopes_disabled():
          i = event.items.create(name="Included thing", default_price=2)
          item.bundles.create(bundled_item=i, count=1, designated_price=2)
      res = dict(TEST_ITEM_RES)
  
      res["id"] = item.pk
      res["bundles"] = [{
          "bundled_item": i.pk,
          "bundled_variation": None,
          "count": 1,
          "designated_price": '2.00',
      }]
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/{}/'.format(organizer.slug, event.slug,
                                                                                 item.pk))
      assert resp.status_code == 200
      assert res == resp.data
  
  
  @pytest.mark.django_db
  def test_item_create(token_client, organizer, event, item, category, taxrule):
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/items/'.format(organizer.slug, event.slug),
          {
              "category": category.pk,
              "name": {
                  "en": "Ticket"
              },
              "active": True,
              "sales_channels": ["web", "pretixpos"],
              "description": None,
              "default_price": "23.00",
              "free_price": False,
              "tax_rate": "19.00",
              "tax_rule": taxrule.pk,
              "admission": True,
              "issue_giftcard": False,
              "position": 0,
              "picture": None,
              "available_from": None,
              "available_until": None,
              "require_voucher": False,
              "hide_without_voucher": False,
              "allow_cancel": True,
              "min_per_order": None,
              "max_per_order": None,
              "checkin_attention": False,
              "has_variations": True,
              "meta_data": {
                  "day": "Wednesday"
              }
          },
          format='json'
      )
      assert resp.status_code == 201
      with scopes_disabled():
          assert Item.objects.get(pk=resp.data['id']).sales_channels == ["web", "pretixpos"]
          assert Item.objects.get(pk=resp.data['id']).meta_data == {'day': 'Wednesday'}
  
  
  @pytest.mark.django_db
  def test_item_create_with_variation(token_client, organizer, event, item, category, taxrule):
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/items/'.format(organizer.slug, event.slug),
          {
              "category": category.pk,
              "name": {
                  "en": "Ticket"
              },
              "active": True,
              "description": None,
              "default_price": "23.00",
              "free_price": False,
              "tax_rate": "19.00",
              "tax_rule": taxrule.pk,
              "admission": True,
              "issue_giftcard": False,
              "position": 0,
              "picture": None,
              "available_from": None,
              "available_until": None,
              "require_voucher": False,
              "hide_without_voucher": False,
              "allow_cancel": True,
              "min_per_order": None,
              "max_per_order": None,
              "checkin_attention": False,
              "has_variations": True,
              "variations": [
                  {
                      "value": {
                          "de": "Kommentar",
                          "en": "Comment"
                      },
                      "active": True,
                      "require_membership": False,
                      "require_membership_types": [],
                      "description": None,
                      "position": 0,
                      "default_price": None,
                      "price": "23.00"
                  }
              ]
          },
          format='json'
      )
      assert resp.status_code == 201
      with scopes_disabled():
          new_item = Item.objects.get(pk=resp.data['id'])
          assert new_item.variations.first().value.localize('de') == "Kommentar"
          assert new_item.variations.first().value.localize('en') == "Comment"
  
  
  @pytest.mark.django_db
  def test_item_create_giftcard_validation(token_client, organizer, event, item, category, category2, taxrule, taxrule0):
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/items/'.format(organizer.slug, event.slug),
          {
              "category": category.pk,
              "name": {
                  "en": "Ticket"
              },
              "active": True,
              "description": None,
              "default_price": "23.00",
              "free_price": False,
              "tax_rate": "19.00",
              "tax_rule": taxrule0.pk,
              "admission": True,
              "issue_giftcard": True,
              "position": 0,
              "picture": None,
              "available_from": None,
              "available_until": None,
              "require_voucher": False,
              "hide_without_voucher": False,
              "allow_cancel": True,
              "min_per_order": None,
              "max_per_order": None,
              "checkin_attention": False,
              "has_variations": True,
              "addons": []
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["Gift card products should not be admission products at the same time."]}'
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/items/'.format(organizer.slug, event.slug),
          {
              "category": category.pk,
              "name": {
                  "en": "Ticket"
              },
              "active": True,
              "description": None,
              "default_price": "23.00",
              "free_price": False,
              "tax_rate": "19.00",
              "tax_rule": taxrule.pk,
              "admission": False,
              "issue_giftcard": True,
              "position": 0,
              "picture": None,
              "available_from": None,
              "available_until": None,
              "require_voucher": False,
              "hide_without_voucher": False,
              "allow_cancel": True,
              "min_per_order": None,
              "max_per_order": None,
              "checkin_attention": False,
              "has_variations": True,
              "addons": []
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["Gift card products should not be associated with non-zero ' \
                                      'tax rates since sales tax will be applied when the gift card is redeemed."]}'
  
  
  @pytest.mark.django_db
  def test_item_create_with_addon(token_client, organizer, event, item, category, category2, taxrule):
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/items/'.format(organizer.slug, event.slug),
          {
              "category": category.pk,
              "name": {
                  "en": "Ticket"
              },
              "active": True,
              "description": None,
              "default_price": "23.00",
              "free_price": False,
              "tax_rate": "19.00",
              "tax_rule": taxrule.pk,
              "admission": True,
              "issue_giftcard": False,
              "position": 0,
              "picture": None,
              "available_from": None,
              "available_until": None,
              "require_voucher": False,
              "hide_without_voucher": False,
              "allow_cancel": True,
              "min_per_order": None,
              "max_per_order": None,
              "checkin_attention": False,
              "has_variations": True,
              "addons": [
                  {
                      "addon_category": category.pk,
                      "min_count": 0,
                      "max_count": 10,
                      "position": 0,
                      "multi_allowed": False,
                      "price_included": True
                  }
              ]
          },
          format='json'
      )
      assert resp.status_code == 201
      with scopes_disabled():
          item = Item.objects.get(pk=resp.data['id'])
          assert item.addons.first().addon_category == category
          assert item.addons.first().max_count == 10
          assert 2 == Item.objects.all().count()
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/items/'.format(organizer.slug, event.slug),
          {
              "category": category.pk,
              "name": {
                  "en": "Ticket"
              },
              "active": True,
              "description": None,
              "default_price": "23.00",
              "free_price": False,
              "tax_rate": "19.00",
              "tax_rule": taxrule.pk,
              "admission": True,
              "issue_giftcard": False,
              "position": 0,
              "picture": None,
              "available_from": None,
              "available_until": None,
              "require_voucher": False,
              "hide_without_voucher": False,
              "allow_cancel": True,
              "min_per_order": None,
              "max_per_order": None,
              "checkin_attention": False,
              "has_variations": True,
              "addons": [
                  {
                      "addon_category": category2.pk,
                      "min_count": 0,
                      "max_count": 10,
                      "position": 0,
                      "multi_allowed": False,
                      "price_included": True
                  }
              ]
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"addons":["The add-on\'s category must belong to the same event as the item."]}'
      with scopes_disabled():
          assert 2 == Item.objects.all().count()
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/items/'.format(organizer.slug, event.slug),
          {
              "category": category.pk,
              "name": {
                  "en": "Ticket"
              },
              "active": True,
              "description": None,
              "default_price": "23.00",
              "free_price": False,
              "tax_rate": "19.00",
              "tax_rule": taxrule.pk,
              "admission": True,
              "issue_giftcard": False,
              "position": 0,
              "picture": None,
              "available_from": None,
              "available_until": None,
              "require_voucher": False,
              "hide_without_voucher": False,
              "allow_cancel": True,
              "min_per_order": None,
              "max_per_order": None,
              "checkin_attention": False,
              "has_variations": True,
              "addons": [
                  {
                      "addon_category": category.pk,
                      "min_count": 110,
                      "max_count": 10,
                      "position": 0,
                      "multi_allowed": False,
                      "price_included": True
                  }
              ]
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"addons":["The maximum count needs to be greater than the minimum count."]}'
      with scopes_disabled():
          assert 2 == Item.objects.all().count()
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/items/'.format(organizer.slug, event.slug),
          {
              "category": category.pk,
              "name": {
                  "en": "Ticket"
              },
              "active": True,
              "description": None,
              "default_price": "23.00",
              "free_price": False,
              "tax_rate": "19.00",
              "tax_rule": taxrule.pk,
              "admission": True,
              "issue_giftcard": True,
              "position": 0,
              "picture": None,
              "available_from": None,
              "available_until": None,
              "require_voucher": False,
              "hide_without_voucher": False,
              "allow_cancel": True,
              "min_per_order": None,
              "max_per_order": None,
              "checkin_attention": False,
              "has_variations": True,
              "addons": [
                  {
                      "addon_category": category.pk,
                      "min_count": -1,
                      "max_count": 10,
                      "position": 0,
                      "multi_allowed": False,
                      "price_included": True
                  }
              ]
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() in [
          '{"addons":["The minimum count needs to be equal to or greater than zero."]}',
          '{"addons":[{"min_count":["Ensure this value is greater than or equal to 0."]}]}',  # mysql
      ]
      with scopes_disabled():
          assert 2 == Item.objects.all().count()
  
  
  @pytest.mark.django_db
  def test_item_create_with_bundle(token_client, organizer, event, item, category, item2, taxrule):
      with scopes_disabled():
          i = event.items.create(name="Included thing", default_price=2)
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/items/'.format(organizer.slug, event.slug),
          {
              "category": category.pk,
              "name": {
                  "en": "Ticket"
              },
              "active": True,
              "description": None,
              "default_price": "23.00",
              "free_price": False,
              "tax_rate": "19.00",
              "tax_rule": taxrule.pk,
              "admission": True,
              "issue_giftcard": False,
              "position": 0,
              "picture": None,
              "available_from": None,
              "available_until": None,
              "require_voucher": False,
              "hide_without_voucher": False,
              "allow_cancel": True,
              "min_per_order": None,
              "max_per_order": None,
              "checkin_attention": False,
              "has_variations": True,
              "bundles": [
                  {
                      "bundled_item": i.pk,
                      "bundled_variation": None,
                      "count": 2,
                      "designated_price": "3.00",
                  }
              ]
          },
          format='json'
      )
      assert resp.status_code == 201
      with scopes_disabled():
          item = Item.objects.get(pk=resp.data['id'])
          b = item.bundles.first()
      assert b.bundled_item == i
      assert b.bundled_variation is None
      assert b.count == 2
      assert b.designated_price == 3
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/items/'.format(organizer.slug, event.slug),
          {
              "category": category.pk,
              "name": {
                  "en": "Ticket"
              },
              "active": True,
              "description": None,
              "default_price": "23.00",
              "free_price": False,
              "tax_rate": "19.00",
              "tax_rule": taxrule.pk,
              "admission": True,
              "issue_giftcard": False,
              "position": 0,
              "picture": None,
              "available_from": None,
              "available_until": None,
              "require_voucher": False,
              "hide_without_voucher": False,
              "allow_cancel": True,
              "min_per_order": None,
              "max_per_order": None,
              "checkin_attention": False,
              "has_variations": True,
              "bundles": [
                  {
                      "bundled_item": item2.pk,
                      "bundled_variation": None,
                      "count": 2,
                      "designated_price": "3.00",
                  }
              ]
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"bundles":["The bundled item must belong to the same event as the item."]}'
  
      with scopes_disabled():
          v = item2.variations.create(value="foo")
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/items/'.format(organizer.slug, event.slug),
          {
              "category": category.pk,
              "name": {
                  "en": "Ticket"
              },
              "active": True,
              "description": None,
              "default_price": "23.00",
              "free_price": False,
              "tax_rate": "19.00",
              "tax_rule": taxrule.pk,
              "admission": True,
              "position": 0,
              "picture": None,
              "available_from": None,
              "available_until": None,
              "require_voucher": False,
              "hide_without_voucher": False,
              "allow_cancel": True,
              "min_per_order": None,
              "max_per_order": None,
              "checkin_attention": False,
              "has_variations": True,
              "bundles": [
                  {
                      "bundled_item": item.pk,
                      "bundled_variation": v.pk,
                      "count": 2,
                      "designated_price": "3.00",
                  }
              ]
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"bundles":["The chosen variation does not belong to this item."]}'
  
  
  @pytest.mark.django_db
  def test_item_update(token_client, organizer, event, item, category, item2, category2, taxrule2):
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/items/{}/'.format(organizer.slug, event.slug, item.pk),
          {
              "min_per_order": 1,
              "max_per_order": 2
          },
          format='json'
      )
      assert resp.status_code == 200
      with scopes_disabled():
          assert Item.objects.get(pk=item.pk).max_per_order == 2
  
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/items/{}/'.format(organizer.slug, event.slug, item.pk),
          {
              "min_per_order": 10,
              "max_per_order": 2
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["The maximum number per order can not be lower than the ' \
                                      'minimum number per order."]}'
  
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/items/{}/'.format(organizer.slug, event.slug, item.pk),
          {
              "available_from": "2017-12-30T12:00",
              "available_until": "2017-12-29T12:00"
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["The item\'s availability cannot end before it starts."]}'
  
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/items/{}/'.format(organizer.slug, event.slug, item.pk),
          {
              "category": category2.pk
          },
          format='json'
  
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"category":["The item\'s category must belong to the same event as the item."]}'
  
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/items/{}/'.format(organizer.slug, event.slug, item.pk),
          {
              "tax_rule": taxrule2.pk
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"tax_rule":["The item\'s tax rule must belong to the same event as the item."]}'
  
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/items/{}/'.format(organizer.slug, event.slug, item.pk),
          {
              "addons": [
                  {
                      "addon_category": category.pk,
                      "min_count": 0,
                      "max_count": 10,
                      "position": 0,
                      "multi_allowed": False,
                      "price_included": True
                  }
              ]
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["Updating add-ons, bundles, or variations via PATCH/PUT is not supported. Please use ' \
                                      'the dedicated nested endpoint."]}'
  
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/items/{}/'.format(organizer.slug, event.slug, item.pk),
          {
              "bundles": [
                  {
                      "bundled_item": item2.pk,
                      "bundled_variation": None,
                      "count": 2,
                      "designated_price": "3.00",
                  }
              ]
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["Updating add-ons, bundles, or variations via PATCH/PUT is not supported. Please use ' \
                                      'the dedicated nested endpoint."]}'
  
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/items/{}/'.format(organizer.slug, event.slug, item.pk),
          {
              "meta_data": {
                  "day": "Friday"
              }
          },
          format='json'
      )
      assert resp.status_code == 200
      with scopes_disabled():
          assert Item.objects.get(pk=item.pk).meta_data == {'day': 'Friday'}
  
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/items/{}/'.format(organizer.slug, event.slug, item.pk),
          {
              "meta_data": {
                  "foo": "bar"
              }
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"meta_data":["Item meta data property \'foo\' does not exist."]}'
  
  
  @pytest.mark.django_db
  def test_item_update_with_variation(token_client, organizer, event, item):
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/items/{}/'.format(organizer.slug, event.slug, item.pk),
          {
              "variations": [
                  {
                      "value": {
                          "de": "Kommentar",
                          "en": "Comment"
                      },
                      "active": True,
                      "description": None,
                      "position": 0,
                      "default_price": None,
                      "price": "23.00"
                  }
              ]
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["Updating add-ons, bundles, or variations via PATCH/PUT is not supported. Please use ' \
                                      'the dedicated nested endpoint."]}'
  
  
  @pytest.mark.django_db
  def test_item_update_with_addon(token_client, organizer, event, item, category):
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/items/{}/'.format(organizer.slug, event.slug, item.pk),
          {
              "addons": [
                  {
                      "addon_category": category.pk,
                      "min_count": 0,
                      "max_count": 10,
                      "position": 0,
                      "multi_allowed": False,
                      "price_included": True
                  }
              ]
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["Updating add-ons, bundles, or variations via PATCH/PUT is not supported. Please use ' \
                                      'the dedicated nested endpoint."]}'
  
  
  @pytest.mark.django_db
  def test_items_delete(token_client, organizer, event, item):
      resp = token_client.delete('/api/v1/organizers/{}/events/{}/items/{}/'.format(organizer.slug, event.slug, item.pk))
      assert resp.status_code == 204
      with scopes_disabled():
          assert not event.items.filter(pk=item.id).exists()
  
  
  @pytest.mark.django_db
  def test_items_with_order_position_not_delete(token_client, organizer, event, item, order_position):
      resp = token_client.delete('/api/v1/organizers/{}/events/{}/items/{}/'.format(organizer.slug, event.slug, item.pk))
      assert resp.status_code == 403
      with scopes_disabled():
          assert event.items.filter(pk=item.id).exists()
  
  
  @pytest.mark.django_db
  def test_items_with_cart_position_delete(token_client, organizer, event, item, cart_position):
      resp = token_client.delete('/api/v1/organizers/{}/events/{}/items/{}/'.format(organizer.slug, event.slug, item.pk))
      assert resp.status_code == 204
      with scopes_disabled():
          assert not event.items.filter(pk=item.id).exists()
  
  
  @pytest.fixture
  def variations(item):
      v = []
      v.append(item.variations.create(value="ChildA1"))
      v.append(item.variations.create(value="ChildA2"))
      return v
  
  
  @pytest.fixture
  def variations2(item2):
      v = []
      v.append(item2.variations.create(value="ChildB1"))
      v.append(item2.variations.create(value="ChildB2"))
      return v
  
  
  @pytest.fixture
  def variation(item):
      return item.variations.create(value="ChildC1")
  
  
  TEST_VARIATIONS_RES = {
      "value": {
          "en": "ChildC1"
      },
      "active": True,
      "description": None,
      "position": 0,
      "default_price": None,
      "price": "23.00",
      "require_membership": False,
      "require_membership_types": [],
      "original_price": None
  }
  
  TEST_VARIATIONS_UPDATE = {
      "value": {
          "en": "ChildC2"
      },
      "active": True,
      "description": None,
      "position": 1,
      "default_price": "20.0",
      "require_membership": False,
      "require_membership_types": [],
      "original_price": None
  }
  
  
  @pytest.mark.django_db
  def test_variations_list(token_client, organizer, event, item, variation):
      res = dict(TEST_VARIATIONS_RES)
      res["id"] = variation.pk
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/{}/variations/'.format(organizer.slug, event.slug, item.pk))
      assert resp.status_code == 200
      assert res['value'] == resp.data['results'][0]['value']
      assert res['position'] == resp.data['results'][0]['position']
      assert res['price'] == resp.data['results'][0]['price']
  
  
  @pytest.mark.django_db
  def test_variations_detail(token_client, organizer, event, item, variation):
      res = dict(TEST_VARIATIONS_RES)
      res["id"] = variation.pk
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/{}/variations/{}/'.format(organizer.slug, event.slug, item.pk, variation.pk))
      assert resp.status_code == 200
      assert res == resp.data
  
  
  @pytest.mark.django_db
  def test_variations_create(token_client, organizer, event, item, variation):
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/items/{}/variations/'.format(organizer.slug, event.slug, item.pk),
          {
              "value": {
                  "en": "ChildC2"
              },
              "active": True,
              "description": None,
              "position": 1,
              "default_price": None,
              "original_price": "23.42",
              "price": 23.0
          },
          format='json'
      )
      assert resp.status_code == 201
      with scopes_disabled():
          var = ItemVariation.objects.get(pk=resp.data['id'])
      assert var.position == 1
      assert var.price == 23.0
  
  
  @pytest.mark.django_db
  def test_variations_create_not_allowed(token_client, organizer, event, item):
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/items/{}/variations/'.format(organizer.slug, event.slug, item.pk),
          {
              "value": {
                  "en": "ChildC2"
              },
              "active": True,
              "description": None,
              "position": 1,
              "default_price": None,
              "price": 23.0
          },
          format='json'
      )
      assert resp.status_code == 403
      assert resp.content.decode() == '{"detail":"This variation cannot be created because the item does ' \
                                      'not have variations. Changing a product without variations to a product with ' \
                                      'variations is not allowed."}'
  
  
  @pytest.mark.django_db
  def test_variations_update(token_client, organizer, event, item, item3, variation):
      res = dict(TEST_VARIATIONS_UPDATE)
      res["id"] = variation.pk
      res["price"] = "20.00"
      res["default_price"] = "20.00"
      res["original_price"] = "50.00"
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/items/{}/variations/{}/'.format(organizer.slug, event.slug, item.pk, variation.pk),
          {
              "value": {
                  "en": "ChildC2"
              },
              "position": 1,
              "default_price": "20.00",
              "original_price": "50.00"
          },
          format='json'
      )
      assert resp.status_code == 200
      assert res == resp.data
  
      # Variation exists but do not belong to item
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/items/{}/variations/{}/'.format(organizer.slug, event.slug, item3.pk, variation.pk),
          {
              "position": 1
          },
          format='json'
      )
      assert resp.status_code == 404
  
  
  @pytest.mark.django_db
  def test_variations_delete(token_client, organizer, event, item, variations, order):
      resp = token_client.delete('/api/v1/organizers/{}/events/{}/items/{}/variations/{}/'.format(organizer.slug, event.slug, item.pk, variations[0].pk))
      assert resp.status_code == 204
      with scopes_disabled():
          assert not item.variations.filter(pk=variations[0].pk).exists()
  
  
  @pytest.mark.django_db
  def test_variations_with_order_position_not_delete(token_client, organizer, event, item, order, variations, order_position):
      with scopes_disabled():
          assert item.variations.filter(pk=variations[0].id).exists()
      resp = token_client.delete('/api/v1/organizers/{}/events/{}/items/{}/variations/{}/'.format(organizer.slug, event.slug, item.pk, variations[0].pk))
      assert resp.status_code == 403
      assert resp.content.decode() == '{"detail":"This variation cannot be deleted because it has already been ordered ' \
                                      'by a user or currently is in a users\'s cart. Please set the variation as ' \
                                      '\'inactive\' instead."}'
      with scopes_disabled():
          assert item.variations.filter(pk=variations[0].id).exists()
  
  
  @pytest.mark.django_db
  def test_variations_with_cart_position_not_delete(token_client, organizer, event, item, variations, cart_position):
      with scopes_disabled():
          assert item.variations.filter(pk=variations[0].id).exists()
      resp = token_client.delete('/api/v1/organizers/{}/events/{}/items/{}/variations/{}/'.format(organizer.slug, event.slug, item.pk, variations[0].pk))
      assert resp.status_code == 403
      assert resp.content.decode() == '{"detail":"This variation cannot be deleted because it has already been ordered ' \
                                      'by a user or currently is in a users\'s cart. Please set the variation as ' \
                                      '\'inactive\' instead."}'
      with scopes_disabled():
          assert item.variations.filter(pk=variations[0].id).exists()
  
  
  @pytest.mark.django_db
  def test_only_variation_not_delete(token_client, organizer, event, item, variation):
      with scopes_disabled():
          assert item.variations.filter(pk=variation.id).exists()
      resp = token_client.delete('/api/v1/organizers/{}/events/{}/items/{}/variations/{}/'.format(organizer.slug, event.slug, item.pk, variation.pk))
      assert resp.status_code == 403
      assert resp.content.decode() == '{"detail":"This variation cannot be deleted because it is the only variation. ' \
                                      'Changing a product with variations to a product without variations is not ' \
                                      'allowed."}'
      with scopes_disabled():
          assert item.variations.filter(pk=variation.id).exists()
  
  
  @pytest.fixture
  def bundle(item, item3, category):
      return item.bundles.create(bundled_item=item3, count=1, designated_price=2)
  
  
  TEST_BUNDLE_RES = {
      "bundled_item": 0,
      "bundled_variation": None,
      "count": 1,
      "designated_price": "2.00"
  }
  
  
  @pytest.mark.django_db
  def test_bundles_list(token_client, organizer, event, item, bundle, item3):
      res = dict(TEST_BUNDLE_RES)
      res["id"] = bundle.pk
      res["bundled_item"] = item3.pk
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/{}/bundles/'.format(organizer.slug, event.slug,
                                                                                         item.pk))
      assert resp.status_code == 200
      assert res == resp.data['results'][0]
  
  
  @pytest.mark.django_db
  def test_bundles_detail(token_client, organizer, event, item, bundle, item3):
      res = dict(TEST_BUNDLE_RES)
      res["id"] = bundle.pk
      res["bundled_item"] = item3.pk
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/{}/bundles/{}/'.format(organizer.slug, event.slug,
                                                                                            item.pk, bundle.pk))
      assert resp.status_code == 200
      assert res == resp.data
  
  
  @pytest.mark.django_db
  def test_bundles_create(token_client, organizer, event, item, item2, item3):
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/items/{}/bundles/'.format(organizer.slug, event.slug, item.pk),
          {
              "bundled_item": item3.pk,
              "bundled_variation": None,
              "count": 1,
              "designated_price": "1.50",
          },
          format='json'
      )
      assert resp.status_code == 201
      with scopes_disabled():
          b = ItemBundle.objects.get(pk=resp.data['id'])
      assert b.bundled_item == item3
      assert b.bundled_variation is None
      assert b.designated_price == 1.5
      assert b.count == 1
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/items/{}/bundles/'.format(organizer.slug, event.slug, item.pk),
          {
              "bundled_item": item2.pk,
              "bundled_variation": None,
              "count": 1,
              "designated_price": "1.50",
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["The bundled item must belong to the same event as the item."]}'
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/items/{}/bundles/'.format(organizer.slug, event.slug, item.pk),
          {
              "bundled_item": item.pk,
              "bundled_variation": None,
              "count": 1,
              "designated_price": "1.50",
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["The bundled item must not be the same item as the bundling one."]}'
  
      with scopes_disabled():
          item3.bundles.create(bundled_item=item, count=1, designated_price=3)
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/items/{}/bundles/'.format(organizer.slug, event.slug, item.pk),
          {
              "bundled_item": item3.pk,
              "bundled_variation": None,
              "count": 1,
              "designated_price": "1.50",
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["The bundled item must not have bundles on its own."]}'
  
  
  @pytest.mark.django_db
  def test_bundles_update(token_client, organizer, event, item, bundle):
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/items/{}/bundles/{}/'.format(organizer.slug, event.slug, item.pk, bundle.pk),
          {
              "count": 3,
          },
          format='json'
      )
      assert resp.status_code == 200
      with scopes_disabled():
          a = ItemBundle.objects.get(pk=bundle.pk)
      assert a.count == 3
  
  
  @pytest.mark.django_db
  def test_bundles_delete(token_client, organizer, event, item, bundle):
      resp = token_client.delete('/api/v1/organizers/{}/events/{}/items/{}/bundles/{}/'.format(organizer.slug, event.slug,
                                                                                               item.pk, bundle.pk))
      assert resp.status_code == 204
      with scopes_disabled():
          assert not item.bundles.filter(pk=bundle.id).exists()
  
  
  @pytest.fixture
  def addon(item, category):
      return item.addons.create(addon_category=category, min_count=0, max_count=10, position=1)
  
  
  @pytest.fixture
  def option(question):
      return question.options.create(answer='XL', identifier='LVETRWVU')
  
  
  TEST_ADDONS_RES = {
      "min_count": 0,
      "max_count": 10,
      "position": 1,
      "multi_allowed": False,
      "price_included": False
  }
  
  
  @pytest.mark.django_db
  def test_addons_list(token_client, organizer, event, item, addon, category):
      res = dict(TEST_ADDONS_RES)
      res["id"] = addon.pk
      res["addon_category"] = category.pk
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/{}/addons/'.format(organizer.slug, event.slug,
                                                                                        item.pk))
      assert resp.status_code == 200
      assert res['addon_category'] == resp.data['results'][0]['addon_category']
      assert res['min_count'] == resp.data['results'][0]['min_count']
      assert res['max_count'] == resp.data['results'][0]['max_count']
      assert res['position'] == resp.data['results'][0]['position']
  
  
  @pytest.mark.django_db
  def test_addons_detail(token_client, organizer, event, item, addon, category):
      res = dict(TEST_ADDONS_RES)
      res["id"] = addon.pk
      res["addon_category"] = category.pk
      resp = token_client.get('/api/v1/organizers/{}/events/{}/items/{}/addons/{}/'.format(organizer.slug, event.slug,
                                                                                           item.pk, addon.pk))
      assert resp.status_code == 200
      assert res == resp.data
  
  
  @pytest.mark.django_db
  def test_addons_create(token_client, organizer, event, item, category, category2):
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/items/{}/addons/'.format(organizer.slug, event.slug, item.pk),
          {
              "addon_category": category.pk,
              "min_count": 0,
              "max_count": 10,
              "position": 1,
              "multi_allowed": False,
              "price_included": False
          },
          format='json'
      )
      assert resp.status_code == 201
      with scopes_disabled():
          addon = ItemAddOn.objects.get(pk=resp.data['id'])
      assert addon.position == 1
      assert addon.addon_category == category
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/items/{}/addons/'.format(organizer.slug, event.slug, item.pk),
          {
              "addon_category": category.pk,
              "min_count": 10,
              "max_count": 20,
              "position": 2,
              "multi_allowed": False,
              "price_included": False
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"addon_category":["The item already has an add-on of this category."]}'
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/items/{}/addons/'.format(organizer.slug, event.slug, item.pk),
          {
              "addon_category": category2.pk,
              "min_count": 10,
              "max_count": 20,
              "position": 2,
              "multi_allowed": False,
              "price_included": False
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"addon_category":["The add-on\'s category must belong to the same event as ' \
                                      'the item."]}'
  
  
  @pytest.mark.django_db
  def test_addons_update(token_client, organizer, event, item, addon):
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/items/{}/addons/{}/'.format(organizer.slug, event.slug, item.pk, addon.pk),
          {
              "min_count": 100,
              "max_count": 101
          },
          format='json'
      )
      assert resp.status_code == 200
      with scopes_disabled():
          a = ItemAddOn.objects.get(pk=addon.pk)
      assert a.min_count == 100
      assert a.max_count == 101
  
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/items/{}/addons/{}/'.format(organizer.slug, event.slug, item.pk, a.pk),
          {
              "min_count": 10,
              "max_count": 1
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["The maximum count needs to be greater than the minimum ' \
                                      'count."]}'
  
  
  @pytest.mark.django_db
  def test_addons_delete(token_client, organizer, event, item, addon):
      resp = token_client.delete('/api/v1/organizers/{}/events/{}/items/{}/addons/{}/'.format(organizer.slug, event.slug,
                                                                                              item.pk, addon.pk))
      assert resp.status_code == 204
      with scopes_disabled():
          assert not item.addons.filter(pk=addon.id).exists()
  
  
  @pytest.fixture
  def quota(event, item):
      q = event.quotas.create(name="Budget Quota", size=200)
      q.items.add(item)
      return q
  
  
  TEST_QUOTA_RES = {
      "name": "Budget Quota",
      "size": 200,
      "items": [],
      "variations": [],
      "subevent": None,
      "close_when_sold_out": False,
      "release_after_exit": False,
      "closed": False
  }
  
  
  @pytest.mark.django_db
  def test_quota_list(token_client, organizer, event, quota, item, subevent):
      res = dict(TEST_QUOTA_RES)
      res["id"] = quota.pk
      res["items"] = [item.pk]
  
      resp = token_client.get('/api/v1/organizers/{}/events/{}/quotas/'.format(organizer.slug, event.slug))
      assert resp.status_code == 200
      assert [res] == resp.data['results']
  
      quota.subevent = subevent
      quota.save()
      res["subevent"] = subevent.pk
      resp = token_client.get(
          '/api/v1/organizers/{}/events/{}/quotas/?subevent={}'.format(organizer.slug, event.slug, subevent.pk))
      assert [res] == resp.data['results']
      with scopes_disabled():
          se2 = event.subevents.create(name="Foobar", date_from=datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC))
      resp = token_client.get(
          '/api/v1/organizers/{}/events/{}/quotas/?subevent={}'.format(organizer.slug, event.slug, se2.pk))
      assert [] == resp.data['results']
  
  
  @pytest.mark.django_db
  def test_quota_detail(token_client, organizer, event, quota, item):
      res = dict(TEST_QUOTA_RES)
  
      res["id"] = quota.pk
      res["items"] = [item.pk]
      resp = token_client.get('/api/v1/organizers/{}/events/{}/quotas/{}/'.format(organizer.slug, event.slug,
                                                                                  quota.pk))
      assert resp.status_code == 200
      assert res == resp.data
  
  
  @pytest.mark.django_db
  def test_quota_create(token_client, organizer, event, event2, item):
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/quotas/'.format(organizer.slug, event.slug),
          {
              "name": "Ticket Quota",
              "size": 200,
              "items": [item.pk],
              "variations": [],
              "subevent": None
          },
          format='json'
      )
      assert resp.status_code == 201
      with scopes_disabled():
          quota = Quota.objects.get(pk=resp.data['id'])
      assert quota.name == "Ticket Quota"
      assert quota.size == 200
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/quotas/'.format(organizer.slug, event2.slug),
          {
              "name": "Ticket Quota",
              "size": 200,
              "items": [item.pk],
              "variations": [],
              "subevent": None
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["One or more items do not belong to this event."]}'
  
  
  @pytest.mark.django_db
  def test_quota_create_with_variations(token_client, organizer, event, item, variations, variations2):
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/quotas/'.format(organizer.slug, event.slug),
          {
              "name": "Ticket Quota",
              "size": 200,
              "items": [item.pk],
              "variations": [variations[0].pk],
              "subevent": None
          },
          format='json'
      )
      assert resp.status_code == 201
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/quotas/'.format(organizer.slug, event.slug),
          {
              "name": "Ticket Quota",
              "size": 200,
              "items": [item.pk],
              "variations": [100],
              "subevent": None
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"variations":["Invalid pk \\"100\\" - object does not exist."]}'
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/quotas/'.format(organizer.slug, event.slug),
          {
              "name": "Ticket Quota",
              "size": 200,
              "items": [item.pk],
              "variations": [variations[0].pk, variations2[0].pk],
              "subevent": None
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["All variations must belong to an item contained in the items list."]}'
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/quotas/'.format(organizer.slug, event.slug),
          {
              "name": "Ticket Quota",
              "size": 200,
              "items": [item.pk],
              "variations": [],
              "subevent": None
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["One or more items has variations but none of these are in the variations list."]}'
  
  
  @pytest.mark.django_db
  def test_quota_create_with_subevent(token_client, organizer, event, event3, item, variations, subevent, subevent2):
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/quotas/'.format(organizer.slug, event.slug),
          {
              "name": "Ticket Quota",
              "size": 200,
              "items": [item.pk],
              "variations": [variations[0].pk],
              "subevent": subevent.pk
          },
          format='json'
      )
      assert resp.status_code == 201
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/quotas/'.format(organizer.slug, event.slug),
          {
              "name": "Ticket Quota",
              "size": 200,
              "items": [item.pk],
              "variations": [variations[0].pk],
              "subevent": None
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["Subevent cannot be null for event series."]}'
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/quotas/'.format(organizer.slug, event.slug),
          {
              "name": "Ticket Quota",
              "size": 200,
              "items": [item.pk],
              "variations": [variations[0].pk],
              "subevent": subevent2.pk
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["The subevent does not belong to this event."]}'
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/quotas/'.format(organizer.slug, event3.slug),
          {
              "name": "Ticket Quota",
              "size": 200,
              "items": [],
              "variations": [],
              "subevent": subevent2.pk
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["The subevent does not belong to this event."]}'
  
  
  @pytest.mark.django_db
  def test_quota_update(token_client, organizer, event, quota, item):
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/quotas/{}/'.format(organizer.slug, event.slug, quota.pk),
          {
              "name": "Ticket Quota Update",
              "size": 111,
          },
          format='json'
      )
      assert resp.status_code == 200
      with scopes_disabled():
          quota = Quota.objects.get(pk=resp.data['id'])
      assert quota.name == "Ticket Quota Update"
      assert quota.size == 111
      assert quota.all_logentries().count() == 1
  
  
  @pytest.mark.django_db
  def test_quota_update_closed(token_client, organizer, event, quota, item):
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/quotas/{}/'.format(organizer.slug, event.slug, quota.pk),
          {
              "closed": True,
          },
          format='json'
      )
      assert resp.status_code == 200
      with scopes_disabled():
          quota = Quota.objects.get(pk=resp.data['id'])
      assert quota.all_logentries().filter(action_type="pretix.event.quota.closed").count() == 1
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/quotas/{}/'.format(organizer.slug, event.slug, quota.pk),
          {
              "closed": False,
          },
          format='json'
      )
      assert resp.status_code == 200
      assert quota.all_logentries().filter(action_type="pretix.event.quota.opened").count() == 1
  
  
  @pytest.mark.django_db
  def test_quota_update_unchanged(token_client, organizer, event, quota, item):
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/quotas/{}/'.format(organizer.slug, event.slug, quota.pk),
          {
              "size": 200,
          },
          format='json'
      )
      assert resp.status_code == 200
      with scopes_disabled():
          quota = Quota.objects.get(pk=resp.data['id'])
      assert quota.size == 200
      assert quota.all_logentries().count() == 0
  
  
  @pytest.mark.django_db
  def test_quota_delete(token_client, organizer, event, quota):
      resp = token_client.delete('/api/v1/organizers/{}/events/{}/quotas/{}/'.format(organizer.slug, event.slug, quota.pk))
      assert resp.status_code == 204
      with scopes_disabled():
          assert not event.quotas.filter(pk=quota.id).exists()
  
  
  @pytest.mark.django_db
  def test_quota_availability(token_client, organizer, event, quota, item):
      resp = token_client.get('/api/v1/organizers/{}/events/{}/quotas/{}/availability/'.format(
          organizer.slug, event.slug, quota.pk))
      assert resp.status_code == 200
      assert {'blocking_vouchers': 0,
              'available_number': 200,
              'pending_orders': 0,
              'exited_orders': 0,
              'cart_positions': 0,
              'available': True,
              'total_size': 200,
              'paid_orders': 0,
              'waiting_list': 0} == resp.data
  
  
  @pytest.fixture
  def question(event, item):
      q = event.questions.create(
          question="T-Shirt size", type="C", identifier="ABC", help_text="This is an example question"
      )
      q.items.add(item)
      q.options.create(answer="XL", identifier="LVETRWVU")
      return q
  
  
  TEST_QUESTION_RES = {
      "question": {"en": "T-Shirt size"},
      "type": "C",
      "required": False,
      "items": [],
      "ask_during_checkin": False,
      "hidden": False,
      "print_on_invoice": False,
      "identifier": "ABC",
      "position": 0,
      "dependency_question": None,
      "dependency_value": None,
      "dependency_values": [],
      "valid_number_min": None,
      "valid_number_max": None,
      "valid_date_min": None,
      "valid_date_max": None,
      "valid_datetime_min": None,
      "valid_datetime_max": None,
      "valid_file_portrait": False,
      "help_text": {"en": "This is an example question"},
      "options": [
          {
              "id": 0,
              "position": 0,
              "identifier": "LVETRWVU",
              "answer": {"en": "XL"}
          }
      ]
  }
  
  
  @pytest.mark.django_db
  def test_question_list(token_client, organizer, event, question, item):
      res = dict(TEST_QUESTION_RES)
      res["id"] = question.pk
      res["items"] = [item.pk]
      res["options"][0]["id"] = question.options.first().pk
  
      resp = token_client.get('/api/v1/organizers/{}/events/{}/questions/'.format(organizer.slug, event.slug))
      assert resp.status_code == 200
      assert [res] == resp.data['results']
  
      resp = token_client.get('/api/v1/organizers/{}/events/{}/questions/?required=false'.format(
          organizer.slug, event.slug
      ))
      assert [res] == resp.data['results']
      resp = token_client.get('/api/v1/organizers/{}/events/{}/questions/?ask_during_checkin=false'.format(
          organizer.slug, event.slug
      ))
      assert [res] == resp.data['results']
      resp = token_client.get('/api/v1/organizers/{}/events/{}/questions/?identifier=ABC'.format(
          organizer.slug, event.slug
      ))
      assert [res] == resp.data['results']
      resp = token_client.get('/api/v1/organizers/{}/events/{}/questions/?required=true'.format(
          organizer.slug, event.slug
      ))
      assert [] == resp.data['results']
      resp = token_client.get('/api/v1/organizers/{}/events/{}/questions/?ask_during_checkin=true'.format(
          organizer.slug, event.slug
      ))
      assert [] == resp.data['results']
      resp = token_client.get('/api/v1/organizers/{}/events/{}/questions/?identifier=DEF'.format(
          organizer.slug, event.slug
      ))
      assert [] == resp.data['results']
  
  
  @pytest.mark.django_db
  def test_question_detail(token_client, organizer, event, question, item):
      res = dict(TEST_QUESTION_RES)
  
      res["id"] = question.pk
      res["items"] = [item.pk]
      res["options"][0]["id"] = question.options.first().pk
  
      resp = token_client.get('/api/v1/organizers/{}/events/{}/questions/{}/'.format(organizer.slug, event.slug,
                                                                                     question.pk))
      assert resp.status_code == 200
      assert res == resp.data
  
  
  @pytest.mark.django_db
  def test_question_create(token_client, organizer, event, event2, item):
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/questions/'.format(organizer.slug, event.slug),
          {
              "question": "What's your name?",
              "type": "S",
              "required": True,
              "items": [item.pk],
              "position": 0,
              "ask_during_checkin": False,
              "identifier": None
          },
          format='json'
      )
      assert resp.status_code == 201
      with scopes_disabled():
          question = Question.objects.get(pk=resp.data['id'])
          assert question.question == "What's your name?"
          assert question.type == "S"
          assert question.identifier is not None
          assert len(question.items.all()) == 1
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/questions/'.format(organizer.slug, event2.slug),
          {
              "question": "What's your name?",
              "type": "S",
              "required": True,
              "items": [item.pk],
              "position": 0,
              "ask_during_checkin": False,
              "identifier": None
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["One or more items do not belong to this event."]}'
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/questions/'.format(organizer.slug, event.slug),
          {
              "question": "What's your name?",
              "type": "S",
              "required": True,
              "items": [item.pk],
              "position": 0,
              "ask_during_checkin": False,
              "identifier": question.identifier
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"identifier":["This identifier is already used for a different question."]}'
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/questions/'.format(organizer.slug, event.slug),
          {
              "question": "What's your name?",
              "type": "S",
              "required": True,
              "items": [item.pk],
              "position": 0,
              "ask_during_checkin": False,
              "dependency_question": question.pk,
              "dependency_value": "1",
              "identifier": None
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"dependency_question":["Question dependencies can only be set to boolean or choice questions."]}'
  
      question.type = Question.TYPE_BOOLEAN
      question.save()
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/questions/'.format(organizer.slug, event.slug),
          {
              "question": "What's your name?",
              "type": "S",
              "required": True,
              "items": [item.pk],
              "position": 0,
              "ask_during_checkin": True,
              "dependency_question": question.pk,
              "dependency_value": "1",
              "identifier": None
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["Dependencies are not supported during check-in."]}'
  
      question.type = Question.TYPE_BOOLEAN
      question.save()
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/questions/'.format(organizer.slug, event.slug),
          {
              "question": "What's your name?",
              "type": "S",
              "required": True,
              "items": [item.pk],
              "position": 0,
              "ask_during_checkin": False,
              "dependency_question": question.pk,
              "dependency_value": "1",
              "identifier": None
          },
          format='json'
      )
      assert resp.status_code == 201
      with scopes_disabled():
          q2 = Question.objects.get(pk=resp.data['id'])
      assert q2.dependency_question == question
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/questions/'.format(organizer.slug, event.slug),
          {
              "question": "What's your name?",
              "type": "S",
              "required": True,
              "items": [item.pk],
              "position": 0,
              "ask_during_checkin": False,
              "identifier": None,
              "dependency_question": None,
              "dependency_values": [],
          },
          format='json'
      )
      assert resp.status_code == 201
      with scopes_disabled():
          question = Question.objects.get(pk=resp.data['id'])
          assert question.question == "What's your name?"
          assert question.type == "S"
          assert question.identifier is not None
          assert len(question.items.all()) == 1
          assert question.dependency_question is None
          assert question.dependency_values == []
  
  
  @pytest.mark.django_db
  def test_question_update(token_client, organizer, event, question):
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/questions/{}/'.format(organizer.slug, event.slug, question.pk),
          {
              "question": "What's your shoe size?",
              "type": "N",
          },
          format='json'
      )
      assert resp.status_code == 200
      with scopes_disabled():
          question = Question.objects.get(pk=resp.data['id'])
      assert question.question == "What's your shoe size?"
      assert question.type == "N"
  
  
  @pytest.mark.django_db
  def test_question_update_circular_dependency(token_client, organizer, event, question):
      with scopes_disabled():
          q2 = event.questions.create(question="T-Shirt size", type="B", identifier="FOO", dependency_question=question)
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/questions/{}/'.format(organizer.slug, event.slug, question.pk),
          {
              "dependency_question": q2.pk
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["Circular dependency between questions detected."]}'
  
  
  @pytest.mark.django_db
  def test_question_self_dependency(token_client, organizer, event, question):
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/questions/{}/'.format(organizer.slug, event.slug, question.pk),
          {
              "dependency_question": question.pk
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"dependency_question":["A question cannot depend on itself."]}'
  
  
  @pytest.mark.django_db
  def test_question_update_options(token_client, organizer, event, question, item):
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/questions/{}/'.format(organizer.slug, event.slug, question.pk),
          {
              "options": [
              ]
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"non_field_errors":["Updating options via PATCH/PUT is not supported. Please use the dedicated nested endpoint."]}'
  
  
  @pytest.mark.django_db
  def test_question_delete(token_client, organizer, event, question):
      resp = token_client.delete('/api/v1/organizers/{}/events/{}/questions/{}/'.format(organizer.slug, event.slug, question.pk))
      assert resp.status_code == 204
      with scopes_disabled():
          assert not event.questions.filter(pk=question.id).exists()
  
  
  @pytest.mark.django_db
  def test_question_update_dependency_values(token_client, organizer, event, question):
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/questions/{}/'.format(organizer.slug, event.slug, question.pk),
          {
              "dependency_values": ["a", "b"]
          },
          format='json'
      )
      assert resp.status_code == 200
      question.refresh_from_db()
      assert question.dependency_values == ["a", "b"]
  
  
  @pytest.mark.django_db
  def test_question_update_dependency_value_legacy(token_client, organizer, event, question):
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/questions/{}/'.format(organizer.slug, event.slug, question.pk),
          {
              "dependency_value": "a"
          },
          format='json'
      )
      assert resp.status_code == 200
      question.refresh_from_db()
      assert question.dependency_values == ["a"]
  
  
  @pytest.mark.django_db
  def test_question_update_dependency_value_legacy_conflict(token_client, organizer, event, question):
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/questions/{}/'.format(organizer.slug, event.slug, question.pk),
          {
              "dependency_values": ["a", "b"],
              "dependency_value": "a"
          },
          format='json'
      )
      assert resp.status_code == 200
      question.refresh_from_db()
      assert question.dependency_values == ["a"]
  
  
  TEST_OPTIONS_RES = {
      "identifier": "LVETRWVU",
      "answer": {"en": "XL"},
      "position": 0
  }
  
  
  @pytest.mark.django_db
  def test_options_list(token_client, organizer, event, question, option):
      res = dict(TEST_OPTIONS_RES)
      res["id"] = option.pk
      resp = token_client.get('/api/v1/organizers/{}/events/{}/questions/{}/options/'.format(
          organizer.slug, event.slug, question.pk)
      )
      assert resp.status_code == 200
      assert res['identifier'] == resp.data['results'][0]['identifier']
      assert res['answer'] == resp.data['results'][0]['answer']
  
  
  @pytest.mark.django_db
  def test_options_detail(token_client, organizer, event, question, option):
      res = dict(TEST_OPTIONS_RES)
      res["id"] = option.pk
      resp = token_client.get('/api/v1/organizers/{}/events/{}/questions/{}/options/{}/'.format(
          organizer.slug, event.slug, question.pk, option.pk
      ))
      assert resp.status_code == 200
      assert res == resp.data
  
  
  @pytest.mark.django_db
  def test_options_create(token_client, organizer, event, question):
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/questions/{}/options/'.format(organizer.slug, event.slug, question.pk),
          {
              "identifier": "DFEMJWMJ",
              "answer": "A",
              "position": 0
          },
          format='json'
      )
      assert resp.status_code == 201
      with scopes_disabled():
          option = QuestionOption.objects.get(pk=resp.data['id'])
      assert option.answer == "A"
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/questions/{}/options/'.format(organizer.slug, event.slug, question.pk),
          {
              "identifier": "DFEMJWMJ",
              "answer": "A",
              "position": 0
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"identifier":["The identifier \\"DFEMJWMJ\\" is already used for a different option."]}'
  
  
  @pytest.mark.django_db
  def test_options_update(token_client, organizer, event, question, option):
      resp = token_client.patch(
          '/api/v1/organizers/{}/events/{}/questions/{}/options/{}/'.format(organizer.slug, event.slug, question.pk, option.pk),
          {
              "answer": "B",
          },
          format='json'
      )
      assert resp.status_code == 200
      with scopes_disabled():
          a = QuestionOption.objects.get(pk=option.pk)
      assert a.answer == "B"
  
  
  @pytest.mark.django_db
  def test_options_delete(token_client, organizer, event, question, option):
      resp = token_client.delete('/api/v1/organizers/{}/events/{}/questions/{}/options/{}/'.format(
          organizer.slug, event.slug, question.pk, option.pk
      ))
      assert resp.status_code == 204
      with scopes_disabled():
          assert not question.options.filter(pk=option.id).exists()
  
  
  @pytest.mark.django_db
  def test_question_create_with_option(token_client, organizer, event, item):
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/questions/'.format(organizer.slug, event.slug),
          {
              "question": "What's your name?",
              "type": "S",
              "required": True,
              "items": [item.pk],
              "position": 0,
              "ask_during_checkin": False,
              "identifier": None,
              "options": [
                  {
                      "identifier": None,
                      "answer": {"en": "A"},
                      "position": 0,
                  },
                  {
                      "identifier": None,
                      "answer": {"en": "B"},
                      "position": 1,
                  },
              ]
          },
          format='json'
      )
      assert resp.status_code == 201
      with scopes_disabled():
          question = Question.objects.get(pk=resp.data['id'])
          assert str(question.options.first().answer) == "A"
          assert question.options.first().identifier is not None
          assert str(question.options.last().answer) == "B"
          assert 2 == question.options.count()
  
      resp = token_client.post(
          '/api/v1/organizers/{}/events/{}/questions/'.format(organizer.slug, event.slug),
          {
              "question": "What's your name?",
              "type": "S",
              "required": True,
              "items": [item.pk],
              "position": 0,
              "ask_during_checkin": False,
              "identifier": None,
              "options": [
                  {
                      "identifier": "ABC",
                      "answer": {"en": "A"},
                      "position": 0,
                  },
                  {
                      "identifier": "ABC",
                      "answer": {"en": "B"},
                      "position": 1,
                  },
              ]
          },
          format='json'
      )
      assert resp.status_code == 400
      assert resp.content.decode() == '{"options":["The identifier \\"ABC\\" is already used for a different option."]}'
  