import datetime import hashlib import json import os from datetime import timedelta from decimal import Decimal from unittest import mock from bs4 import BeautifulSoup from django.conf import settings from django.core import mail as djmail from django.core.files.uploadedfile import SimpleUploadedFile from django.test import TestCase from django.utils.crypto import get_random_string from django.utils.timezone import now from django_countries.fields import Country from django_scopes import scopes_disabled from pretix.base.decimal import round_decimal from pretix.base.models import ( CartPosition, Event, Invoice, InvoiceAddress, Item, ItemCategory, Order, OrderPayment, OrderPosition, Organizer, Question, QuestionAnswer, Quota, SeatingPlan, Voucher, ) from pretix.base.models.items import ( ItemAddOn, ItemBundle, ItemVariation, SubEventItem, SubEventItemVariation, ) from pretix.base.services.orders import OrderError, _perform_order from pretix.testutils.scope import classscope from pretix.testutils.sessions import get_cart_session_key class BaseCheckoutTestCase: @scopes_disabled() def setUp(self): super().setUp() self.orga = Organizer.objects.create(name='CCC', slug='ccc') self.event = Event.objects.create( organizer=self.orga, name='30C3', slug='30c3', date_from=datetime.datetime(now().year + 1, 12, 26, tzinfo=datetime.timezone.utc), plugins='pretix.plugins.stripe,pretix.plugins.banktransfer', live=True ) self.tr19 = self.event.tax_rules.create(rate=19) self.category = ItemCategory.objects.create(event=self.event, name="Everything", position=0) self.quota_tickets = Quota.objects.create(event=self.event, name='Tickets', size=5) self.ticket = Item.objects.create(event=self.event, name='Early-bird ticket', category=self.category, default_price=23, admission=True, tax_rule=self.tr19) self.quota_tickets.items.add(self.ticket) self.event.settings.set('timezone', 'UTC') self.event.settings.set('attendee_names_asked', False) self.event.settings.set('payment_banktransfer__enabled', True) self.client.get('/%s/%s/' % (self.orga.slug, self.event.slug)) self.session_key = get_cart_session_key(self.client, self.event) self._set_session('email', 'admin@localhost') self.workshopcat = ItemCategory.objects.create(name="Workshops", is_addon=True, event=self.event) self.workshopquota = Quota.objects.create(event=self.event, name='Workshop 1', size=5) self.workshop1 = Item.objects.create(event=self.event, name='Workshop 1', category=self.workshopcat, default_price=Decimal('12.00')) self.workshop2 = Item.objects.create(event=self.event, name='Workshop 2', category=self.workshopcat, default_price=Decimal('12.00')) self.workshop2a = ItemVariation.objects.create(item=self.workshop2, value='A') self.workshop2b = ItemVariation.objects.create(item=self.workshop2, value='B') self.workshopquota.items.add(self.workshop1) self.workshopquota.items.add(self.workshop2) self.workshopquota.variations.add(self.workshop2a) self.workshopquota.variations.add(self.workshop2b) def _set_session(self, key, value): session = self.client.session session['carts'][get_cart_session_key(self.client, self.event)][key] = value session.save() class CheckoutTestCase(BaseCheckoutTestCase, TestCase): def _enable_reverse_charge(self): self.tr19.eu_reverse_charge = True self.tr19.home_country = Country('DE') self.tr19.save() ia = InvoiceAddress.objects.create( is_business=True, vat_id='ATU1234567', vat_id_validated=True, country=Country('AT') ) self._set_session('invoice_address', ia.pk) return ia def _enable_country_specific_taxing(self): self.tr19.custom_rules = json.dumps([ {'country': 'EU', 'address_type': 'individual', 'action': 'vat', 'rate': '20.00'}, {'country': 'US', 'address_type': 'individual', 'action': 'vat', 'rate': '10.00'}, ]) self.tr19.save() with scopes_disabled(): ia = InvoiceAddress.objects.create( country=Country('AT'), ) self._set_session('invoice_address', ia.pk) return ia def test_empty_cart(self): response = self.client.get('/%s/%s/checkout/start' % (self.orga.slug, self.event.slug), follow=True) self.assertRedirects(response, '/%s/%s/?require_cookie=true' % (self.orga.slug, self.event.slug), target_status_code=200) def test_reverse_charge(self): self.tr19.eu_reverse_charge = True self.tr19.home_country = Country('DE') self.tr19.save() self.event.settings.invoice_address_vatid = True with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) with mock.patch('vat_moss.id.validate') as mock_validate: mock_validate.return_value = ('AT', 'AT123456', 'Foo') self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { 'is_business': 'business', 'company': 'Foo', 'name': 'Bar', 'street': 'Baz', 'zipcode': '12345', 'city': 'Here', 'country': 'AT', 'vat_id': 'AT123456', 'email': 'admin@localhost' }, follow=True) cr1.refresh_from_db() assert cr1.price == round_decimal(Decimal('23.00') / Decimal('1.19')) with scopes_disabled(): ia = InvoiceAddress.objects.get(pk=self.client.session['carts'][self.session_key].get('invoice_address')) assert ia.vat_id_validated def test_reverse_charge_enable_then_disable(self): self.test_reverse_charge() with mock.patch('vat_moss.id.validate') as mock_validate: mock_validate.return_value = ('AT', 'AT123456', 'Foo') self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { 'is_business': 'individual', 'name': 'Bar', 'street': 'Baz', 'zipcode': '12345', 'city': 'Here', 'country': 'AT', 'vat_id': '', 'email': 'admin@localhost' }, follow=True) with scopes_disabled(): cr = CartPosition.objects.get(cart_id=self.session_key) assert cr.price == Decimal('23.00') ia = InvoiceAddress.objects.get(pk=self.client.session['carts'][self.session_key].get('invoice_address')) assert not ia.vat_id_validated def test_reverse_charge_invalid_vatid(self): self.tr19.eu_reverse_charge = True self.tr19.home_country = Country('DE') self.tr19.save() self.event.settings.invoice_address_vatid = True with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) with mock.patch('vat_moss.id.validate') as mock_validate: def raiser(*args, **kwargs): import vat_moss.errors raise vat_moss.errors.InvalidError() mock_validate.side_effect = raiser resp = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { 'is_business': 'business', 'company': 'Foo', 'name': 'Bar', 'street': 'Baz', 'zipcode': '12345', 'city': 'Here', 'country': 'AT', 'vat_id': 'AT123456', 'email': 'admin@localhost' }, follow=True) assert 'alert-danger' in resp.rendered_content cr1.refresh_from_db() assert cr1.price == Decimal('23.00') def test_reverse_charge_vatid_non_eu(self): self.tr19.eu_reverse_charge = True self.tr19.home_country = Country('NO') self.tr19.save() self.event.settings.invoice_address_vatid = True with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) with mock.patch('vat_moss.id.validate') as mock_validate: mock_validate.return_value = ('AU', 'AU123456', 'Foo') self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { 'is_business': 'business', 'company': 'Foo', 'name': 'Bar', 'street': 'Baz', 'zipcode': '12345', 'city': 'Here', 'country': 'AU', 'state': 'QLD', 'vat_id': 'AU123456', 'email': 'admin@localhost' }, follow=True) cr1.refresh_from_db() assert cr1.price == round_decimal(Decimal('23.00') / Decimal('1.19')) with scopes_disabled(): ia = InvoiceAddress.objects.get(pk=self.client.session['carts'][self.session_key].get('invoice_address')) assert not ia.vat_id_validated def test_reverse_charge_vatid_same_country(self): self.tr19.eu_reverse_charge = True self.tr19.home_country = Country('AT') self.tr19.save() self.event.settings.invoice_address_vatid = True with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) with mock.patch('vat_moss.id.validate') as mock_validate: mock_validate.return_value = ('AT', 'AT123456', 'Foo') self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { 'is_business': 'business', 'company': 'Foo', 'name': 'Bar', 'street': 'Baz', 'zipcode': '12345', 'city': 'Here', 'country': 'AT', 'vat_id': 'AT123456', 'email': 'admin@localhost' }, follow=True) cr1.refresh_from_db() assert cr1.price == Decimal('23.00') with scopes_disabled(): ia = InvoiceAddress.objects.get(pk=self.client.session['carts'][self.session_key].get('invoice_address')) assert ia.vat_id_validated def test_reverse_charge_vatid_check_invalid_country(self): self.tr19.eu_reverse_charge = True self.tr19.home_country = Country('DE') self.tr19.save() self.event.settings.invoice_address_vatid = True with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) with mock.patch('vat_moss.id.validate') as mock_validate: mock_validate.return_value = ('AT', 'AT123456', 'Foo') resp = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { 'is_business': 'business', 'company': 'Foo', 'name': 'Bar', 'street': 'Baz', 'zipcode': '12345', 'city': 'Here', 'country': 'FR', 'vat_id': 'AT123456', 'email': 'admin@localhost' }, follow=True) assert 'alert-danger' in resp.rendered_content cr1.refresh_from_db() assert cr1.price == Decimal('23.00') def test_reverse_charge_vatid_check_unavailable(self): self.tr19.eu_reverse_charge = True self.tr19.home_country = Country('DE') self.tr19.save() self.event.settings.invoice_address_vatid = True with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) with mock.patch('vat_moss.id.validate') as mock_validate: def raiser(*args, **kwargs): import vat_moss.errors raise vat_moss.errors.WebServiceUnavailableError('Fail') mock_validate.side_effect = raiser self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { 'is_business': 'business', 'company': 'Foo', 'name': 'Bar', 'street': 'Baz', 'zipcode': '12345', 'city': 'Here', 'country': 'AT', 'vat_id': 'AT123456', 'email': 'admin@localhost' }, follow=True) cr1.refresh_from_db() assert cr1.price == Decimal('23.00') with scopes_disabled(): ia = InvoiceAddress.objects.get(pk=self.client.session['carts'][self.session_key].get('invoice_address')) assert not ia.vat_id_validated def test_custom_tax_rules(self): self.tr19.custom_rules = json.dumps([ {'country': 'AT', 'address_type': 'business_vat_id', 'action': 'reverse'}, {'country': 'ZZ', 'address_type': '', 'action': 'vat'}, ]) self.tr19.save() self.event.settings.invoice_address_vatid = True with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) with mock.patch('vat_moss.id.validate') as mock_validate: mock_validate.return_value = ('AT', 'AT123456', 'Foo') self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { 'is_business': 'business', 'company': 'Foo', 'name': 'Bar', 'street': 'Baz', 'zipcode': '12345', 'city': 'Here', 'country': 'AT', 'vat_id': 'AT123456', 'email': 'admin@localhost' }, follow=True) cr1.refresh_from_db() assert cr1.price == Decimal('19.33') with mock.patch('vat_moss.id.validate') as mock_validate: mock_validate.return_value = ('DE', 'DE123456', 'Foo') self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { 'is_business': 'business', 'company': 'Foo', 'name': 'Bar', 'street': 'Baz', 'zipcode': '12345', 'city': 'Here', 'country': 'DE', 'vat_id': 'DE123456', 'email': 'admin@localhost' }, follow=True) cr1.refresh_from_db() assert cr1.price == Decimal('23.00') def test_custom_tax_rules_blocked(self): self.tr19.custom_rules = json.dumps([ {'country': 'AT', 'address_type': 'business_vat_id', 'action': 'reverse'}, {'country': 'ZZ', 'address_type': '', 'action': 'block'}, ]) self.tr19.save() self.event.settings.invoice_address_vatid = True with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) r = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { 'is_business': 'business', 'company': 'Foo', 'name': 'Bar', 'street': 'Baz', 'zipcode': '12345', 'city': 'Here', 'country': 'DE', 'email': 'admin@localhost' }, follow=True) doc = BeautifulSoup(r.rendered_content, "lxml") assert doc.select(".alert-danger") cr1.refresh_from_db() assert cr1.price == Decimal('23.00') with mock.patch('vat_moss.id.validate') as mock_validate: mock_validate.return_value = ('AT', 'AT123456', 'Foo') r = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { 'is_business': 'business', 'company': 'Foo', 'name': 'Bar', 'street': 'Baz', 'zipcode': '12345', 'city': 'Here', 'country': 'AT', 'vat_id': 'AT123456', 'email': 'admin@localhost' }, follow=True) doc = BeautifulSoup(r.rendered_content, "lxml") assert not doc.select(".alert-danger") cr1.refresh_from_db() assert cr1.price == Decimal('19.33') def test_country_taxing(self): self._enable_country_specific_taxing() with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) with mock.patch('vat_moss.id.validate') as mock_validate: mock_validate.return_value = ('AT', 'AT123456', 'Foo') self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { 'is_business': 'individual', 'name': 'Bar', 'street': 'Baz', 'zipcode': '12345', 'city': 'Here', 'country': 'AT', 'email': 'admin@localhost' }, follow=True) cr1.refresh_from_db() assert cr1.price == Decimal('23.20') def test_country_taxing_free_price_and_voucher(self): self._enable_country_specific_taxing() self.ticket.free_price = True self.ticket.save() with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10), voucher=self.event.vouchers.create() ) with mock.patch('vat_moss.id.validate') as mock_validate: mock_validate.return_value = ('AT', 'AT123456', 'Foo') self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { 'is_business': 'individual', 'name': 'Bar', 'street': 'Baz', 'zipcode': '12345', 'city': 'Here', 'country': 'AT', 'email': 'admin@localhost' }, follow=True) cr1.refresh_from_db() assert cr1.price == Decimal('23.20') self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'banktransfer' }, follow=True) self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) with scopes_disabled(): assert not CartPosition.objects.filter(pk=cr1.pk).exists() o = Order.objects.last() assert o.positions.get().price == Decimal('23.20') def test_country_taxing_switch(self): self.test_country_taxing() with mock.patch('vat_moss.id.validate') as mock_validate: mock_validate.return_value = ('AT', 'AT123456', 'Foo') self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { 'is_business': 'individual', 'name': 'Bar', 'street': 'Baz', 'zipcode': '12345', 'city': 'Here', 'country': 'US', 'state': 'CA', 'email': 'admin@localhost' }, follow=True) with scopes_disabled(): cr = CartPosition.objects.get(cart_id=self.session_key) assert cr.price == Decimal('21.26') def test_question_file_upload(self): with scopes_disabled(): q1 = Question.objects.create( event=self.event, question='Student ID', type=Question.TYPE_FILE, required=False ) self.ticket.questions.add(q1) cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="%s-question_%s"]' % (cr1.id, q1.id))), 1) f = SimpleUploadedFile("testfile.txt", b"file_content") response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { '%s-question_%s' % (cr1.id, q1.id): f, 'email': 'admin@localhost' }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), target_status_code=200) with scopes_disabled(): cr1 = CartPosition.objects.get(id=cr1.id) a = cr1.answers.get(question=q1) assert a.file assert a.file.read() == b"file_content" assert os.path.exists(os.path.join(settings.MEDIA_ROOT, a.file.name)) # Delete self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { '%s-question_%s-clear' % (cr1.id, q1.id): 'on', 'email': 'admin@localhost' }, follow=True) with scopes_disabled(): assert not cr1.answers.exists() assert not os.path.exists(os.path.join(settings.MEDIA_ROOT, a.file.name)) def test_attendee_email_required(self): self.event.settings.set('attendee_emails_asked', True) self.event.settings.set('attendee_emails_required', True) with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="%s-attendee_email"]' % cr1.id)), 1) # Not all required fields filled out, expect failure response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { '%s-attendee_email' % cr1.id: '', 'email': 'admin@localhost' }, follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertGreaterEqual(len(doc.select('.has-error')), 1) # Corrected request response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { '%s-attendee_email' % cr1.id: 'foo@localhost', 'email': 'admin@localhost' }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), target_status_code=200) with scopes_disabled(): cr1 = CartPosition.objects.get(id=cr1.id) self.assertEqual(cr1.attendee_email, 'foo@localhost') def test_attendee_company_required(self): self.event.settings.set('attendee_company_asked', True) self.event.settings.set('attendee_company_required', True) with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="%s-company"]' % cr1.id)), 1) # Not all required fields filled out, expect failure response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { '%s-company' % cr1.id: '', 'email': 'admin@localhost' }, follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertGreaterEqual(len(doc.select('.has-error')), 1) # Corrected request response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { '%s-company' % cr1.id: 'foobar', 'email': 'admin@localhost' }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), target_status_code=200) with scopes_disabled(): cr1 = CartPosition.objects.get(id=cr1.id) self.assertEqual(cr1.company, 'foobar') def test_attendee_address_required(self): self.event.settings.set('attendee_addresses_asked', True) self.event.settings.set('attendee_addresses_required', True) with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('textarea[name="%s-street"]' % cr1.id)), 1) # Not all required fields filled out, expect failure response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { '%s-street' % cr1.id: '', '%s-zipcode' % cr1.id: '', '%s-city' % cr1.id: '', '%s-country' % cr1.id: '', '%s-state' % cr1.id: '', 'email': 'admin@localhost' }, follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertGreaterEqual(len(doc.select('.has-error')), 1) # Corrected request response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { '%s-street' % cr1.id: 'Musterstrasse', '%s-zipcode' % cr1.id: '12345', '%s-city' % cr1.id: 'Musterstadt', '%s-country' % cr1.id: 'DE', '%s-state' % cr1.id: '', 'email': 'admin@localhost' }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), target_status_code=200) with scopes_disabled(): cr1 = CartPosition.objects.get(id=cr1.id) self.assertEqual(cr1.street, 'Musterstrasse') self.assertEqual(cr1.zipcode, '12345') self.assertEqual(cr1.city, 'Musterstadt') self.assertEqual(cr1.country, 'DE') def test_attendee_name_required(self): self.event.settings.set('attendee_names_asked', True) self.event.settings.set('attendee_names_required', True) with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="%s-attendee_name_parts_0"]' % cr1.id)), 1) # Not all required fields filled out, expect failure response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { '%s-attendee_name_parts_0' % cr1.id: '', 'email': 'admin@localhost' }, follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertGreaterEqual(len(doc.select('.has-error')), 1) # Corrected request response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { '%s-attendee_name_parts_0' % cr1.id: 'Peter', 'email': 'admin@localhost' }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), target_status_code=200) with scopes_disabled(): cr1 = CartPosition.objects.get(id=cr1.id) self.assertEqual(cr1.attendee_name, 'Peter') def test_attendee_name_scheme(self): self.event.settings.set('attendee_names_asked', True) self.event.settings.set('attendee_names_required', True) self.event.settings.set('name_scheme', 'salutation_title_given_family') with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('select[name="%s-attendee_name_parts_0"]' % cr1.id)), 1) self.assertEqual(len(doc.select('input[name="%s-attendee_name_parts_1"]' % cr1.id)), 1) self.assertEqual(len(doc.select('input[name="%s-attendee_name_parts_2"]' % cr1.id)), 1) self.assertEqual(len(doc.select('input[name="%s-attendee_name_parts_3"]' % cr1.id)), 1) # Not all required fields filled out, expect failure response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { '%s-attendee_name_parts_0' % cr1.id: 'Mr', '%s-attendee_name_parts_1' % cr1.id: '', '%s-attendee_name_parts_2' % cr1.id: 'John', '%s-attendee_name_parts_3' % cr1.id: 'Doe', 'email': 'admin@localhost' }) self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), target_status_code=200) with scopes_disabled(): cr1 = CartPosition.objects.get(id=cr1.id) self.assertEqual(cr1.attendee_name, 'John Doe') self.assertEqual(cr1.attendee_name_parts, { 'salutation': 'Mr', 'title': '', 'given_name': 'John', 'family_name': 'Doe', "_scheme": "salutation_title_given_family" }) def test_attendee_name_optional(self): self.event.settings.set('attendee_names_asked', True) self.event.settings.set('attendee_names_required', False) with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="%s-attendee_name_parts_0"]' % cr1.id)), 1) # Not all fields filled out, expect success response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { '%s-attendee_name_parts_0' % cr1.id: '', 'email': 'admin@localhost' }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), target_status_code=200) with scopes_disabled(): cr1 = CartPosition.objects.get(id=cr1.id) assert not cr1.attendee_name def test_invoice_address_required(self): self.event.settings.invoice_address_asked = True self.event.settings.invoice_address_required = True self.event.settings.invoice_address_not_asked_free = True self.event.settings.set('name_scheme', 'title_given_middle_family') with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="city"]')), 1) # Not all required fields filled out, expect failure response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { 'is_business': 'business', 'city': 'Here', 'country': 'DE', 'vat_id': 'DE123456', 'email': 'admin@localhost' }, follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertGreaterEqual(len(doc.select('.has-error')), 1) # Corrected request response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { 'is_business': 'business', 'company': 'Foo', 'name_parts_0': 'Mr', 'name_parts_1': 'John', 'name_parts_2': '', 'name_parts_3': 'Kennedy', 'street': 'Baz', 'zipcode': '12345', 'city': 'Here', 'country': 'DE', 'vat_id': 'DE123456', 'email': 'admin@localhost' }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), target_status_code=200) with scopes_disabled(): ia = InvoiceAddress.objects.last() assert ia.name_parts == { 'title': 'Mr', 'given_name': 'John', 'middle_name': '', 'family_name': 'Kennedy', "_scheme": "title_given_middle_family" } assert ia.name_cached == 'Mr John Kennedy' def test_invoice_address_hidden_for_free(self): self.event.settings.invoice_address_asked = True self.event.settings.invoice_address_required = True self.event.settings.invoice_address_not_asked_free = True self.event.settings.set('name_scheme', 'title_given_middle_family') with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=0, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="city"]')), 0) response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { 'email': 'admin@localhost' }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), target_status_code=200) def test_invoice_address_optional(self): self.event.settings.invoice_address_asked = True self.event.settings.invoice_address_required = False with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="city"]')), 1) # Not all required fields filled out, expect failure response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { 'is_business': 'business', 'city': 'Here', 'country': 'DE', 'vat_id': 'DE123456', 'email': 'admin@localhost' }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), target_status_code=200) def test_invoice_name_required(self): self.event.settings.invoice_address_asked = False self.event.settings.invoice_name_required = True with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="name_parts_0"]')), 1) self.assertEqual(len(doc.select('input[name="street"]')), 0) # Not all required fields filled out, expect failure response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { 'email': 'admin@localhost' }, follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertGreaterEqual(len(doc.select('.has-error')), 1) # Corrected request response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { 'name_parts_0': 'Raphael', 'email': 'admin@localhost' }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), target_status_code=200) def test_payment(self): # TODO: Test for correct payment method fees self.event.settings.set('payment_stripe__enabled', True) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="payment"]')), 2) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'banktransfer' }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), target_status_code=200) def test_payment_max_value(self): self.event.settings.set('payment_stripe__enabled', True) self.event.settings.set('payment_banktransfer__total_max', Decimal('42.00')) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="payment"]')), 2) with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="payment"]')), 1) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'banktransfer' }, follow=True) self.assertEqual(response.status_code, 200) doc = BeautifulSoup(response.rendered_content, "lxml") assert doc.select(".alert-danger") def test_payment_hidden(self): self.event.settings.set('payment_stripe__enabled', True) self.event.settings.set('payment_banktransfer__enabled', True) self.event.settings.set('payment_banktransfer__hidden', True) self.event.settings.set('payment_banktransfer__hidden_seed', get_random_string(32)) with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="payment"]')), 1) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'banktransfer' }, follow=True) self.assertEqual(response.status_code, 200) doc = BeautifulSoup(response.rendered_content, "lxml") assert doc.select(".alert-danger") self.client.get('/%s/%s/unlock/%s/' % ( self.orga.slug, self.event.slug, hashlib.sha256( (self.event.settings.payment_banktransfer__hidden_seed + self.event.slug).encode() ).hexdigest(), ), follow=True) response = self.client.get('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="payment"]')), 2) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'banktransfer' }, follow=True) self.assertEqual(response.status_code, 200) doc = BeautifulSoup(response.rendered_content, "lxml") assert not doc.select(".alert-danger") def test_payment_min_value(self): self.event.settings.set('payment_stripe__enabled', True) self.event.settings.set('payment_banktransfer__total_min', Decimal('42.00')) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="payment"]')), 1) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'banktransfer' }, follow=True) self.assertEqual(response.status_code, 200) doc = BeautifulSoup(response.rendered_content, "lxml") assert doc.select(".alert-danger") def test_payment_country_ignored_without_invoice_address_required(self): self.event.settings.set('payment_stripe__enabled', True) self.event.settings.set('payment_banktransfer__restricted_countries', ['DE', 'AT']) self.event.settings.set('payment_banktransfer__enabled', True) self.event.settings.set('invoice_address_required', False) ia = InvoiceAddress.objects.create( is_business=True, vat_id='ATU1234567', vat_id_validated=True, country=Country('CH') ) self._set_session('invoice_address', ia.pk) with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="payment"]')), 2) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'banktransfer' }, follow=True) self.assertEqual(response.status_code, 200) doc = BeautifulSoup(response.rendered_content, "lxml") assert not doc.select(".alert-danger") def test_payment_country_allowed(self): self.event.settings.set('payment_stripe__enabled', True) self.event.settings.set('payment_banktransfer__restricted_countries', ['DE', 'AT']) self.event.settings.set('payment_banktransfer__enabled', True) self.event.settings.set('invoice_address_required', True) ia = InvoiceAddress.objects.create( is_business=True, vat_id='ATU1234567', vat_id_validated=True, country=Country('DE'), name_parts={'full_name': 'Foo', "_scheme": "full"}, name_cached='Foo', street='Foo' ) self._set_session('invoice_address', ia.pk) with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="payment"]')), 2) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'banktransfer' }, follow=True) self.assertEqual(response.status_code, 200) doc = BeautifulSoup(response.rendered_content, "lxml") assert not doc.select(".alert-danger") def test_payment_country_blocked(self): self.event.settings.set('payment_stripe__enabled', True) self.event.settings.set('payment_banktransfer__restricted_countries', ['DE', 'AT']) self.event.settings.set('payment_banktransfer__enabled', True) self.event.settings.set('invoice_address_required', True) ia = InvoiceAddress.objects.create( is_business=True, vat_id='ATU1234567', vat_id_validated=True, country=Country('CH'), name_parts={'full_name': 'Foo', "_scheme": "full"}, name_cached='Foo', street='Foo' ) self._set_session('invoice_address', ia.pk) with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="payment"]')), 1) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'banktransfer' }, follow=True) self.assertEqual(response.status_code, 200) doc = BeautifulSoup(response.rendered_content, "lxml") assert doc.select(".alert-danger") def test_giftcard_partial(self): gc = self.orga.issued_gift_cards.create(currency="EUR") gc.transactions.create(value=20) self.event.settings.set('payment_stripe__enabled', True) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="payment"]')), 3) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'giftcard', 'giftcard': gc.secret }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), target_status_code=200) assert '-€20.00' in response.rendered_content assert '3.00' in response.rendered_content assert 'alert-success' in response.rendered_content response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'banktransfer', }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), target_status_code=200) assert '-€20.00' in response.rendered_content assert '3.00' in response.rendered_content response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): o = Order.objects.last() assert o.payments.get(provider='giftcard').amount == Decimal('20.00') assert o.payments.get(provider='banktransfer').amount == Decimal('3.00') assert '-€20.00' in response.rendered_content assert '3.00' in response.rendered_content def test_giftcard_full_with_multiple(self): gc = self.orga.issued_gift_cards.create(currency="EUR") gc.transactions.create(value=20) gc2 = self.orga.issued_gift_cards.create(currency="EUR") gc2.transactions.create(value=20) self.event.settings.set('payment_stripe__enabled', True) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="payment"]')), 3) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'giftcard', 'giftcard': gc.secret }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), target_status_code=200) assert '-€20.00' in response.rendered_content assert '3.00' in response.rendered_content assert 'alert-success' in response.rendered_content response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'giftcard', 'giftcard': gc2.secret }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), target_status_code=200) response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): o = Order.objects.last() assert o.payments.get(provider='giftcard', amount=Decimal('20.00')) assert o.payments.get(provider='giftcard', amount=Decimal('20.00')) def test_giftcard_full(self): gc = self.orga.issued_gift_cards.create(currency="EUR") gc.transactions.create(value=30) self.event.settings.set('payment_stripe__enabled', True) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="payment"]')), 3) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'giftcard', 'giftcard': gc.secret }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), target_status_code=200) assert '-€23.00' in response.rendered_content assert '0.00' in response.rendered_content response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): o = Order.objects.last() assert o.payments.get(provider='giftcard').amount == Decimal('23.00') def test_giftcard_racecondition(self): gc = self.orga.issued_gift_cards.create(currency="EUR") gc.transactions.create(value=20) self.event.settings.set('payment_stripe__enabled', True) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="payment"]')), 3) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'giftcard', 'giftcard': gc.secret }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), target_status_code=200) assert '-€20.00' in response.rendered_content assert '3.00' in response.rendered_content assert 'alert-success' in response.rendered_content response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'banktransfer', }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), target_status_code=200) assert '-€20.00' in response.rendered_content assert '3.00' in response.rendered_content gc.transactions.create(value=-2) response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".alert-danger")), 1) assert '-€18.00' in response.rendered_content assert '5.00' in response.rendered_content response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): o = Order.objects.last() assert o.payments.get(provider='giftcard').amount == Decimal('18.00') assert o.payments.get(provider='banktransfer').amount == Decimal('5.00') def test_giftcard_expired(self): gc = self.orga.issued_gift_cards.create(currency="EUR", expires=now() - timedelta(days=1)) gc.transactions.create(value=20) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'giftcard', 'giftcard': gc.secret }, follow=True) assert 'This gift card is no longer valid.' in response.rendered_content def test_giftcard_invalid_currency(self): gc = self.orga.issued_gift_cards.create(currency="USD") gc.transactions.create(value=20) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'giftcard', 'giftcard': gc.secret }, follow=True) assert 'This gift card does not support this currency.' in response.rendered_content def test_giftcard_invalid_organizer(self): self.orga.issued_gift_cards.create(currency="EUR") orga2 = Organizer.objects.create(slug="foo2", name="foo2") gc = orga2.issued_gift_cards.create(currency="EUR") gc.transactions.create(value=20) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'giftcard', 'giftcard': gc.secret }, follow=True) assert 'This gift card is not known.' in response.rendered_content def test_giftcard_cross_organizer(self): self.orga.issued_gift_cards.create(currency="EUR") orga2 = Organizer.objects.create(slug="foo2", name="foo2") gc = orga2.issued_gift_cards.create(currency="EUR") gc.transactions.create(value=23) self.orga.gift_card_issuer_acceptance.create(issuer=orga2) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'giftcard', 'giftcard': gc.secret }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), target_status_code=200) assert '-€23.00' in response.rendered_content assert '0.00' in response.rendered_content response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): o = Order.objects.last() assert o.payments.get(provider='giftcard').amount == Decimal('23.00') def test_giftcard_in_test_mode(self): gc = self.orga.issued_gift_cards.create(currency="EUR") gc.transactions.create(value=20) self.event.settings.set('payment_banktransfer__enabled', True) self.event.testmode = True self.event.save() with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'giftcard', 'giftcard': gc.secret }, follow=True) assert 'Only test gift cards can be used in test mode.' in response.rendered_content def test_giftcard_not_in_test_mode(self): gc = self.orga.issued_gift_cards.create(currency="EUR", testmode=True) gc.transactions.create(value=20) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'giftcard', 'giftcard': gc.secret }, follow=True) assert 'This gift card can only be used in test mode.' in response.rendered_content def test_giftcard_empty(self): gc = self.orga.issued_gift_cards.create(currency="EUR") self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'giftcard', 'giftcard': gc.secret }, follow=True) assert 'All credit on this gift card has been used.' in response.rendered_content def test_giftcard_twice(self): gc = self.orga.issued_gift_cards.create(currency="EUR") gc.transactions.create(value=20) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'giftcard', 'giftcard': gc.secret }, follow=True) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'giftcard', 'giftcard': gc.secret }, follow=True) assert 'This gift card is already used for your payment.' in response.rendered_content def test_giftcard_swap(self): gc = self.orga.issued_gift_cards.create(currency="EUR") gc.transactions.create(value=20) self.event.settings.set('payment_banktransfer__enabled', True) self.ticket.issue_giftcard = True self.ticket.save() with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.post('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), { 'payment': 'giftcard', 'giftcard': gc.secret }, follow=True) assert 'You cannot pay with gift cards when buying a gift card.' in response.rendered_content def test_premature_confirm(self): response = self.client.get('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) self.assertRedirects(response, '/%s/%s/?require_cookie=true' % (self.orga.slug, self.event.slug), target_status_code=200) self.event.settings.set('payment_stripe__enabled', True) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), target_status_code=200) self._set_session('payment', 'banktransfer') self.event.settings.set('attendee_names_asked', True) self.event.settings.set('attendee_names_required', True) response = self.client.get('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) self.assertRedirects(response, '/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), target_status_code=200) cr1.attendee_name_parts = {"full_name": 'Peter', "_scheme": "full"} cr1.save() with scopes_disabled(): q1 = Question.objects.create( event=self.event, question='Age', type=Question.TYPE_NUMBER, required=True ) self.ticket.questions.add(q1) response = self.client.get('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) self.assertRedirects(response, '/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), target_status_code=200) q1.required = False q1.save() response = self.client.get('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) self.assertEqual(response.status_code, 200) self._set_session('email', 'invalid') response = self.client.get('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) self.assertRedirects(response, '/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), target_status_code=200) def test_subevent(self): self.event.has_subevents = True self.event.save() with scopes_disabled(): se = self.event.subevents.create(name='Foo', date_from=now()) q = se.quotas.create(name="foo", size=None, event=self.event) q.items.add(self.ticket) cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10), subevent=se ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) self.assertEqual(Order.objects.count(), 1) self.assertEqual(OrderPosition.objects.count(), 1) self.assertEqual(OrderPosition.objects.first().subevent, se) def test_require_approval_no_payment_step(self): self.event.settings.invoice_generate = 'True' self.ticket.require_approval = True self.ticket.save() with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=42, expires=now() + timedelta(minutes=10) ) response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) self.assertEqual(Order.objects.count(), 1) self.assertEqual(Order.objects.first().status, Order.STATUS_PENDING) self.assertTrue(Order.objects.first().require_approval) self.assertEqual(OrderPosition.objects.count(), 1) self.assertEqual(Invoice.objects.count(), 0) def test_require_approval_no_payment_step_free(self): self.ticket.require_approval = True self.ticket.save() with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=0, expires=now() + timedelta(minutes=10) ) response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") with scopes_disabled(): self.assertEqual(len(doc.select(".thank-you")), 1) self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) self.assertEqual(Order.objects.count(), 1) self.assertEqual(Order.objects.first().status, Order.STATUS_PENDING) self.assertTrue(Order.objects.first().require_approval) self.assertEqual(OrderPosition.objects.count(), 1) def test_require_approval_in_addon_to_free(self): with scopes_disabled(): ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat, min_count=1, price_included=True) cp1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=0, expires=now() - timedelta(minutes=10) ) self.ticket.default_price = 0 self.ticket.save() self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True) self.workshop1.require_approval = True self.workshop1.save() with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.workshop1, price=0, expires=now() - timedelta(minutes=10), addon_to=cp1 ) self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True) response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): self.assertFalse(CartPosition.objects.filter(id=cp1.id).exists()) self.assertEqual(Order.objects.count(), 1) self.assertEqual(Order.objects.first().status, Order.STATUS_PENDING) self.assertTrue(Order.objects.first().require_approval) self.assertEqual(OrderPayment.objects.count(), 0) self.assertEqual(OrderPosition.objects.count(), 2) def test_free_price(self): self.ticket.free_price = True self.ticket.save() with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=42, expires=now() + timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") with scopes_disabled(): self.assertEqual(len(doc.select(".thank-you")), 1) self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) self.assertEqual(Order.objects.count(), 1) self.assertEqual(OrderPosition.objects.count(), 1) self.assertEqual(OrderPosition.objects.first().price, 42) def test_free_order(self): with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=0, expires=now() + timedelta(minutes=10) ) self._set_session('payment', 'free') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) self.assertEqual(Order.objects.count(), 1) self.assertEqual(OrderPosition.objects.count(), 1) self.assertEqual(OrderPosition.objects.first().price, 0) self.assertEqual(Order.objects.first().status, Order.STATUS_PAID) def test_free_order_require_approval(self): self.ticket.require_approval = True self.ticket.save() with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=0, expires=now() + timedelta(minutes=10) ) self._set_session('payment', 'free') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) self.assertEqual(Order.objects.count(), 1) self.assertEqual(OrderPosition.objects.count(), 1) self.assertEqual(OrderPosition.objects.first().price, 0) self.assertEqual(Order.objects.first().status, Order.STATUS_PENDING) self.assertEqual(Order.objects.first().require_approval, True) def test_confirm_in_time(self): with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") with scopes_disabled(): self.assertEqual(len(doc.select(".thank-you")), 1) self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) self.assertEqual(Order.objects.count(), 1) self.assertEqual(OrderPosition.objects.count(), 1) self.assertEqual(Order.objects.first().status, Order.STATUS_PENDING) def test_subevent_confirm_expired_available(self): with scopes_disabled(): self.event.has_subevents = True self.event.save() se = self.event.subevents.create(name='Foo', date_from=now()) se2 = self.event.subevents.create(name='Foo', date_from=now()) self.quota_tickets.size = 0 self.quota_tickets.subevent = se2 self.quota_tickets.save() q2 = se.quotas.create(event=self.event, size=1, name='Bar') q2.items.add(self.ticket) cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10), subevent=se ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) self.assertEqual(Order.objects.count(), 1) self.assertEqual(OrderPosition.objects.count(), 1) def test_confirm_expired_available(self): with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) self.assertEqual(Order.objects.count(), 1) self.assertEqual(OrderPosition.objects.count(), 1) def test_subevent_confirm_price_changed(self): self.event.has_subevents = True self.event.save() with scopes_disabled(): se = self.event.subevents.create(name='Foo', date_from=now()) q = se.quotas.create(name="foo", size=None, event=self.event) q.items.add(self.ticket) SubEventItem.objects.create(subevent=se, item=self.ticket, price=24) cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10), subevent=se ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".alert-danger")), 1) with scopes_disabled(): cr1 = CartPosition.objects.get(id=cr1.id) self.assertEqual(cr1.price, 24) def test_subevent_disabled(self): self.event.has_subevents = True self.event.save() with scopes_disabled(): se = self.event.subevents.create(name='Foo', date_from=now()) q = se.quotas.create(name="foo", size=None, event=self.event) q.items.add(self.ticket) SubEventItem.objects.create(subevent=se, item=self.ticket, price=24, disabled=True) cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10), subevent=se ) self._set_session('payment', 'banktransfer') self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) with scopes_disabled(): assert not CartPosition.objects.filter(id=cr1.id).exists() def test_subevent_variation_disabled(self): self.event.has_subevents = True self.event.save() with scopes_disabled(): se = self.event.subevents.create(name='Foo', date_from=now()) q = se.quotas.create(name="foo", size=None, event=self.event) q.items.add(self.workshop2) q.variations.add(self.workshop2b) SubEventItemVariation.objects.create(subevent=se, variation=self.workshop2b, price=24, disabled=True) cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.workshop2, variation=self.workshop2b, price=23, expires=now() - timedelta(minutes=10), subevent=se ) self._set_session('payment', 'banktransfer') self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) with scopes_disabled(): assert not CartPosition.objects.filter(id=cr1.id).exists() def test_addon_price_included(self): with scopes_disabled(): ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat, min_count=1, price_included=True) cp1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10) ) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.workshop1, price=0, expires=now() - timedelta(minutes=10), addon_to=cp1 ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): self.assertEqual(OrderPosition.objects.filter(item=self.workshop1).last().price, 0) def test_confirm_price_changed_reverse_charge(self): self._enable_reverse_charge() self.ticket.default_price = 24 self.ticket.save() with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".alert-danger")), 1) with scopes_disabled(): cr1 = CartPosition.objects.get(id=cr1.id) self.assertEqual(cr1.price, round_decimal(Decimal('24.00') / Decimal('1.19'))) def test_confirm_price_changed(self): self.ticket.default_price = 24 self.ticket.save() with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".alert-danger")), 1) with scopes_disabled(): cr1 = CartPosition.objects.get(id=cr1.id) self.assertEqual(cr1.price, 24) def test_confirm_free_price_increased(self): self.ticket.default_price = 24 self.ticket.free_price = True self.ticket.save() with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".alert-danger")), 1) with scopes_disabled(): cr1 = CartPosition.objects.get(id=cr1.id) self.assertEqual(cr1.price, 24) def test_voucher(self): with scopes_disabled(): v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event, price_mode='set', valid_until=now() + timedelta(days=2)) cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=12, expires=now() + timedelta(minutes=10), voucher=v ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) self.assertEqual(Order.objects.count(), 1) self.assertEqual(OrderPosition.objects.count(), 1) self.assertEqual(OrderPosition.objects.first().voucher, v) self.assertEqual(Voucher.objects.get(pk=v.pk).redeemed, 1) def test_voucher_required(self): with scopes_disabled(): v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event, price_mode='set', valid_until=now() + timedelta(days=2)) self.ticket.require_voucher = True self.ticket.save() CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=12, expires=now() + timedelta(minutes=10), voucher=v ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): self.assertEqual(Voucher.objects.get(pk=v.pk).redeemed, 1) def test_voucher_required_but_missing(self): self.ticket.require_voucher = True self.ticket.save() with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=12, expires=now() + timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") assert doc.select(".alert-danger") def test_voucher_price_changed(self): with scopes_disabled(): v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event, price_mode='set', valid_until=now() + timedelta(days=2)) cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=13, expires=now() - timedelta(minutes=10), voucher=v ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".alert-danger")), 1) with scopes_disabled(): cr1 = CartPosition.objects.get(id=cr1.id) self.assertEqual(cr1.price, Decimal('12.00')) def test_voucher_redeemed(self): with scopes_disabled(): v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event, valid_until=now() + timedelta(days=2), redeemed=1) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=12, expires=now() - timedelta(minutes=10), voucher=v ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertIn("has already been", doc.select(".alert-danger")[0].text) def test_voucher_multiuse_redeemed(self): with scopes_disabled(): v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event, valid_until=now() + timedelta(days=2), max_usages=3, redeemed=3) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=12, expires=now() - timedelta(minutes=10), voucher=v ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertIn("has already been", doc.select(".alert-danger")[0].text) def test_voucher_multiuse_partially(self): with scopes_disabled(): v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event, price_mode='set', valid_until=now() + timedelta(days=2), max_usages=3, redeemed=2) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=12, expires=now() - timedelta(minutes=10), voucher=v ) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=12, expires=now() - timedelta(minutes=10), voucher=v ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertIn("has already been", doc.select(".alert-danger")[0].text) with scopes_disabled(): assert CartPosition.objects.filter(cart_id=self.session_key).count() == 1 def test_voucher_multiuse_ok(self): with scopes_disabled(): v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event, price_mode='set', valid_until=now() + timedelta(days=2), max_usages=3, redeemed=1) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=12, expires=now() - timedelta(minutes=10), voucher=v ) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=12, expires=now() - timedelta(minutes=10), voucher=v ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) with scopes_disabled(): doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) self.assertFalse(CartPosition.objects.filter(cart_id=self.session_key).exists()) self.assertEqual(Order.objects.count(), 1) self.assertEqual(OrderPosition.objects.count(), 2) v.refresh_from_db() assert v.redeemed == 3 def test_voucher_multiuse_in_other_cart_expired(self): with scopes_disabled(): v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event, price_mode='set', valid_until=now() + timedelta(days=2), max_usages=3, redeemed=1) CartPosition.objects.create( event=self.event, cart_id='other', item=self.ticket, price=12, expires=now() - timedelta(minutes=10), voucher=v ) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=12, expires=now() - timedelta(minutes=10), voucher=v ) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=12, expires=now() - timedelta(minutes=10), voucher=v ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): self.assertFalse(CartPosition.objects.filter(cart_id=self.session_key).exists()) self.assertEqual(Order.objects.count(), 1) self.assertEqual(OrderPosition.objects.count(), 2) v.refresh_from_db() assert v.redeemed == 3 def test_voucher_multiuse_in_other_cart(self): with scopes_disabled(): v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event, price_mode='set', valid_until=now() + timedelta(days=2), max_usages=3, redeemed=1) CartPosition.objects.create( event=self.event, cart_id='other', item=self.ticket, price=12, expires=now() + timedelta(minutes=10), voucher=v ) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=12, expires=now() - timedelta(minutes=10), voucher=v ) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=12, expires=now() - timedelta(minutes=10), voucher=v ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertIn("has already been", doc.select(".alert-danger")[0].text) with scopes_disabled(): assert CartPosition.objects.filter(cart_id=self.session_key).count() == 1 def test_voucher_ignore_quota(self): self.quota_tickets.size = 0 self.quota_tickets.save() with scopes_disabled(): v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event, price_mode='set', valid_until=now() + timedelta(days=2), allow_ignore_quota=True) cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=12, expires=now() - timedelta(minutes=10), voucher=v ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) self.assertEqual(Order.objects.count(), 1) self.assertEqual(OrderPosition.objects.count(), 1) def test_voucher_block_quota(self): self.quota_tickets.size = 1 self.quota_tickets.save() with scopes_disabled(): v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event, price_mode='set', valid_until=now() + timedelta(days=2), block_quota=True) cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=12, expires=now() - timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".alert-danger")), 1) with scopes_disabled(): self.assertEqual(CartPosition.objects.filter(cart_id=self.session_key).count(), 1) cr1.voucher = v cr1.save() self.client.get('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) self.assertEqual(Order.objects.count(), 1) self.assertEqual(OrderPosition.objects.count(), 1) def test_voucher_block_quota_other_quota_full(self): with scopes_disabled(): self.quota_tickets.size = 0 self.quota_tickets.save() q2 = self.event.quotas.create(name='Testquota', size=0) q2.items.add(self.ticket) v = Voucher.objects.create(quota=self.quota_tickets, value=Decimal('12.00'), event=self.event, valid_until=now() + timedelta(days=2), block_quota=True) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=12, expires=now() - timedelta(minutes=10), voucher=v ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertTrue(doc.select(".alert-danger")) with scopes_disabled(): self.assertFalse(Order.objects.exists()) def test_voucher_double(self): self.quota_tickets.size = 2 self.quota_tickets.save() with scopes_disabled(): v = Voucher.objects.create(item=self.ticket, event=self.event, valid_until=now() + timedelta(days=2), block_quota=True) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10), voucher=v ) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10), voucher=v ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") with scopes_disabled(): self.assertEqual(CartPosition.objects.filter(cart_id=self.session_key, voucher=v).count(), 1) self.assertEqual(len(doc.select(".alert-danger")), 1) self.assertFalse(Order.objects.exists()) response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") with scopes_disabled(): self.assertFalse(CartPosition.objects.filter(cart_id=self.session_key, voucher=v).exists()) self.assertEqual(len(doc.select(".thank-you")), 1) self.assertEqual(Order.objects.count(), 1) self.assertEqual(OrderPosition.objects.count(), 1) def test_max_per_item_failed(self): self.quota_tickets.size = 3 self.quota_tickets.save() self.ticket.max_per_order = 1 self.ticket.save() with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10), ) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10), ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") with scopes_disabled(): self.assertEqual(CartPosition.objects.filter(cart_id=self.session_key).count(), 1) self.assertEqual(len(doc.select(".alert-danger")), 1) self.assertFalse(Order.objects.exists()) response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") with scopes_disabled(): self.assertEqual(len(doc.select(".thank-you")), 1) self.assertEqual(Order.objects.count(), 1) self.assertEqual(OrderPosition.objects.count(), 1) def test_subevent_confirm_expired_partial(self): self.event.has_subevents = True self.event.save() with scopes_disabled(): se = self.event.subevents.create(name='Foo', date_from=now()) se2 = self.event.subevents.create(name='Foo', date_from=now()) self.quota_tickets.size = 10 self.quota_tickets.subevent = se2 self.quota_tickets.save() q2 = se.quotas.create(event=self.event, size=1, name='Bar') q2.items.add(self.ticket) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10), subevent=se ) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10), subevent=se ) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10), subevent=se2 ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".alert-danger")), 1) with scopes_disabled(): self.assertEqual(CartPosition.objects.filter(cart_id=self.session_key).count(), 2) def test_confirm_expired_partial(self): self.quota_tickets.size = 1 self.quota_tickets.save() with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10) ) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".alert-danger")), 1) with scopes_disabled(): self.assertEqual(CartPosition.objects.filter(cart_id=self.session_key).count(), 1) def test_confirm_event_over(self): self.event.date_to = now() - datetime.timedelta(days=1) self.event.save() with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertGreaterEqual(len(doc.select(".alert-danger")), 1) def test_confirm_presale_over(self): self.event.presale_end = now() - datetime.timedelta(days=1) self.event.save() with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertGreaterEqual(len(doc.select(".alert-danger")), 1) def test_confirm_payment_period_over(self): self.event.settings.payment_term_last = (now() - datetime.timedelta(days=1)).date().isoformat() with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertGreaterEqual(len(doc.select(".alert-danger")), 1) def test_confirm_require_voucher(self): self.ticket.require_voucher = True self.ticket.save() with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertGreaterEqual(len(doc.select(".alert-danger")), 1) with scopes_disabled(): self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) def test_confirm_require_hide_without_voucher(self): self.ticket.require_voucher = True self.ticket.save() with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertGreaterEqual(len(doc.select(".alert-danger")), 1) with scopes_disabled(): self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) def test_confirm_no_longer_available(self): self.ticket.available_until = now() - timedelta(days=1) self.ticket.save() with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertGreaterEqual(len(doc.select(".alert-danger")), 1) with scopes_disabled(): self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) def test_confirm_inactive(self): self.ticket.active = False self.ticket.save() with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertGreaterEqual(len(doc.select(".alert-danger")), 1) with scopes_disabled(): self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) def test_confirm_expired_unavailable(self): self.quota_tickets.size = 0 self.quota_tickets.save() with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertGreaterEqual(len(doc.select(".alert-danger")), 1) with scopes_disabled(): self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) def test_confirm_completely_unavailable(self): self.quota_tickets.items.remove(self.ticket) with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertGreaterEqual(len(doc.select(".alert-danger")), 1) with scopes_disabled(): self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) def test_confirm_expired_with_blocking_voucher_unavailable(self): self.quota_tickets.size = 0 self.quota_tickets.save() with scopes_disabled(): v = Voucher.objects.create(quota=self.quota_tickets, event=self.event, block_quota=True) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, voucher=v, price=23, expires=now() - timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) def test_confirm_expired_with_non_blocking_voucher_unavailable(self): self.quota_tickets.size = 0 self.quota_tickets.save() with scopes_disabled(): v = Voucher.objects.create(quota=self.quota_tickets, event=self.event) cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, voucher=v, price=23, expires=now() - timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertGreaterEqual(len(doc.select(".alert-danger")), 1) with scopes_disabled(): self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) def test_confirm_not_expired_with_blocking_voucher_unavailable(self): self.quota_tickets.size = 0 self.quota_tickets.save() with scopes_disabled(): v = Voucher.objects.create(quota=self.quota_tickets, event=self.event, block_quota=True) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, voucher=v, price=23, expires=now() + timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) def test_confirm_not_expired_with_non_blocking_voucher_unavailable(self): self.quota_tickets.size = 0 self.quota_tickets.save() with scopes_disabled(): v = Voucher.objects.create(quota=self.quota_tickets, event=self.event) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, voucher=v, price=23, expires=now() + timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) def test_addons_as_first_step(self): with scopes_disabled(): ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/start' % (self.orga.slug, self.event.slug), follow=True) self.assertRedirects(response, '/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug), target_status_code=200) def test_set_addons_item_and_variation(self): with scopes_disabled(): ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat) cp1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10) ) cp2 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10) ) response = self.client.post('/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug), { 'cp_{}_item_{}'.format(cp1.pk, self.workshop1.pk): '1', 'cp_{}_variation_{}_{}'.format(cp2.pk, self.workshop2.pk, self.workshop2a.pk): '1', }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), target_status_code=200) with scopes_disabled(): assert cp1.addons.first().item == self.workshop1 assert cp2.addons.first().item == self.workshop2 assert cp2.addons.first().variation == self.workshop2a def test_set_addon_multi(self): with scopes_disabled(): ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat, multi_allowed=True, max_count=2) cp1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10) ) response = self.client.post('/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug), { 'cp_{}_item_{}'.format(cp1.pk, self.workshop1.pk): '2', }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), target_status_code=200) with scopes_disabled(): assert cp1.addons.count() == 2 assert cp1.addons.first().item == self.workshop1 assert cp1.addons.last().item == self.workshop1 def test_set_addon_free_price(self): with scopes_disabled(): self.workshop1.free_price = True self.workshop1.save() ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat) cp1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10) ) response = self.client.post('/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug), { 'cp_{}_item_{}'.format(cp1.pk, self.workshop1.pk): '1', 'cp_{}_item_{}_price'.format(cp1.pk, self.workshop1.pk): '999,99', }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), target_status_code=200) with scopes_disabled(): assert cp1.addons.count() == 1 assert cp1.addons.first().item == self.workshop1 assert cp1.addons.first().price == Decimal('999.99') def test_set_addons_required(self): with scopes_disabled(): ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat, min_count=1) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug)) self.assertRedirects(response, '/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug), target_status_code=200) response = self.client.get('/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug)) assert 'Workshop 1' in response.rendered_content assert '€12.00' in response.rendered_content def test_set_addons_included(self): with scopes_disabled(): ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat, min_count=1, price_included=True) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True) self.assertRedirects(response, '/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug), target_status_code=200) assert 'Workshop 1' in response.rendered_content assert '€12.00' not in response.rendered_content def test_set_addons_hide_sold_out(self): with scopes_disabled(): self.workshopquota.size = 0 self.workshopquota.save() ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat, min_count=1) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True) self.assertRedirects(response, '/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug), target_status_code=200) assert 'Workshop 1' in response.rendered_content self.event.settings.hide_sold_out = True response = self.client.get('/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug), follow=True) assert 'Workshop 1' not in response.rendered_content def test_set_addons_hidden_if_available(self): with scopes_disabled(): self.workshopquota2 = Quota.objects.create(event=self.event, name='Workshop 1', size=5) self.workshopquota2.items.add(self.workshop2) self.workshopquota2.variations.add(self.workshop2a) self.workshop2.hidden_if_available = self.workshopquota self.workshop2.save() ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat, min_count=1) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True) self.assertRedirects(response, '/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug), target_status_code=200) assert 'Workshop 1' in response.rendered_content assert 'Workshop 2' not in response.rendered_content self.workshopquota.size = 0 self.workshopquota.save() response = self.client.get('/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug), follow=True) assert 'Workshop 1' in response.rendered_content assert 'Workshop 2' in response.rendered_content def test_set_addons_subevent(self): with scopes_disabled(): self.event.has_subevents = True self.event.save() se = self.event.subevents.create(name='Foo', date_from=now()) self.workshopquota.size = 1 self.workshopquota.subevent = se self.workshopquota.save() SubEventItem.objects.create(subevent=se, item=self.workshop1, price=42) ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat, min_count=1) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10), subevent=se ) response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True) self.assertRedirects(response, '/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug), target_status_code=200) assert '42.00' in response.rendered_content def test_set_addons_subevent_net_prices(self): with scopes_disabled(): self.event.has_subevents = True self.event.settings.display_net_prices = True self.event.save() se = self.event.subevents.create(name='Foo', date_from=now()) self.workshopquota.size = 1 self.workshopquota.subevent = se self.workshopquota.save() self.workshop1.tax_rule = self.event.tax_rules.get_or_create(rate=Decimal('19.00'), name="VAT")[0] self.workshop1.save() self.workshop2.tax_rule = self.event.tax_rules.get_or_create(rate=Decimal('19.00'), name="VAT")[0] self.workshop2.save() SubEventItem.objects.create(subevent=se, item=self.workshop1, price=42) ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat, min_count=1) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() - timedelta(minutes=10), subevent=se ) response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True) self.assertRedirects(response, '/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug), target_status_code=200) assert '35.29' in response.rendered_content assert '10.08' in response.rendered_content def test_confirm_subevent_presale_not_yet(self): with scopes_disabled(): self.event.has_subevents = True self.event.settings.display_net_prices = True self.event.save() se = self.event.subevents.create(name='Foo', date_from=now(), presale_start=now() + datetime.timedelta(days=1)) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10), subevent=se ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertGreaterEqual(len(doc.select(".alert-danger")), 1) assert 'presale period for one of the events in your cart has not yet started.' in response.rendered_content with scopes_disabled(): assert not CartPosition.objects.filter(cart_id=self.session_key).exists() def test_confirm_subevent_presale_over(self): with scopes_disabled(): self.event.has_subevents = True self.event.settings.display_net_prices = True self.event.save() se = self.event.subevents.create(name='Foo', date_from=now(), presale_end=now() - datetime.timedelta(days=1)) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10), subevent=se ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertGreaterEqual(len(doc.select(".alert-danger")), 1) assert 'presale period for one of the events in your cart has ended.' in response.rendered_content with scopes_disabled(): assert not CartPosition.objects.filter(cart_id=self.session_key).exists() def test_confirm_subevent_payment_period_over(self): with scopes_disabled(): self.event.has_subevents = True self.event.settings.display_net_prices = True self.event.save() self.event.settings.payment_term_last = 'RELDATE/1/23:59:59/date_from/' se = self.event.subevents.create(name='Foo', date_from=now()) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10), subevent=se ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertGreaterEqual(len(doc.select(".alert-danger")), 1) assert 'presale period for one of the events in your cart has ended.' in response.rendered_content with scopes_disabled(): assert not CartPosition.objects.filter(cart_id=self.session_key).exists() def test_confirm_subevent_ignore_series_dates(self): self.event.has_subevents = True self.event.date_to = now() - datetime.timedelta(days=1) self.event.save() with scopes_disabled(): se = self.event.subevents.create(name='Foo', date_from=now(), presale_end=now() + datetime.timedelta(days=1)) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10), subevent=se ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) def test_create_testmode_order_in_testmode(self): self.event.testmode = True self.event.save() with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.get('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) assert "test mode" in response.rendered_content response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): assert Order.objects.last().testmode assert Order.objects.last().code[1] == "0" def test_do_not_create_testmode_order_without_testmode(self): with scopes_disabled(): CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) self._set_session('payment', 'banktransfer') response = self.client.get('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) assert "test mode" not in response.rendered_content response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): assert not Order.objects.last().testmode assert "0" not in Order.objects.last().code def test_receive_order_confirmation_and_paid_mail(self): with scopes_disabled(): cp1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) djmail.outbox = [] oid = _perform_order(self.event, 'manual', [cp1.pk], 'admin@example.org', 'en', None, {}, 'web') assert len(djmail.outbox) == 1 o = Order.objects.get(pk=oid) o.payments.first().confirm() assert len(djmail.outbox) == 2 def test_order_confirmation_and_paid_mail_not_send_on_disabled_sales_channel(self): with scopes_disabled(): cp1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) djmail.outbox = [] self.event.settings.mail_sales_channel_placed_paid = [] oid = _perform_order(self.event, 'manual', [cp1.pk], 'admin@example.org', 'en', None, {}, 'web') assert len(djmail.outbox) == 0 o = Order.objects.get(pk=oid) o.payments.first().confirm() assert len(djmail.outbox) == 0 class QuestionsTestCase(BaseCheckoutTestCase, TestCase): def test_timezone(self): """ Test basic timezone change handling by date and time questions """ with scopes_disabled(): q1 = Question.objects.create( event=self.event, question='When did you wake up today?', type=Question.TYPE_TIME, required=True ) q2 = Question.objects.create( event=self.event, question='When was your last haircut?', type=Question.TYPE_DATE, required=True ) q3 = Question.objects.create( event=self.event, question='When are you going to arrive?', type=Question.TYPE_DATETIME, required=True ) self.ticket.questions.add(q1) self.ticket.questions.add(q2) self.ticket.questions.add(q3) cr = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { '%s-question_%s' % (cr.id, q1.id): '06:30', '%s-question_%s' % (cr.id, q2.id): '2005-12-31', '%s-question_%s_0' % (cr.id, q3.id): '2018-01-01', '%s-question_%s_1' % (cr.id, q3.id): '5:23', 'email': 'admin@localhost', }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), target_status_code=200) self.event.settings.set('timezone', 'US/Central') with scopes_disabled(): o1 = QuestionAnswer.objects.get(question=q1) o2 = QuestionAnswer.objects.get(question=q2) o3 = QuestionAnswer.objects.get(question=q3) order = Order.objects.create(event=self.event, status=Order.STATUS_PAID, expires=now() + timedelta(days=3), total=4) op = OrderPosition.objects.create(order=order, item=self.ticket, price=42) o1.cartposition, o2.cartposition, o3.cartposition = None, None, None o1.orderposition, o2.orderposition, o3.orderposition = op, op, op # only time and date answers should be unaffected by timezone change self.assertEqual(str(o1), '06:30') self.assertEqual(str(o2), '2005-12-31') o3date, o3time = str(o3).split(' ') self.assertEqual(o3date, '2017-12-31') self.assertEqual(o3time, '23:23') def test_addon_questions(self): with scopes_disabled(): q1 = Question.objects.create( event=self.event, question='Age', type=Question.TYPE_NUMBER, required=True ) q1.items.add(self.ticket) q1.items.add(self.workshop1) ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat, min_count=1, price_included=True) cp1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) cp1.answers.create(question=q1, answer='12') cp2 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.workshop1, addon_to=cp1, price=0, expires=now() + timedelta(minutes=10) ) cp2.answers.create(question=q1, answer='12') self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): self.assertEqual(OrderPosition.objects.filter(item=self.ticket).first().answers.first().answer, '12') self.assertEqual(OrderPosition.objects.filter(item=self.workshop1).first().answers.first().answer, '12') def test_questions(self): with scopes_disabled(): q1 = Question.objects.create( event=self.event, question='Age', type=Question.TYPE_NUMBER, required=True ) q2 = Question.objects.create( event=self.event, question='How have you heard from us?', type=Question.TYPE_STRING, required=False ) self.ticket.questions.add(q1) self.ticket.questions.add(q2) cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) cr2 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=20, expires=now() + timedelta(minutes=10) ) response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select('input[name="%s-question_%s"]' % (cr1.id, q1.id))), 1) self.assertEqual(len(doc.select('input[name="%s-question_%s"]' % (cr2.id, q1.id))), 1) self.assertEqual(len(doc.select('input[name="%s-question_%s"]' % (cr1.id, q2.id))), 1) self.assertEqual(len(doc.select('input[name="%s-question_%s"]' % (cr2.id, q2.id))), 1) # Not all required fields filled out, expect failure response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { '%s-question_%s' % (cr1.id, q1.id): '42', '%s-question_%s' % (cr2.id, q1.id): '', '%s-question_%s' % (cr1.id, q2.id): 'Internet', '%s-question_%s' % (cr2.id, q2.id): '', 'email': 'admin@localhost' }, follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertGreaterEqual(len(doc.select('.has-error')), 1) # Corrected request response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), { '%s-question_%s' % (cr1.id, q1.id): '42', '%s-question_%s' % (cr2.id, q1.id): '0', '%s-question_%s' % (cr1.id, q2.id): 'Internet', '%s-question_%s' % (cr2.id, q2.id): '', 'email': 'admin@localhost' }, follow=True) self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), target_status_code=200) with scopes_disabled(): cr1 = CartPosition.objects.get(id=cr1.id) cr2 = CartPosition.objects.get(id=cr2.id) self.assertEqual(cr1.answers.filter(question=q1).count(), 1) self.assertEqual(cr2.answers.filter(question=q1).count(), 1) self.assertEqual(cr1.answers.filter(question=q2).count(), 1) self.assertFalse(cr2.answers.filter(question=q2).exists()) def _test_question_input(self, data, should_fail): with scopes_disabled(): cr1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=23, expires=now() + timedelta(minutes=10) ) pl = { ('%s-question_%s' % (cr1.id, k.id)): v for k, v in data.items() if v != 'False' } pl['email'] = 'admin@localhost' response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), pl, follow=True) if should_fail: doc = BeautifulSoup(response.rendered_content, "lxml") assert doc.select('.has-error') assert doc.select('.alert-danger') else: self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), target_status_code=200) with scopes_disabled(): cr1.answers.all().delete() with scopes_disabled(): for k, v in data.items(): a = cr1.answers.create(question=k, answer=str(v)) if k.type in ('M', 'C'): a.options.add(*k.options.filter(identifier__in=(v if isinstance(v, list) else [v]))) response = self.client.get('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), follow=True) if should_fail: self.assertRedirects(response, '/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), target_status_code=200) doc = BeautifulSoup(response.rendered_content, "lxml") assert doc.select('.alert-warning') else: assert response.status_code == 200 doc = BeautifulSoup(response.rendered_content, "lxml") assert not doc.select('.alert-warning') @scopes_disabled() def _setup_dependency_questions(self): self.q1 = self.event.questions.create( event=self.event, question='What industry are you in?', type=Question.TYPE_CHOICE, required=True ) self.q1.options.create(answer='Tech', identifier='TECH') self.q1.options.create(answer='Health', identifier='HEALTH') self.q1.options.create(answer='IT', identifier='IT') self.q2a = self.event.questions.create( event=self.event, question='What is your occupation?', type=Question.TYPE_CHOICE_MULTIPLE, required=False, dependency_question=self.q1, dependency_values=['TECH', 'IT'] ) self.q2a.options.create(answer='Software developer', identifier='DEV') self.q2a.options.create(answer='System administrator', identifier='ADMIN') self.q2b = self.event.questions.create( event=self.event, question='What is your occupation?', type=Question.TYPE_CHOICE_MULTIPLE, required=True, dependency_question=self.q1, dependency_values=['HEALTH'] ) self.q2b.options.create(answer='Doctor', identifier='DOC') self.q2b.options.create(answer='Nurse', identifier='NURSE') self.q3 = self.event.questions.create( event=self.event, question='Do you like Python?', type=Question.TYPE_BOOLEAN, required=False, dependency_question=self.q2a, dependency_values=['DEV'] ) self.q4a = self.event.questions.create( event=self.event, question='Why?', type=Question.TYPE_TEXT, required=True, dependency_question=self.q3, dependency_values=['True'] ) self.q4b = self.event.questions.create( event=self.event, question='Why not?', type=Question.TYPE_TEXT, required=True, dependency_question=self.q3, dependency_values=['False'] ) self.ticket.questions.add(self.q1) self.ticket.questions.add(self.q2a) self.ticket.questions.add(self.q2b) self.ticket.questions.add(self.q3) self.ticket.questions.add(self.q4a) self.ticket.questions.add(self.q4b) def test_question_dependencies_first_path(self): self._setup_dependency_questions() self._test_question_input({ self.q1: 'HEALTH', self.q2b: 'NURSE' }, should_fail=False) def test_question_dependencies_sidepath_ignored(self): self._setup_dependency_questions() self._test_question_input({ self.q1: 'HEALTH', self.q2b: 'NURSE', self.q2a: 'DEV', self.q3: 'True', }, should_fail=False) def test_question_dependencies_first_path_required(self): self._setup_dependency_questions() self._test_question_input({ self.q1: 'HEALTH', }, should_fail=True) def test_question_dependencies_second_path(self): self._setup_dependency_questions() self._test_question_input({ self.q1: 'TECH', self.q2a: 'DEV', self.q3: 'True', self.q4a: 'No curly braces!' }, should_fail=False) def test_question_dependencies_second_path_alterative(self): self._setup_dependency_questions() self._test_question_input({ self.q1: 'IT', self.q2a: 'DEV', self.q3: 'True', self.q4a: 'No curly braces!' }, should_fail=False) def test_question_dependencies_subitem_required(self): self._setup_dependency_questions() self._test_question_input({ self.q1: 'HEALTH', }, should_fail=True) def test_question_dependencies_subsubitem_required(self): self._setup_dependency_questions() self._test_question_input({ self.q1: 'TECH', self.q2a: 'DEV', self.q3: 'True', }, should_fail=True) def test_question_dependencies_subsubitem_required_alternative(self): self._setup_dependency_questions() self._test_question_input({ self.q1: 'IT', self.q2a: 'DEV', self.q3: 'True', }, should_fail=True) def test_question_dependencies_parent_not_required(self): self._setup_dependency_questions() self._test_question_input({ self.q1: 'TECH', }, should_fail=False) def test_question_dependencies_conditional_require_bool(self): self._setup_dependency_questions() self._test_question_input({ self.q1: 'TECH', self.q2a: 'DEV', self.q3: 'False', self.q4b: 'No curly braces!' }, should_fail=False) def test_question_dependencies_conditional_require_bool_fail(self): self._setup_dependency_questions() self._test_question_input({ self.q1: 'TECH', self.q2a: 'DEV', self.q3: 'False', }, should_fail=True) class CheckoutBundleTest(BaseCheckoutTestCase, TestCase): @scopes_disabled() def setUp(self): super().setUp() self.trans = Item.objects.create(event=self.event, name='Public Transport Ticket', default_price=2.50) self.transquota = Quota.objects.create(event=self.event, name='Transport', size=5) self.transquota.items.add(self.trans) self.bundle1 = ItemBundle.objects.create( base_item=self.ticket, bundled_item=self.trans, designated_price=1.5, count=1 ) self.cp1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=21.5, expires=now() + timedelta(minutes=10) ) self.bundled1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.trans, addon_to=self.cp1, price=1.5, expires=now() + timedelta(minutes=10), is_bundled=True ) @classscope(attr='orga') def test_simple_bundle(self): oid = _perform_order(self.event, 'manual', [self.cp1.pk, self.bundled1.pk], 'admin@example.org', 'en', None, {}, 'web') o = Order.objects.get(pk=oid) cp = o.positions.get(addon_to__isnull=True) assert cp.item == self.ticket assert cp.price == 23 - 1.5 assert cp.addons.count() == 1 a = cp.addons.get() assert a.item == self.trans assert a.price == 1.5 @classscope(attr='orga') def test_simple_bundle_with_variation(self): v = self.trans.variations.create(value="foo", default_price=4) self.transquota.variations.add(v) self.bundle1.bundled_variation = v self.bundle1.save() self.bundled1.variation = v self.bundled1.save() oid = _perform_order(self.event, 'manual', [self.cp1.pk, self.bundled1.pk], 'admin@example.org', 'en', None, {}, 'web') o = Order.objects.get(pk=oid) cp = o.positions.get(addon_to__isnull=True) assert cp.item == self.ticket assert cp.price == 23 - 1.5 assert cp.addons.count() == 1 a = cp.addons.get() assert a.item == self.trans assert a.variation == v assert a.price == 1.5 @classscope(attr='orga') def test_bundle_with_count(self): self.cp1.price -= 1.5 self.cp1.save() bundled2 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.trans, addon_to=self.cp1, price=1.5, expires=now() + timedelta(minutes=10), is_bundled=True ) oid = _perform_order(self.event, 'manual', [self.cp1.pk, self.bundled1.pk, bundled2.pk], 'admin@example.org', 'en', None, {}, 'web') o = Order.objects.get(pk=oid) cp = o.positions.get(addon_to__isnull=True) assert cp.item == self.ticket assert cp.price == 23 - 1.5 - 1.5 assert cp.addons.count() == 2 a = cp.addons.first() assert a.item == self.trans assert a.price == 1.5 a = cp.addons.last() assert a.item == self.trans assert a.price == 1.5 @classscope(attr='orga') def test_bundle_position_free_price(self): self.ticket.free_price = True self.ticket.default_price = 1 self.ticket.save() self.cp1.price = 20 - 1.5 self.cp1.save() oid = _perform_order(self.event, 'manual', [self.cp1.pk, self.bundled1.pk], 'admin@example.org', 'en', None, {}, 'web') o = Order.objects.get(pk=oid) cp = o.positions.get(addon_to__isnull=True) assert cp.item == self.ticket assert cp.price == 20 - 1.5 a = cp.addons.get() assert a.item == self.trans assert a.price == 1.5 @classscope(attr='orga') def test_bundle_position_free_price_lower_than_designated_price(self): self.ticket.free_price = True self.ticket.default_price = 1 self.ticket.save() self.cp1.price = 0 self.cp1.save() oid = _perform_order(self.event, 'manual', [self.cp1.pk, self.bundled1.pk], 'admin@example.org', 'en', None, {}, 'web') o = Order.objects.get(pk=oid) cp = o.positions.get(addon_to__isnull=True) assert cp.item == self.ticket assert cp.price == Decimal('0.00') a = cp.addons.get() assert a.item == self.trans assert a.price == Decimal('1.50') @classscope(attr='orga') def test_bundle_different_tax_rates(self): tr19 = self.event.tax_rules.create( name='VAT', rate=Decimal('19.00') ) tr7 = self.event.tax_rules.create( name='VAT', rate=Decimal('7.00'), price_includes_tax=True, # will be ignored ) self.ticket.tax_rule = tr19 self.ticket.save() self.trans.tax_rule = tr7 self.trans.save() oid = _perform_order(self.event, 'manual', [self.cp1.pk, self.bundled1.pk], 'admin@example.org', 'en', None, {}, 'web') o = Order.objects.get(pk=oid) cp = o.positions.get(addon_to__isnull=True) assert cp.item == self.ticket assert cp.price == Decimal('21.50') assert cp.tax_rate == Decimal('19.00') assert cp.tax_value == Decimal('3.43') assert cp.addons.count() == 1 a = cp.addons.first() assert a.item == self.trans assert a.price == 1.5 assert a.tax_rate == Decimal('7.00') assert a.tax_value == Decimal('0.10') @classscope(attr='orga') def test_simple_bundle_with_voucher(self): v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event, price_mode='none', valid_until=now() + timedelta(days=2)) self.cp1.voucher = v self.cp1.save() oid = _perform_order(self.event, 'manual', [self.cp1.pk, self.bundled1.pk], 'admin@example.org', 'en', None, {}, 'web') o = Order.objects.get(pk=oid) cp = o.positions.get(addon_to__isnull=True) assert cp.item == self.ticket assert cp.price == 23 - 1.5 assert cp.addons.count() == 1 a = cp.addons.get() assert a.item == self.trans assert a.price == 1.5 @classscope(attr='orga') def test_expired_keep_price(self): self.cp1.expires = now() - timedelta(minutes=10) self.cp1.save() self.bundled1.expires = now() - timedelta(minutes=10) self.bundled1.save() oid = _perform_order(self.event, 'manual', [self.cp1.pk, self.bundled1.pk], 'admin@example.org', 'en', None, {}, 'web') o = Order.objects.get(pk=oid) cp = o.positions.get(addon_to__isnull=True) b = cp.addons.first() assert cp.price == 21.5 assert b.price == 1.5 @classscope(attr='orga') def test_expired_designated_price_changed(self): self.bundle1.designated_price = Decimal('2.00') self.bundle1.save() self.cp1.expires = now() - timedelta(minutes=10) self.cp1.save() self.bundled1.expires = now() - timedelta(minutes=10) self.bundled1.save() with self.assertRaises(OrderError): _perform_order(self.event, 'manual', [self.cp1.pk, self.bundled1.pk], 'admin@example.org', 'en', None, {}, 'web') self.cp1.refresh_from_db() self.bundled1.refresh_from_db() assert self.cp1.price == 21 assert self.bundled1.price == 2 @classscope(attr='orga') def test_expired_designated_price_changed_beyond_base_price(self): self.bundle1.designated_price = Decimal('40.00') self.bundle1.save() self.cp1.expires = now() - timedelta(minutes=10) self.cp1.save() self.bundled1.expires = now() - timedelta(minutes=10) self.bundled1.save() with self.assertRaises(OrderError): _perform_order(self.event, 'manual', [self.cp1.pk, self.bundled1.pk], 'admin@example.org', 'en', None, {}, 'web') self.cp1.refresh_from_db() self.bundled1.refresh_from_db() assert self.cp1.price == 0 assert self.bundled1.price == 40 @classscope(attr='orga') def test_expired_base_price_changed(self): self.ticket.default_price = Decimal('25.00') self.ticket.save() self.cp1.expires = now() - timedelta(minutes=10) self.cp1.save() self.bundled1.expires = now() - timedelta(minutes=10) self.bundled1.save() with self.assertRaises(OrderError): _perform_order(self.event, 'manual', [self.cp1.pk, self.bundled1.pk], 'admin@example.org', 'en', None, {}, 'web') self.cp1.refresh_from_db() self.bundled1.refresh_from_db() assert self.cp1.price == 23.5 assert self.bundled1.price == 1.5 @classscope(attr='orga') def test_expired_bundled_and_addon(self): a = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.trans, addon_to=self.cp1, price=2.5, expires=now() - timedelta(minutes=10), is_bundled=False ) self.cp1.expires = now() - timedelta(minutes=10) self.cp1.includes_tax = False self.cp1.save() self.bundled1.expires = now() - timedelta(minutes=10) self.bundled1.includes_tax = False self.bundled1.save() oid = _perform_order(self.event, 'manual', [self.cp1.pk, self.bundled1.pk, a.pk], 'admin@example.org', 'en', None, {}, 'web') o = Order.objects.get(pk=oid) cp = o.positions.get(addon_to__isnull=True) b = cp.addons.order_by('price').first() a = cp.addons.order_by('price').last() assert cp.price == 21.5 assert b.price == 1.5 assert cp.price == 21.5 assert b.price == 1.5 assert a.price == 2.5 @classscope(attr='orga') def test_expired_base_product_sold_out(self): self.quota_tickets.size = 0 self.quota_tickets.save() self.cp1.expires = now() - timedelta(minutes=10) self.cp1.save() self.bundled1.expires = now() - timedelta(minutes=10) self.bundled1.save() with self.assertRaises(OrderError): _perform_order(self.event, 'manual', [self.cp1.pk, self.bundled1.pk], 'admin@example.org', 'en', None, {}, 'web') assert not CartPosition.objects.exists() @classscope(attr='orga') def test_expired_bundled_product_sold_out(self): self.transquota.size = 0 self.transquota.save() self.cp1.expires = now() - timedelta(minutes=10) self.cp1.save() self.bundled1.expires = now() - timedelta(minutes=10) self.bundled1.save() with self.assertRaises(OrderError): _perform_order(self.event, 'manual', [self.cp1.pk, self.bundled1.pk], 'admin@example.org', 'en', None, {}, 'web') assert not CartPosition.objects.exists() @classscope(attr='orga') def test_expired_bundled_products_sold_out_partially(self): self.transquota.size = 1 self.transquota.save() a = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.trans, addon_to=self.cp1, price=1.5, expires=now() - timedelta(minutes=10), is_bundled=True ) self.cp1.price -= 1.5 self.cp1.expires = now() - timedelta(minutes=10) self.cp1.save() self.bundled1.expires = now() - timedelta(minutes=10) self.bundled1.save() with self.assertRaises(OrderError): _perform_order(self.event, 'manual', [self.cp1.pk, self.bundled1.pk, a.pk], 'admin@example.org', 'en', None, {}, 'web') assert not CartPosition.objects.exists() @classscope(attr='orga') def test_expired_reverse_charge_only_bundled(self): tr19 = self.event.tax_rules.create(name='VAT', rate=Decimal('19.00')) ia = InvoiceAddress.objects.create( is_business=True, vat_id='ATU1234567', vat_id_validated=True, country=Country('AT') ) tr7 = self.event.tax_rules.create(name='VAT', rate=Decimal('7.00'), eu_reverse_charge=True, home_country=Country('DE')) self.ticket.tax_rule = tr19 self.ticket.save() self.trans.tax_rule = tr7 self.trans.save() self.cp1.expires = now() - timedelta(minutes=10) self.cp1.save() self.bundled1.expires = now() - timedelta(minutes=10) self.bundled1.price = Decimal('1.40') self.bundled1.includes_tax = False self.bundled1.save() oid = _perform_order(self.event, 'manual', [self.cp1.pk, self.bundled1.pk], 'admin@example.org', 'en', ia.pk, {}, 'web') o = Order.objects.get(pk=oid) cp = o.positions.get(addon_to__isnull=True) assert cp.item == self.ticket assert cp.price == Decimal('21.50') assert cp.tax_rate == Decimal('19.00') assert cp.tax_value == Decimal('3.43') assert cp.addons.count() == 1 a = cp.addons.first() assert a.item == self.trans assert a.price == Decimal('1.40') assert a.tax_rate == Decimal('0.00') assert a.tax_value == Decimal('0.00') @classscope(attr='orga') def test_expired_reverse_charge_all(self): ia = InvoiceAddress.objects.create( is_business=True, vat_id='ATU1234567', vat_id_validated=True, country=Country('AT') ) tr19 = self.event.tax_rules.create(name='VAT', rate=Decimal('19.00'), eu_reverse_charge=True, home_country=Country('DE')) tr7 = self.event.tax_rules.create(name='VAT', rate=Decimal('7.00'), eu_reverse_charge=True, home_country=Country('DE')) self.ticket.tax_rule = tr19 self.ticket.save() self.trans.tax_rule = tr7 self.trans.save() self.cp1.expires = now() - timedelta(minutes=10) self.cp1.price = Decimal('18.07') self.cp1.includes_tax = False self.cp1.save() self.bundled1.expires = now() - timedelta(minutes=10) self.bundled1.price = Decimal('1.40') self.bundled1.includes_tax = False self.bundled1.save() oid = _perform_order(self.event, 'manual', [self.cp1.pk, self.bundled1.pk], 'admin@example.org', 'en', ia.pk, {}, 'web') o = Order.objects.get(pk=oid) cp = o.positions.get(addon_to__isnull=True) assert cp.item == self.ticket assert cp.price == Decimal('18.07') assert cp.tax_rate == Decimal('0.00') assert cp.tax_value == Decimal('0.00') assert cp.addons.count() == 1 a = cp.addons.first() assert a.item == self.trans assert a.price == Decimal('1.40') assert a.tax_rate == Decimal('0.00') assert a.tax_value == Decimal('0.00') @classscope(attr='orga') def test_addon_and_bundle_through_frontend_stack(self): cat = self.event.categories.create(name="addons") self.trans.category = cat self.trans.save() ItemAddOn.objects.create(base_item=self.ticket, addon_category=cat, min_count=1, price_included=True) CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.trans, addon_to=self.cp1, price=0, expires=now() + timedelta(minutes=10), is_bundled=False ) self._set_session('payment', 'banktransfer') response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.rendered_content, "lxml") self.assertEqual(len(doc.select(".thank-you")), 1) class CheckoutSeatingTest(BaseCheckoutTestCase, TestCase): @scopes_disabled() def setUp(self): super().setUp() self.plan = SeatingPlan.objects.create( name="Plan", organizer=self.orga, layout="{}" ) self.event.seat_category_mappings.create( layout_category='Stalls', product=self.ticket ) self.seat_a1 = self.event.seats.create(seat_number="A1", product=self.ticket, seat_guid="A1") self.seat_a2 = self.event.seats.create(seat_number="A2", product=self.ticket, seat_guid="A2") self.seat_a3 = self.event.seats.create(seat_number="A3", product=self.ticket, seat_guid="A3") self.cp1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=21.5, expires=now() + timedelta(minutes=10), seat=self.seat_a1 ) @scopes_disabled() def test_passes(self): oid = _perform_order(self.event, 'manual', [self.cp1.pk], 'admin@example.org', 'en', None, {}, 'web') o = Order.objects.get(pk=oid) op = o.positions.first() assert op.item == self.ticket assert op.seat == self.seat_a1 @scopes_disabled() def test_seat_required(self): self.cp1.seat = None self.cp1.save() with self.assertRaises(OrderError): _perform_order(self.event, 'manual', [self.cp1.pk], 'admin@example.org', 'en', None, {}, 'web') assert not CartPosition.objects.filter(pk=self.cp1.pk).exists() @scopes_disabled() def test_seat_not_required_if_no_choice(self): self.cp1.seat = None self.cp1.save() self.event.settings.seating_choice = False _perform_order(self.event, 'manual', [self.cp1.pk], 'admin@example.org', 'en', None, {}, 'web') @scopes_disabled() def test_seat_not_allowed(self): self.cp1.item = self.workshop1 self.cp1.save() with self.assertRaises(OrderError): _perform_order(self.event, 'manual', [self.cp1.pk], 'admin@example.org', 'en', None, {}, 'web') assert not CartPosition.objects.filter(pk=self.cp1.pk).exists() @scopes_disabled() def test_seat_invalid_product(self): self.cp1.item = self.workshop1 self.cp1.save() self.event.seat_category_mappings.create( layout_category='Foo', product=self.workshop1 ) with self.assertRaises(OrderError): _perform_order(self.event, 'manual', [self.cp1.pk], 'admin@example.org', 'en', None, {}, 'web') assert not CartPosition.objects.filter(pk=self.cp1.pk).exists() @scopes_disabled() def test_seat_multiple_times_same_seat(self): cp2 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price=21.5, expires=now() + timedelta(minutes=10), seat=self.seat_a1 ) with self.assertRaises(OrderError): _perform_order(self.event, 'manual', [self.cp1.pk, cp2.pk], 'admin@example.org', 'en', None, {}, 'web') assert not CartPosition.objects.filter(pk=self.cp1.pk).exists() assert not CartPosition.objects.filter(pk=cp2.pk).exists() @scopes_disabled() def test_seat_blocked(self): self.seat_a1.blocked = True self.seat_a1.save() with self.assertRaises(OrderError): _perform_order(self.event, 'manual', [self.cp1.pk], 'admin@example.org', 'en', None, {}, 'web') assert not CartPosition.objects.filter(pk=self.cp1.pk).exists() @scopes_disabled() def test_seat_taken(self): CartPosition.objects.create( event=self.event, cart_id=self.session_key + '_other', item=self.ticket, price=21.5, expires=now() + timedelta(minutes=10), seat=self.seat_a1 ) with self.assertRaises(OrderError): _perform_order(self.event, 'manual', [self.cp1.pk], 'admin@example.org', 'en', None, {}, 'web') assert not CartPosition.objects.filter(pk=self.cp1.pk).exists() class CheckoutVoucherBudgetTest(BaseCheckoutTestCase, TestCase): @scopes_disabled() def setUp(self): super().setUp() self.v = Voucher.objects.create(item=self.ticket, value=Decimal('21.50'), event=self.event, price_mode='set', valid_until=now() + timedelta(days=2), max_usages=999, redeemed=0) self.cp1 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price_before_voucher=23, price=21.5, expires=now() + timedelta(minutes=10), voucher=self.v ) self.cp2 = CartPosition.objects.create( event=self.event, cart_id=self.session_key, item=self.ticket, price_before_voucher=23, price=21.5, expires=now() + timedelta(minutes=10), voucher=self.v ) @scopes_disabled() def test_no_budget(self): oid = _perform_order(self.event, 'manual', [self.cp1.pk, self.cp2.pk], 'admin@example.org', 'en', None, {}, 'web') o = Order.objects.get(pk=oid) op = o.positions.first() assert op.item == self.ticket assert op.price_before_voucher == Decimal('23.00') @scopes_disabled() def test_budget_exceeded_for_second_order(self): self.v.budget = Decimal('1.50') self.v.save() oid = _perform_order(self.event, 'manual', [self.cp1.pk], 'admin@example.org', 'en', None, {}, 'web') o = Order.objects.get(pk=oid) op = o.positions.first() assert op.item == self.ticket with self.assertRaises(OrderError): _perform_order(self.event, 'manual', [self.cp2.pk], 'admin@example.org', 'en', None, {}, 'web') self.cp2.refresh_from_db() assert self.cp2.price == Decimal('23.00') @scopes_disabled() def test_budget_exceeded_between_positions(self): self.v.budget = Decimal('1.50') self.v.save() with self.assertRaises(OrderError): _perform_order(self.event, 'manual', [self.cp1.pk, self.cp2.pk], 'admin@example.org', 'en', None, {}, 'web') self.cp1.refresh_from_db() assert self.cp1.price == Decimal('21.50') self.cp2.refresh_from_db() assert self.cp2.price == Decimal('23.00') @scopes_disabled() def test_budget_exceeded_in_first_position(self): self.v.budget = Decimal('1.00') self.v.save() with self.assertRaises(OrderError): _perform_order(self.event, 'manual', [self.cp1.pk, self.cp2.pk], 'admin@example.org', 'en', None, {}, 'web') self.cp1.refresh_from_db() assert self.cp1.price == Decimal('22.00') self.cp2.refresh_from_db() assert self.cp2.price == Decimal('23.00') @scopes_disabled() def test_budget_exceeded_in_second_position(self): self.v.budget = Decimal('2.50') self.v.save() with self.assertRaises(OrderError): _perform_order(self.event, 'manual', [self.cp1.pk, self.cp2.pk], 'admin@example.org', 'en', None, {}, 'web') self.cp1.refresh_from_db() assert self.cp1.price == Decimal('21.50') self.cp2.refresh_from_db() assert self.cp2.price == Decimal('22.00') @scopes_disabled() def test_budget_exceeded_during_price_change(self): self.v.budget = Decimal('2.50') self.v.value = Decimal('21.00') self.v.save() self.cp1.expires = now() - timedelta(hours=1) self.cp1.save() self.cp2.expires = now() - timedelta(hours=1) self.cp2.save() with self.assertRaises(OrderError): _perform_order(self.event, 'manual', [self.cp1.pk, self.cp2.pk], 'admin@example.org', 'en', None, {}, 'web') self.cp1.refresh_from_db() assert self.cp1.price == Decimal('21.00') self.cp2.refresh_from_db() assert self.cp2.price == Decimal('22.50') @scopes_disabled() def test_budget_exceeded_expired_cart(self): self.v.budget = Decimal('0.00') self.v.value = Decimal('21.00') self.v.save() self.cp1.expires = now() - timedelta(hours=1) self.cp1.save() self.cp2.expires = now() - timedelta(hours=1) self.cp2.save() with self.assertRaises(OrderError): _perform_order(self.event, 'manual', [self.cp1.pk, self.cp2.pk], 'admin@example.org', 'en', None, {}, 'web') self.cp1.refresh_from_db() assert self.cp1.price == Decimal('23.00') self.cp2.refresh_from_db() assert self.cp2.price == Decimal('23.00') @scopes_disabled() def test_budget_overbooked_expired_cart(self): self.v.budget = Decimal('1.50') self.v.value = Decimal('21.50') self.v.save() self.cp1.expires = now() - timedelta(hours=1) self.cp1.save() self.cp2.expires = now() - timedelta(hours=1) self.cp2.save() oid = _perform_order(self.event, 'manual', [self.cp1.pk], 'admin@example.org', 'en', None, {}, 'web') o = Order.objects.get(pk=oid) op = o.positions.first() assert op.item == self.ticket self.v.budget = Decimal('1.00') self.v.save() with self.assertRaises(OrderError): _perform_order(self.event, 'manual', [self.cp2.pk], 'admin@example.org', 'en', None, {}, 'web') self.cp2.refresh_from_db() assert self.cp2.price == Decimal('23.00')