mirror of
https://github.com/pretix/pretix.git
synced 2026-08-07 10:17:49 +00:00
* Add option to restrict anonymous access to order URLs By default, users who place orders while logged in can still access their order URLs without authentication. This raises potential security risks, particularly if order confirmation emails are forwarded. This commit introduces an organiser-level setting to disable anonymous access for such orders. When enabled, unauthenticated attempts to access URLs starting with `/order/`, which are intended for the customer, are redirected to the login page. Upon successful authentication, the user is redirected back to the original order URL. It is important to note that this change does not impact routes intended for attendees (e.g., `/ticket/*`), which remain accessible without authentication. * Change name of setting for future clarity Co-authored-by: Raphael Michel <mail@raphaelmichel.de> * Update message wording Co-authored-by: Raphael Michel <mail@raphaelmichel.de> * Eliminate database query Co-authored-by: Raphael Michel <mail@raphaelmichel.de> * Rename feature flag to fix breaking tests * Refactor order access verification code into `OrderDetailsMixin` * Add test for logged-in customer accessing another customer's order * Refactor order access conditions to remove nesting * Handle case where customer is not yet verified * Add additional information to help message * Fix multidomain issue Co-authored-by: Raphael Michel <mail@raphaelmichel.de> * Merge order/position variants into single tests * Add docstring explaining return type of `order` property * Apply suggestion from @raphaelm * Fix indentation --------- Co-authored-by: Raphael Michel <mail@raphaelmichel.de> Co-authored-by: Raphael Michel <michel@rami.io>
1960 lines
92 KiB
Python
1960 lines
92 KiB
Python
#
|
|
# This file is part of pretix (Community Edition).
|
|
#
|
|
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
|
# Copyright (C) 2020-today pretix GmbH and contributors
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
|
# Public License as published by the Free Software Foundation in version 3 of the License.
|
|
#
|
|
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
|
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
|
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
|
# this file, see <https://pretix.eu/about/en/license>.
|
|
#
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
|
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
|
# <https://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
# This file is based on an earlier version of pretix which was released under the Apache License 2.0. The full text of
|
|
# the Apache License 2.0 can be obtained at <http://www.apache.org/licenses/LICENSE-2.0>.
|
|
#
|
|
# This file may have since been changed and any changes are released under the terms of AGPLv3 as described above. A
|
|
# full history of changes and contributors is available at <https://github.com/pretix/pretix>.
|
|
#
|
|
# This file contains Apache-licensed contributions copyrighted by: Flavia Bastos
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software distributed under the Apache License 2.0 is
|
|
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations under the License.
|
|
|
|
import datetime
|
|
import re
|
|
from decimal import Decimal
|
|
|
|
from bs4 import BeautifulSoup
|
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
|
from django.test import TestCase
|
|
from django.utils.timezone import now
|
|
from django_scopes import scopes_disabled
|
|
|
|
from pretix.base.models import (
|
|
Event, Item, ItemCategory, ItemVariation, Order, OrderPosition, Organizer,
|
|
Question, Quota,
|
|
)
|
|
from pretix.base.models.orders import OrderFee, OrderPayment
|
|
from pretix.base.reldate import RelativeDate, RelativeDateWrapper
|
|
from pretix.base.services.invoices import generate_invoice
|
|
|
|
|
|
class BaseOrdersTest(TestCase):
|
|
|
|
@scopes_disabled()
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.orga = Organizer.objects.create(name='CCC', slug='ccc', plugins='pretix.plugins.banktransfer')
|
|
self.event = Event.objects.create(
|
|
organizer=self.orga, name='30C3', slug='30c3',
|
|
date_from=datetime.datetime(2013, 12, 26, tzinfo=datetime.timezone.utc),
|
|
plugins='pretix.plugins.stripe,pretix.plugins.banktransfer,tests.testdummy',
|
|
live=True
|
|
)
|
|
self.event.settings.set('payment_banktransfer__enabled', True)
|
|
self.event.settings.set('ticketoutput_testdummy__enabled', True)
|
|
|
|
self.category = ItemCategory.objects.create(event=self.event, name="Everything", position=0)
|
|
self.quota_shirts = Quota.objects.create(event=self.event, name='Shirts', size=2)
|
|
self.shirt = Item.objects.create(event=self.event, name='T-Shirt', category=self.category, default_price=12)
|
|
self.quota_shirts.items.add(self.shirt)
|
|
self.shirt_red = ItemVariation.objects.create(item=self.shirt, default_price=14, value="Red")
|
|
self.shirt_blue = ItemVariation.objects.create(item=self.shirt, value="Blue")
|
|
self.quota_shirts.variations.add(self.shirt_red)
|
|
self.quota_shirts.variations.add(self.shirt_blue)
|
|
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, personalized=True)
|
|
self.quota_tickets.items.add(self.ticket)
|
|
self.event.settings.set('attendee_names_asked', True)
|
|
self.question = Question.objects.create(question='Foo', type=Question.TYPE_STRING, event=self.event,
|
|
required=False)
|
|
self.ticket.questions.add(self.question)
|
|
|
|
self.order = Order.objects.create(
|
|
status=Order.STATUS_PENDING,
|
|
event=self.event,
|
|
email='admin@localhost',
|
|
datetime=now() - datetime.timedelta(days=3),
|
|
expires=now() + datetime.timedelta(days=11),
|
|
total=Decimal("23"),
|
|
sales_channel=self.orga.sales_channels.get(identifier="web"),
|
|
locale='en'
|
|
)
|
|
self.ticket_pos = OrderPosition.objects.create(
|
|
order=self.order,
|
|
item=self.ticket,
|
|
variation=None,
|
|
price=Decimal("23"),
|
|
attendee_name_parts={'full_name': "Peter"}
|
|
)
|
|
self.deleted_pos = OrderPosition.objects.create(
|
|
order=self.order,
|
|
item=self.ticket,
|
|
variation=None,
|
|
price=Decimal("23"),
|
|
attendee_name_parts={'full_name': "Lukas"},
|
|
canceled=True
|
|
)
|
|
self.not_my_order = Order.objects.create(
|
|
status=Order.STATUS_PENDING,
|
|
event=self.event,
|
|
email='user@localhost',
|
|
datetime=now() - datetime.timedelta(days=3),
|
|
expires=now() + datetime.timedelta(days=11),
|
|
total=Decimal("23"),
|
|
sales_channel=self.orga.sales_channels.get(identifier="web"),
|
|
)
|
|
|
|
|
|
class OrdersTest(BaseOrdersTest):
|
|
def test_unknown_order(self):
|
|
response = self.client.get(
|
|
'/%s/%s/order/ABCDE/123/' % (self.orga.slug, self.event.slug)
|
|
)
|
|
assert response.status_code == 404
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/123/' % (self.orga.slug, self.event.slug, self.not_my_order.code)
|
|
)
|
|
assert response.status_code == 404
|
|
response = self.client.get(
|
|
'/%s/%s/order/ABCDE/123/pay' % (self.orga.slug, self.event.slug)
|
|
)
|
|
assert response.status_code == 404
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/123/pay' % (self.orga.slug, self.event.slug, self.not_my_order.code)
|
|
)
|
|
assert response.status_code == 404
|
|
response = self.client.get(
|
|
'/%s/%s/order/ABCDE/123/pay/confirm' % (self.orga.slug, self.event.slug)
|
|
)
|
|
assert response.status_code == 404
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/123/pay/confirm' % (self.orga.slug, self.event.slug, self.not_my_order.code)
|
|
)
|
|
assert response.status_code == 404
|
|
response = self.client.get(
|
|
'/%s/%s/order/ABCDE/123/modify' % (self.orga.slug, self.event.slug)
|
|
)
|
|
assert response.status_code == 404
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/123/modify' % (self.orga.slug, self.event.slug, self.not_my_order.code)
|
|
)
|
|
assert response.status_code == 404
|
|
response = self.client.get(
|
|
'/%s/%s/order/ABCDE/123/cancel' % (self.orga.slug, self.event.slug)
|
|
)
|
|
assert response.status_code == 404
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/123/cancel' % (self.orga.slug, self.event.slug, self.not_my_order.code)
|
|
)
|
|
assert response.status_code == 404
|
|
response = self.client.post(
|
|
'/%s/%s/order/ABCDE/123/cancel/do' % (self.orga.slug, self.event.slug)
|
|
)
|
|
assert response.status_code == 404
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/123/cancel/do' % (self.orga.slug, self.event.slug, self.not_my_order.code)
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
def test_unknown_position(self):
|
|
response = self.client.get(
|
|
'/%s/%s/ticket/ABCDE/1/123/' % (self.orga.slug, self.event.slug)
|
|
)
|
|
assert response.status_code == 404
|
|
response = self.client.get(
|
|
'/%s/%s/ticket/%s/1/123/' % (self.orga.slug, self.event.slug, self.order.code)
|
|
)
|
|
assert response.status_code == 404
|
|
response = self.client.get(
|
|
'/%s/%s/ticket/%s/1/%s/' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 404
|
|
response = self.client.get(
|
|
'/%s/%s/ticket/%s/1/123/' % (self.orga.slug, self.event.slug, self.not_my_order.code)
|
|
)
|
|
assert response.status_code == 404
|
|
response = self.client.get(
|
|
'/%s/%s/ticket/%s/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.deleted_pos.positionid, self.deleted_pos.web_secret)
|
|
)
|
|
assert response.status_code == 404
|
|
response = self.client.get(
|
|
'/%s/%s/ticket/%s/1/123/modify' % (self.orga.slug, self.event.slug, self.not_my_order.code)
|
|
)
|
|
assert response.status_code == 404
|
|
response = self.client.get(
|
|
'/%s/%s/ticket/%s/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.deleted_pos.positionid, self.deleted_pos.web_secret)
|
|
)
|
|
assert response.status_code == 404
|
|
response = self.client.get(
|
|
'/%s/%s/ticket/%s/1/123/change' % (self.orga.slug, self.event.slug, self.not_my_order.code)
|
|
)
|
|
assert response.status_code == 404
|
|
response = self.client.get(
|
|
'/%s/%s/ticket/%s/%s/%s/change' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.deleted_pos.positionid, self.deleted_pos.web_secret)
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
def test_orders_confirm_email(self):
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/open/%s/' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret, 'aabbccdd')
|
|
)
|
|
assert response.status_code == 302
|
|
self.order.refresh_from_db()
|
|
assert not self.order.email_known_to_work
|
|
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/open/%s/' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret, self.order.email_confirm_secret())
|
|
)
|
|
assert response.status_code == 302
|
|
self.order.refresh_from_db()
|
|
assert self.order.email_known_to_work
|
|
|
|
def test_orders_detail(self):
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
assert len(doc.select(".cart-row")) > 0
|
|
assert "pending" in doc.select(".order-details")[0].text.lower()
|
|
assert "Peter" in response.content.decode()
|
|
assert "Lukas" not in response.content.decode()
|
|
|
|
def test_ticket_detail(self):
|
|
response = self.client.get(
|
|
'/%s/%s/ticket/%s/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.ticket_pos.positionid, self.ticket_pos.web_secret)
|
|
)
|
|
assert response.status_code == 200
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
assert len(doc.select(".cart-row")) > 0
|
|
assert "pending" in doc.select(".order-details")[0].text.lower()
|
|
assert "Peter" in response.content.decode()
|
|
assert "Lukas" not in response.content.decode()
|
|
|
|
def test_orders_modify_invalid(self):
|
|
self.order.status = Order.STATUS_CANCELED
|
|
self.order.save()
|
|
self.client.get(
|
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
with scopes_disabled():
|
|
self.order = Order.objects.get(id=self.order.id)
|
|
assert self.order.status == Order.STATUS_CANCELED
|
|
|
|
def test_orders_modify_attendee_optional(self):
|
|
self.event.settings.set('attendee_names_asked', True)
|
|
self.event.settings.set('attendee_names_required', False)
|
|
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret))
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
self.assertEqual(len(doc.select('input[name="%s-attendee_name_parts_0"]' % self.ticket_pos.id)), 1)
|
|
|
|
# Not all fields filled out, expect success
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
'%s-attendee_name_parts_0' % self.ticket_pos.id: '',
|
|
'transmission_type': 'email',
|
|
}, follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
with scopes_disabled():
|
|
self.ticket_pos = OrderPosition.objects.get(id=self.ticket_pos.id)
|
|
assert self.ticket_pos.attendee_name in (None, '')
|
|
|
|
def test_orders_modify_attendee_required(self):
|
|
self.event.settings.set('attendee_names_asked', True)
|
|
self.event.settings.set('attendee_names_required', True)
|
|
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret))
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
self.assertEqual(len(doc.select('input[name="%s-attendee_name_parts_0"]' % self.ticket_pos.id)), 1)
|
|
assert "Peter" in response.content.decode()
|
|
assert "Lukas" not in response.content.decode()
|
|
|
|
# Not all required fields filled out, expect failure
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
'%s-attendee_name_parts_0' % self.ticket_pos.id: '',
|
|
}, follow=True)
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
self.assertGreaterEqual(len(doc.select('.has-error')), 1)
|
|
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
'%s-attendee_name_parts_0' % self.ticket_pos.id: 'Peter',
|
|
'transmission_type': 'email',
|
|
}, follow=True)
|
|
self.assertRedirects(response, '/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
with scopes_disabled():
|
|
self.ticket_pos = OrderPosition.objects.get(id=self.ticket_pos.id)
|
|
assert self.ticket_pos.attendee_name == 'Peter'
|
|
|
|
def test_orders_questions_optional(self):
|
|
self.event.settings.set('attendee_names_asked', False)
|
|
self.event.settings.set('attendee_names_required', False)
|
|
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret))
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
self.assertEqual(len(doc.select('input[name="%s-question_%s"]' % (
|
|
self.ticket_pos.id, self.question.id))), 1)
|
|
|
|
# Not all fields filled out, expect success
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
'%s-question_%s' % (self.ticket_pos.id, self.question.id): '',
|
|
'transmission_type': 'email',
|
|
}, follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
with scopes_disabled():
|
|
assert not self.ticket_pos.answers.filter(question=self.question).exists()
|
|
|
|
def test_orders_questions_required(self):
|
|
self.event.settings.set('attendee_names_asked', False)
|
|
self.event.settings.set('attendee_names_required', False)
|
|
self.question.required = True
|
|
self.question.save()
|
|
|
|
response = self.client.get('/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret))
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
self.assertEqual(len(doc.select('input[name="%s-question_%s"]' % (
|
|
self.ticket_pos.id, self.question.id))), 1)
|
|
|
|
# Not all required fields filled out, expect failure
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
'%s-question_%s' % (self.ticket_pos.id, self.question.id): '',
|
|
'transmission_type': 'email',
|
|
}, follow=True)
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
self.assertGreaterEqual(len(doc.select('.has-error')), 1)
|
|
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
'%s-question_%s' % (self.ticket_pos.id, self.question.id): 'ABC',
|
|
'transmission_type': 'email',
|
|
}, follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
with scopes_disabled():
|
|
assert self.ticket_pos.answers.get(question=self.question).answer == 'ABC'
|
|
|
|
def test_modify_invoice_address_validated(self):
|
|
self.event.settings.set('invoice_reissue_after_modify', True)
|
|
self.event.settings.set('invoice_address_asked', True)
|
|
with scopes_disabled():
|
|
generate_invoice(self.order)
|
|
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
'%s-question_%s' % (self.ticket_pos.id, self.question.id): 'ABC',
|
|
'transmission_type': 'email',
|
|
}, follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
# Only questions changed
|
|
with scopes_disabled():
|
|
assert self.order.invoices.count() == 1
|
|
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
'%s-question_%s' % (self.ticket_pos.id, self.question.id): 'ABC',
|
|
'zipcode': 'XXINVALIDXX',
|
|
'street': 'Main Street',
|
|
'city': 'Heidelberg',
|
|
'country': 'DE',
|
|
'transmission_type': 'email',
|
|
}, follow=True)
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
self.assertGreaterEqual(len(doc.select('.has-error')), 1)
|
|
|
|
def test_modify_invoice_regenerate(self):
|
|
self.event.settings.set('invoice_reissue_after_modify', True)
|
|
self.event.settings.set('invoice_address_asked', True)
|
|
with scopes_disabled():
|
|
generate_invoice(self.order)
|
|
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
'%s-question_%s' % (self.ticket_pos.id, self.question.id): 'ABC',
|
|
'transmission_type': 'email',
|
|
}, follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
# Only questions changed
|
|
with scopes_disabled():
|
|
assert self.order.invoices.count() == 1
|
|
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
'%s-question_%s' % (self.ticket_pos.id, self.question.id): 'ABC',
|
|
'zipcode': '12345',
|
|
'street': 'Main Street',
|
|
'city': 'Heidelberg',
|
|
'country': 'DE',
|
|
'transmission_type': 'email',
|
|
}, follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
with scopes_disabled():
|
|
assert self.order.invoices.count() == 3
|
|
|
|
self.event.settings.set('invoice_reissue_after_modify', False)
|
|
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
'%s-question_%s' % (self.ticket_pos.id, self.question.id): 'ABC',
|
|
'zipcode': '54321',
|
|
'street': 'Main Street',
|
|
'city': 'Heidelberg',
|
|
'country': 'DE',
|
|
'transmission_type': 'email',
|
|
}, follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
with scopes_disabled():
|
|
assert self.order.invoices.count() == 3
|
|
|
|
def test_orders_attendee_modify_invalid(self):
|
|
self.order.status = Order.STATUS_CANCELED
|
|
self.order.save()
|
|
self.event.settings.set('allow_modifications', 'attendee')
|
|
r = self.client.get(
|
|
'/%s/%s/ticket/%s/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.ticket_pos.positionid, self.ticket_pos.web_secret)
|
|
)
|
|
assert r.status_code == 302
|
|
|
|
def test_orders_attendee_modify_forbidden(self):
|
|
self.event.settings.set('allow_modifications', 'order')
|
|
r = self.client.get(
|
|
'/%s/%s/ticket/%s/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.ticket_pos.positionid, self.ticket_pos.web_secret)
|
|
)
|
|
assert r.status_code == 302
|
|
|
|
def test_orders_attendee_modify_attendee_optional(self):
|
|
self.event.settings.set('allow_modifications', 'attendee')
|
|
self.event.settings.set('attendee_names_asked', True)
|
|
self.event.settings.set('attendee_names_required', False)
|
|
|
|
response = self.client.get(
|
|
'/%s/%s/ticket/%s/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.ticket_pos.positionid, self.ticket_pos.web_secret)
|
|
)
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
self.assertEqual(len(doc.select('input[name="%s-attendee_name_parts_0"]' % self.ticket_pos.id)), 1)
|
|
|
|
# Not all fields filled out, expect success
|
|
response = self.client.post(
|
|
'/%s/%s/ticket/%s/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.ticket_pos.positionid, self.ticket_pos.web_secret),
|
|
{
|
|
'transmission_type': 'email',
|
|
}
|
|
)
|
|
print(response.content.decode())
|
|
self.assertRedirects(response,
|
|
'/%s/%s/ticket/%s/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.ticket_pos.positionid, self.ticket_pos.web_secret),
|
|
target_status_code=200)
|
|
with scopes_disabled():
|
|
self.ticket_pos = OrderPosition.objects.get(id=self.ticket_pos.id)
|
|
assert self.ticket_pos.attendee_name in (None, '')
|
|
|
|
def test_orders_cancel_invalid(self):
|
|
self.order.status = Order.STATUS_PAID
|
|
self.order.save()
|
|
r = self.client.post(
|
|
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
}, follow=True)
|
|
assert 'btn-danger' not in r.content.decode()
|
|
self.client.post(
|
|
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
}, follow=True)
|
|
self.order.refresh_from_db()
|
|
assert self.order.status == Order.STATUS_PAID
|
|
|
|
def test_orders_cancel(self):
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
}, follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
self.order.refresh_from_db()
|
|
assert self.order.status == Order.STATUS_CANCELED
|
|
|
|
def test_orders_cancel_paid(self):
|
|
self.event.settings.cancel_allow_user_paid = True
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
}, follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
self.order.refresh_from_db()
|
|
assert self.order.status == Order.STATUS_CANCELED
|
|
|
|
def test_orders_cancel_paid_request(self):
|
|
self.order.status = Order.STATUS_PAID
|
|
self.order.save()
|
|
with scopes_disabled():
|
|
self.order.payments.create(provider='testdummy_partialrefund', amount=self.order.total, state=OrderPayment.PAYMENT_STATE_CONFIRMED)
|
|
self.event.settings.cancel_allow_user_paid = True
|
|
self.event.settings.cancel_allow_user_paid_keep = Decimal('3.00')
|
|
self.event.settings.cancel_allow_user_paid_require_approval = True
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
}, follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
self.order.refresh_from_db()
|
|
assert self.order.status == Order.STATUS_PAID
|
|
assert self.order.total == Decimal('23.00')
|
|
with scopes_disabled():
|
|
assert not self.order.refunds.exists()
|
|
r = self.order.cancellation_requests.get()
|
|
assert r.cancellation_fee == Decimal('3.00')
|
|
|
|
def test_orders_cancel_partially_paid_no_selfservice(self):
|
|
self.order.status = Order.STATUS_PENDING
|
|
self.order.save()
|
|
with scopes_disabled():
|
|
self.order.payments.create(provider='testdummy_partialrefund', amount=self.order.total - Decimal("1.00"),
|
|
state=OrderPayment.PAYMENT_STATE_CONFIRMED)
|
|
self.event.settings.cancel_allow_user_paid = True
|
|
self.event.settings.cancel_allow_user_paid_keep = Decimal('3.00')
|
|
self.event.settings.cancel_allow_user_paid_require_approval = True
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 302
|
|
|
|
def test_orders_cancel_free_no_request(self):
|
|
self.order.status = Order.STATUS_PAID
|
|
self.order.total = Decimal('0.00')
|
|
self.order.save()
|
|
with scopes_disabled():
|
|
self.order.payments.create(provider='testdummy_partialrefund', amount=self.order.total, state=OrderPayment.PAYMENT_STATE_CONFIRMED)
|
|
self.event.settings.cancel_allow_user_paid = True
|
|
self.event.settings.cancel_allow_user_paid_keep = Decimal('3.00')
|
|
self.event.settings.cancel_allow_user_paid_require_approval = True
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
}, follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
self.order.refresh_from_db()
|
|
assert self.order.status == Order.STATUS_CANCELED
|
|
with scopes_disabled():
|
|
assert not self.order.refunds.exists()
|
|
assert not self.order.cancellation_requests.exists()
|
|
|
|
def test_orders_cancel_free_ignore_fixed_fee(self):
|
|
self.order.status = Order.STATUS_PAID
|
|
self.order.total = Decimal('0.00')
|
|
self.order.save()
|
|
with scopes_disabled():
|
|
self.order.payments.create(provider='testdummy_partialrefund', amount=self.order.total, state=OrderPayment.PAYMENT_STATE_CONFIRMED)
|
|
self.event.settings.cancel_allow_user_paid = True
|
|
self.event.settings.cancel_allow_user_paid_keep = Decimal('3.00')
|
|
self.event.settings.cancel_allow_user_paid_require_approval = True
|
|
self.event.settings.cancel_allow_user_paid_adjust_fees = True
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
'cancel_fee': '3.00'
|
|
}, follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
self.order.refresh_from_db()
|
|
assert self.order.status == Order.STATUS_CANCELED
|
|
with scopes_disabled():
|
|
assert not self.order.refunds.exists()
|
|
assert not self.order.cancellation_requests.exists()
|
|
|
|
def test_orders_cancel_paid_fee_autorefund_gift_card_optional(self):
|
|
self.order.status = Order.STATUS_PAID
|
|
self.order.save()
|
|
with scopes_disabled():
|
|
self.order.payments.create(provider='testdummy_partialrefund', amount=self.order.total, state=OrderPayment.PAYMENT_STATE_CONFIRMED)
|
|
self.event.settings.cancel_allow_user_paid = True
|
|
self.event.settings.cancel_allow_user_paid_keep = Decimal('3.00')
|
|
self.event.settings.cancel_allow_user_paid_refund_as_giftcard = 'option'
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
assert 'manually' not in response.content.decode()
|
|
assert "gift card" in response.content.decode()
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
'giftcard': 'true'
|
|
}, follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
assert "gift card" in response.content.decode()
|
|
self.order.refresh_from_db()
|
|
assert self.order.status == Order.STATUS_PAID
|
|
assert self.order.total == Decimal('3.00')
|
|
with scopes_disabled():
|
|
r = self.order.refunds.get()
|
|
assert r.provider == "giftcard"
|
|
assert r.amount == Decimal('20.00')
|
|
|
|
def test_orders_cancel_paid_fee_autorefund_gift_card_force(self):
|
|
self.order.status = Order.STATUS_PAID
|
|
self.order.save()
|
|
with scopes_disabled():
|
|
self.order.payments.create(provider='testdummy_partialrefund', amount=self.order.total, state=OrderPayment.PAYMENT_STATE_CONFIRMED)
|
|
self.event.settings.cancel_allow_user_paid = True
|
|
self.event.settings.cancel_allow_user_paid_keep = Decimal('3.00')
|
|
self.event.settings.cancel_allow_user_paid_refund_as_giftcard = 'force'
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
assert 'manually' not in response.content.decode()
|
|
assert "gift card" in response.content.decode()
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
'payment_giftcard-code': 'false'
|
|
}, follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
assert "gift card" in response.content.decode()
|
|
self.order.refresh_from_db()
|
|
assert self.order.status == Order.STATUS_PAID
|
|
assert self.order.total == Decimal('3.00')
|
|
with scopes_disabled():
|
|
r = self.order.refunds.get()
|
|
assert r.provider == "giftcard"
|
|
assert r.amount == Decimal('20.00')
|
|
|
|
def test_orders_cancel_autorefund_gift_card_customer(self):
|
|
self.order.status = Order.STATUS_PAID
|
|
self.order.save()
|
|
with scopes_disabled():
|
|
customer = self.orga.customers.create(email='john@example.org', is_verified=True)
|
|
customer.set_password('foo')
|
|
customer.save()
|
|
self.order.customer = customer
|
|
self.order.save()
|
|
with scopes_disabled():
|
|
self.order.payments.create(provider='testdummy_partialrefund', amount=self.order.total,
|
|
state=OrderPayment.PAYMENT_STATE_CONFIRMED)
|
|
self.event.settings.cancel_allow_user_paid = True
|
|
self.event.settings.cancel_allow_user_paid_refund_as_giftcard = 'option'
|
|
gc = customer.usable_gift_cards()
|
|
assert len(gc) == 0
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
assert 'manually' not in response.content.decode()
|
|
assert "gift card" in response.content.decode()
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
'giftcard': 'true'
|
|
}, follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
assert "gift card" in response.content.decode()
|
|
self.order.refresh_from_db()
|
|
assert self.order.status == Order.STATUS_CANCELED
|
|
with scopes_disabled():
|
|
r = self.order.refunds.get()
|
|
assert r.provider == "giftcard"
|
|
assert r.amount == Decimal('23.00')
|
|
gc = customer.usable_gift_cards()
|
|
assert len(gc) == 1
|
|
|
|
def test_orders_cancel_unpaid_fee(self):
|
|
self.order.status = Order.STATUS_PENDING
|
|
self.order.save()
|
|
with scopes_disabled():
|
|
self.order.payments.create(provider='testdummy_partialrefund', amount=self.order.total, state=OrderPayment.PAYMENT_STATE_CREATED)
|
|
self.event.settings.cancel_allow_user = True
|
|
self.event.settings.cancel_allow_user_paid = False
|
|
self.event.settings.cancel_allow_user_unpaid_keep = Decimal('3.00')
|
|
self.event.settings.cancel_allow_user_paid_keep = Decimal('7.00')
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
assert 'manually' not in response.content.decode()
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
}, follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
self.order.refresh_from_db()
|
|
assert self.order.status == Order.STATUS_PENDING
|
|
assert self.order.total == Decimal('3.00')
|
|
with scopes_disabled():
|
|
assert self.order.refunds.count() == 0
|
|
|
|
def test_orders_cancel_paid_fee_autorefund(self):
|
|
self.order.status = Order.STATUS_PAID
|
|
self.order.save()
|
|
with scopes_disabled():
|
|
self.order.payments.create(provider='testdummy_partialrefund', amount=self.order.total, state=OrderPayment.PAYMENT_STATE_CONFIRMED)
|
|
self.event.settings.cancel_allow_user_paid = True
|
|
self.event.settings.cancel_allow_user_paid_keep = Decimal('3.00')
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
assert 'manually' not in response.content.decode()
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
}, follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
self.order.refresh_from_db()
|
|
assert self.order.status == Order.STATUS_PAID
|
|
assert self.order.total == Decimal('3.00')
|
|
with scopes_disabled():
|
|
assert self.order.refunds.count() == 1
|
|
|
|
def test_orders_cancel_paid_custom_fee_autorefund(self):
|
|
self.order.status = Order.STATUS_PAID
|
|
self.order.save()
|
|
with scopes_disabled():
|
|
self.order.payments.create(provider='testdummy_partialrefund', amount=self.order.total, state=OrderPayment.PAYMENT_STATE_CONFIRMED)
|
|
self.event.settings.cancel_allow_user_paid = True
|
|
self.event.settings.cancel_allow_user_paid_keep = Decimal('3.00')
|
|
self.event.settings.cancel_allow_user_paid_adjust_fees = True
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
'cancel_fee': '6.00'
|
|
}, follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
self.order.refresh_from_db()
|
|
assert self.order.status == Order.STATUS_PAID
|
|
assert self.order.total == Decimal('6.00')
|
|
with scopes_disabled():
|
|
assert self.order.refunds.count() == 1
|
|
|
|
def test_orders_cancel_paid_custom_fee_limit(self):
|
|
self.order.status = Order.STATUS_PAID
|
|
self.order.save()
|
|
with scopes_disabled():
|
|
self.order.payments.create(provider='testdummy_partialrefund', amount=self.order.total, state=OrderPayment.PAYMENT_STATE_CONFIRMED)
|
|
self.event.settings.cancel_allow_user_paid = True
|
|
self.event.settings.cancel_allow_user_paid_keep = Decimal('3.00')
|
|
self.event.settings.cancel_allow_user_paid_adjust_fees = True
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
'cancel_fee': '2.00'
|
|
}, follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
self.order.refresh_from_db()
|
|
assert self.order.status == Order.STATUS_PAID
|
|
assert self.order.total == Decimal('23.00')
|
|
with scopes_disabled():
|
|
assert self.order.refunds.count() == 0
|
|
|
|
def test_orders_cancel_paid_fee_no_autorefund(self):
|
|
self.order.status = Order.STATUS_PAID
|
|
self.order.save()
|
|
with scopes_disabled():
|
|
self.order.payments.create(provider='testdummy', amount=self.order.total,
|
|
state=OrderPayment.PAYMENT_STATE_CONFIRMED)
|
|
self.event.settings.cancel_allow_user_paid = True
|
|
self.event.settings.cancel_allow_user_paid_keep = Decimal('3.00')
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
assert 'cancellation fee of <strong>€3.00</strong>' in response.content.decode()
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
assert 'manually' in response.content.decode()
|
|
assert '20.00' in response.content.decode()
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
}, follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
self.order.refresh_from_db()
|
|
assert self.order.status == Order.STATUS_PAID
|
|
assert self.order.total == Decimal('3.00')
|
|
with scopes_disabled():
|
|
assert self.order.refunds.count() == 0
|
|
|
|
def test_orders_cancel_forbidden_if_any_payment_made(self):
|
|
self.event.settings.set('cancel_allow_user', True)
|
|
self.event.settings.set('cancel_allow_user_paid', False)
|
|
with scopes_disabled():
|
|
self.order.payments.create(
|
|
state=OrderPayment.PAYMENT_STATE_CONFIRMED,
|
|
amount=12,
|
|
provider='manual',
|
|
)
|
|
self.client.post(
|
|
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
}, follow=True)
|
|
self.order.refresh_from_db()
|
|
assert self.order.status == Order.STATUS_PENDING
|
|
|
|
def test_orders_cancel_paid_checkin_list(self):
|
|
self.order.status = Order.STATUS_PAID
|
|
self.order.save()
|
|
with scopes_disabled():
|
|
cl = self.event.checkin_lists.create(name="Foo")
|
|
self.order.positions.first().checkins.create(list=cl)
|
|
self.event.settings.cancel_allow_user_paid = True
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 302
|
|
|
|
cl.consider_tickets_used = False
|
|
cl.save()
|
|
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
def test_orders_cancel_forbidden(self):
|
|
self.event.settings.set('cancel_allow_user', False)
|
|
self.client.post(
|
|
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
|
}, follow=True)
|
|
self.order.refresh_from_db()
|
|
assert self.order.status == Order.STATUS_PENDING
|
|
|
|
def test_invoice_create_notallowed(self):
|
|
self.event.settings.set('invoice_generate', 'no')
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/invoice' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
|
|
def test_invoice_create_onlypaid(self):
|
|
self.event.settings.set('invoice_generate', 'user_paid')
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/invoice' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
self.order.status = Order.STATUS_PAID
|
|
self.order.save()
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/invoice' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{}, follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
with scopes_disabled():
|
|
assert self.order.invoices.exists()
|
|
|
|
def test_invoice_create_duplicate(self):
|
|
self.event.settings.set('invoice_generate', 'user')
|
|
with scopes_disabled():
|
|
generate_invoice(self.order)
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/invoice' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
|
|
def test_invoice_create_wrong_secret(self):
|
|
self.event.settings.set('invoice_generate', 'user')
|
|
with scopes_disabled():
|
|
generate_invoice(self.order)
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/invoice' % (self.orga.slug, self.event.slug, self.order.code, '1234'),
|
|
{})
|
|
assert 404 == response.status_code
|
|
|
|
def test_invoice_create_require_payment(self):
|
|
self.event.settings.set('invoice_generate', 'user')
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/invoice' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
with scopes_disabled():
|
|
assert not self.order.invoices.exists()
|
|
|
|
def test_invoice_create_ok(self):
|
|
self.event.settings.set('invoice_generate', 'user')
|
|
with scopes_disabled():
|
|
self.order.payments.create(provider='banktransfer', state=OrderPayment.PAYMENT_STATE_CONFIRMED,
|
|
amount=self.order.total)
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/invoice' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{}, follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
with scopes_disabled():
|
|
assert self.order.invoices.exists()
|
|
|
|
def test_orders_download_pending(self):
|
|
self.event.settings.set('ticket_download', True)
|
|
del self.event.settings['ticket_download_date']
|
|
|
|
self.order.status = Order.STATUS_PENDING
|
|
self.order.save()
|
|
self.event.settings.set('ticket_download_pending', True)
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/download/%d/testdummy' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret, self.ticket_pos.pk),
|
|
follow=True
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
def test_orders_download_pending_only_approved(self):
|
|
self.event.settings.set('ticket_download', True)
|
|
del self.event.settings['ticket_download_date']
|
|
|
|
self.order.status = Order.STATUS_PENDING
|
|
self.order.require_approval = True
|
|
self.order.save()
|
|
self.event.settings.set('ticket_download_pending', True)
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/download/%d/testdummy' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret, self.ticket_pos.pk),
|
|
)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
|
|
def test_ticket_download(self):
|
|
self.event.settings.set('ticket_download', True)
|
|
del self.event.settings['ticket_download_date']
|
|
self.order.status = Order.STATUS_PAID
|
|
self.order.save()
|
|
response = self.client.post(
|
|
'/%s/%s/ticket/%s/%s/%s/download/%d/testdummy' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.ticket_pos.positionid, self.ticket_pos.web_secret,
|
|
self.ticket_pos.pk),
|
|
follow=True)
|
|
assert response.status_code == 200
|
|
|
|
def test_orders_download(self):
|
|
self.event.settings.set('ticket_download', True)
|
|
del self.event.settings['ticket_download_date']
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/download/%d/pdf' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret, self.ticket_pos.pk),
|
|
follow=True)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
|
|
response = self.client.post(
|
|
'/%s/%s/order/ABC/123/download/%d/testdummy' % (self.orga.slug, self.event.slug,
|
|
self.ticket_pos.pk)
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/download/%d/testdummy' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret, self.ticket_pos.pk),
|
|
follow=True
|
|
)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
|
|
self.order.status = Order.STATUS_PAID
|
|
self.order.save()
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/download/%d/testdummy' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret, self.ticket_pos.pk),
|
|
follow=True
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
self.event.settings.set('ticket_download_date', now() + datetime.timedelta(days=1))
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/download/%d/testdummy' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret, self.ticket_pos.pk),
|
|
follow=True
|
|
)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
|
|
self.event.date_from = now() + datetime.timedelta(days=3)
|
|
self.event.save()
|
|
self.event.settings.set('ticket_download_date', RelativeDateWrapper(RelativeDate(
|
|
base_date_name='date_from', days=2, time=None, minutes=None
|
|
)))
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/download/%d/testdummy' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret, self.ticket_pos.pk),
|
|
follow=True
|
|
)
|
|
self.assertRedirects(response,
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
|
|
del self.event.settings['ticket_download_date']
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/download/%d/testdummy' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret, self.ticket_pos.pk),
|
|
follow=True
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
self.event.settings.set('ticket_download', False)
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/download/%d/testdummy' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret, self.ticket_pos.pk),
|
|
follow=True
|
|
)
|
|
self.assertRedirects(response, '/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret),
|
|
target_status_code=200)
|
|
|
|
def test_change_paymentmethod_wrong_secret(self):
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, '123'))
|
|
assert response.status_code == 404
|
|
|
|
def test_change_paymentmethod_wrong_state(self):
|
|
self.order.status = Order.STATUS_PAID
|
|
self.order.save()
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
follow=True
|
|
)
|
|
assert 'alert-danger' in response.content.decode()
|
|
|
|
def test_change_paymentmethod_expired_not_available(self):
|
|
self.order.status = Order.STATUS_EXPIRED
|
|
self.order.save()
|
|
self.quota_tickets.size = 0
|
|
self.quota_tickets.save()
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
follow=True
|
|
)
|
|
assert 'alert-danger' in response.content.decode()
|
|
|
|
def test_pay_wrong_payment_state(self):
|
|
with scopes_disabled():
|
|
p = self.order.payments.create(
|
|
provider='manual',
|
|
state=OrderPayment.PAYMENT_STATE_CANCELED,
|
|
amount=Decimal('10.00'),
|
|
)
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/%d/' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret,
|
|
p.pk),
|
|
follow=True
|
|
)
|
|
assert 'alert-danger' in response.content.decode()
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/%d/confirm' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret,
|
|
p.pk),
|
|
follow=True
|
|
)
|
|
assert 'alert-danger' in response.content.decode()
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/%d/complete' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret,
|
|
p.pk),
|
|
follow=True
|
|
)
|
|
assert 'alert-danger' in response.content.decode()
|
|
|
|
def test_pay_expired_not_available(self):
|
|
self.order.status = Order.STATUS_EXPIRED
|
|
self.order.save()
|
|
self.quota_tickets.size = 0
|
|
self.quota_tickets.save()
|
|
with scopes_disabled():
|
|
p = self.order.payments.create(
|
|
provider='banktransfer',
|
|
state=OrderPayment.PAYMENT_STATE_CREATED,
|
|
amount=Decimal('10.00'),
|
|
)
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/%d/' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret, p.pk),
|
|
follow=True
|
|
)
|
|
assert 'alert-danger' in response.content.decode()
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/%d/confirm' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret, p.pk),
|
|
follow=True
|
|
)
|
|
assert 'alert-danger' in response.content.decode()
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/%d/complete' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret, p.pk),
|
|
follow=True
|
|
)
|
|
assert 'alert-danger' in response.content.decode()
|
|
|
|
def test_pay_wrong_order_state(self):
|
|
self.order.status = Order.STATUS_PAID
|
|
self.order.save()
|
|
with scopes_disabled():
|
|
p = self.order.payments.create(
|
|
provider='manual',
|
|
state=OrderPayment.PAYMENT_STATE_PENDING,
|
|
amount=Decimal('10.00'),
|
|
)
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/%d/' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret,
|
|
p.pk),
|
|
follow=True
|
|
)
|
|
assert 'alert-danger' in response.content.decode()
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/%d/confirm' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret,
|
|
p.pk),
|
|
follow=True
|
|
)
|
|
assert 'alert-danger' in response.content.decode()
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/%d/complete' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret,
|
|
p.pk),
|
|
follow=True
|
|
)
|
|
assert 'alert-danger' in response.content.decode()
|
|
|
|
def test_pay_change_link(self):
|
|
self.order.status = Order.STATUS_PAID
|
|
self.order.save()
|
|
with scopes_disabled():
|
|
p = self.order.payments.create(
|
|
provider='banktransfer',
|
|
state=OrderPayment.PAYMENT_STATE_CONFIRMED,
|
|
amount=self.order.total,
|
|
)
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
follow=True
|
|
)
|
|
assert '/pay/change' not in response.content.decode()
|
|
self.order.status = Order.STATUS_PENDING
|
|
self.order.save()
|
|
p.state = OrderPayment.PAYMENT_STATE_PENDING
|
|
p.save()
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
follow=True
|
|
)
|
|
assert '/pay/change' in response.content.decode()
|
|
p.provider = 'testdummy'
|
|
p.save()
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
follow=True
|
|
)
|
|
assert '/pay/change' not in response.content.decode()
|
|
|
|
def test_change_paymentmethod_partial(self):
|
|
self.event.settings.set('payment_banktransfer__enabled', True)
|
|
self.event.settings.set('payment_testdummy__enabled', True)
|
|
self.event.settings.set('payment_testdummy__fee_reverse_calc', False)
|
|
self.event.settings.set('payment_testdummy__fee_percent', '10.00')
|
|
with scopes_disabled():
|
|
self.order.payments.create(
|
|
provider='manual',
|
|
state=OrderPayment.PAYMENT_STATE_CONFIRMED,
|
|
amount=Decimal('10.00'),
|
|
)
|
|
|
|
generate_invoice(self.order)
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
)
|
|
assert 'Test dummy' in response.content.decode()
|
|
assert '+ €1.30' in response.content.decode()
|
|
self.client.post(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{
|
|
'payment': 'testdummy'
|
|
}
|
|
)
|
|
self.order.refresh_from_db()
|
|
with scopes_disabled():
|
|
assert self.order.payments.last().provider == 'testdummy'
|
|
fee = self.order.fees.filter(fee_type=OrderFee.FEE_TYPE_PAYMENT).last()
|
|
assert fee.value == Decimal('1.30')
|
|
assert self.order.total == Decimal('23.00') + fee.value
|
|
assert self.order.invoices.count() == 3
|
|
p = self.order.payments.last()
|
|
assert p.provider == 'testdummy'
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CREATED
|
|
assert p.amount == Decimal('14.30')
|
|
|
|
def test_change_paymentmethod_partial_with_previous_fee(self):
|
|
self.event.settings.set('payment_banktransfer__enabled', True)
|
|
self.event.settings.set('payment_testdummy__enabled', True)
|
|
self.event.settings.set('payment_testdummy__fee_reverse_calc', False)
|
|
self.event.settings.set('payment_testdummy__fee_percent', '10.00')
|
|
with scopes_disabled():
|
|
f = self.order.fees.create(
|
|
fee_type=OrderFee.FEE_TYPE_PAYMENT,
|
|
value='1.40'
|
|
)
|
|
self.order.total += Decimal('1.4')
|
|
self.order.save()
|
|
self.order.payments.create(
|
|
provider='manual',
|
|
state=OrderPayment.PAYMENT_STATE_CONFIRMED,
|
|
amount=Decimal('11.40'),
|
|
fee=f
|
|
)
|
|
|
|
generate_invoice(self.order)
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
)
|
|
assert 'Test dummy' in response.content.decode()
|
|
assert '+ €1.30' in response.content.decode()
|
|
self.client.post(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{
|
|
'payment': 'testdummy'
|
|
}
|
|
)
|
|
self.order.refresh_from_db()
|
|
with scopes_disabled():
|
|
assert self.order.payments.last().provider == 'testdummy'
|
|
fee = self.order.fees.filter(fee_type=OrderFee.FEE_TYPE_PAYMENT).last()
|
|
assert fee.value == Decimal('1.30')
|
|
assert self.order.total == Decimal('24.40') + fee.value
|
|
assert self.order.invoices.count() == 3
|
|
p = self.order.payments.last()
|
|
assert p.provider == 'testdummy'
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CREATED
|
|
assert p.amount == Decimal('14.30')
|
|
self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret, p.pk),
|
|
{}
|
|
)
|
|
self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/%s/confirm' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret, p.pk),
|
|
{}
|
|
)
|
|
p.refresh_from_db()
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CREATED
|
|
|
|
def test_change_paymentmethod_with_rounding_change(self):
|
|
tr19 = self.event.tax_rules.create(
|
|
name='VAT',
|
|
rate=Decimal('19.00'),
|
|
default=True
|
|
)
|
|
self.ticket.tax_rule = tr19
|
|
self.ticket.save()
|
|
self.ticket_pos.price = Decimal("100.00")
|
|
self.ticket_pos.tax_rule = tr19
|
|
self.ticket_pos._calculate_tax()
|
|
self.ticket_pos.save()
|
|
self.order.total = Decimal("100.00")
|
|
self.order.tax_rounding_mode = "sum_by_net"
|
|
self.order.save()
|
|
|
|
self.event.settings.tax_rounding = "sum_by_net"
|
|
self.event.settings.set('payment_banktransfer__enabled', True)
|
|
self.event.settings.set('payment_testdummy__enabled', True)
|
|
self.event.settings.set('payment_testdummy__fee_reverse_calc', False)
|
|
self.event.settings.set('payment_testdummy__fee_abs', '100.00')
|
|
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
)
|
|
assert 'Test dummy' in response.content.decode()
|
|
assert '+ €100.00' in response.content.decode()
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{
|
|
'payment': 'testdummy'
|
|
}, follow=True
|
|
)
|
|
assert 'Total: €199.99' in response.content.decode()
|
|
self.order.refresh_from_db()
|
|
with scopes_disabled():
|
|
assert self.order.payments.last().provider == 'testdummy'
|
|
fee = self.order.fees.filter(fee_type=OrderFee.FEE_TYPE_PAYMENT).last()
|
|
assert fee.value == Decimal('100.00')
|
|
assert fee.tax_value == Decimal('15.97')
|
|
self.ticket_pos.refresh_from_db()
|
|
assert self.ticket_pos.price == Decimal("99.99")
|
|
assert self.ticket_pos.price_includes_rounding_correction == Decimal("-0.01")
|
|
self.order.refresh_from_db()
|
|
assert self.order.total == Decimal('199.99')
|
|
p = self.order.payments.last()
|
|
assert p.provider == 'testdummy'
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CREATED
|
|
assert p.amount == Decimal('199.99')
|
|
|
|
def test_change_paymentmethod_to_same(self):
|
|
with scopes_disabled():
|
|
p_old = self.order.payments.create(
|
|
provider='banktransfer',
|
|
state=OrderPayment.PAYMENT_STATE_CREATED,
|
|
amount=Decimal('10.00'),
|
|
)
|
|
self.client.post(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{
|
|
'payment': 'banktransfer'
|
|
}
|
|
)
|
|
self.order.refresh_from_db()
|
|
with scopes_disabled():
|
|
p_new = self.order.payments.last()
|
|
assert p_new.provider == 'banktransfer'
|
|
assert p_new.id != p_old.id
|
|
assert p_new.state == OrderPayment.PAYMENT_STATE_CREATED
|
|
p_old.refresh_from_db()
|
|
assert p_old.state == OrderPayment.PAYMENT_STATE_CANCELED
|
|
|
|
def test_change_paymentmethod_cancel_old(self):
|
|
self.event.settings.set('payment_banktransfer__enabled', True)
|
|
with scopes_disabled():
|
|
p_old = self.order.payments.create(
|
|
provider='testdummy',
|
|
state=OrderPayment.PAYMENT_STATE_CREATED,
|
|
amount=Decimal('10.00'),
|
|
)
|
|
self.client.post(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{
|
|
'payment': 'banktransfer'
|
|
}
|
|
)
|
|
self.order.refresh_from_db()
|
|
with scopes_disabled():
|
|
p_new = self.order.payments.last()
|
|
assert p_new.provider == 'banktransfer'
|
|
assert p_new.id != p_old.id
|
|
assert p_new.state == OrderPayment.PAYMENT_STATE_CREATED
|
|
p_old.refresh_from_db()
|
|
assert p_old.state == OrderPayment.PAYMENT_STATE_CANCELED
|
|
|
|
def test_change_paymentmethod_delete_fee(self):
|
|
self.event.settings.set('payment_banktransfer__enabled', True)
|
|
self.event.settings.set('payment_testdummy__enabled', True)
|
|
self.event.settings.set('payment_testdummy__fee_reverse_calc', False)
|
|
self.event.settings.set('payment_testdummy__fee_percent', '0.00')
|
|
with scopes_disabled():
|
|
f = self.order.fees.create(
|
|
fee_type=OrderFee.FEE_TYPE_PAYMENT,
|
|
value='1.40'
|
|
)
|
|
self.order.total += Decimal('1.4')
|
|
self.order.save()
|
|
p0 = self.order.payments.create(
|
|
provider='manual',
|
|
state=OrderPayment.PAYMENT_STATE_CREATED,
|
|
amount=Decimal('24.40'),
|
|
fee=f
|
|
)
|
|
|
|
generate_invoice(self.order)
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
)
|
|
assert 'Test dummy' in response.content.decode()
|
|
assert '- €1.40' in response.content.decode()
|
|
self.client.post(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{
|
|
'payment': 'testdummy'
|
|
}
|
|
)
|
|
with scopes_disabled():
|
|
self.order.refresh_from_db()
|
|
assert self.order.payments.last().provider == 'testdummy'
|
|
assert not self.order.fees.filter(fee_type=OrderFee.FEE_TYPE_PAYMENT).exists()
|
|
assert self.order.total == Decimal('23.00')
|
|
assert self.order.invoices.count() == 3
|
|
p0.refresh_from_db()
|
|
assert p0.state == OrderPayment.PAYMENT_STATE_CANCELED
|
|
p = self.order.payments.last()
|
|
assert p.provider == 'testdummy'
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CREATED
|
|
assert p.amount == Decimal('23.00')
|
|
|
|
def test_change_paymentmethod_available(self):
|
|
self.event.settings.set('payment_banktransfer__enabled', True)
|
|
self.event.settings.set('payment_testdummy__enabled', True)
|
|
self.event.settings.set('payment_testdummy__fee_abs', '12.00')
|
|
with scopes_disabled():
|
|
generate_invoice(self.order)
|
|
self.order.payments.create(
|
|
provider='banktransfer',
|
|
state=OrderPayment.PAYMENT_STATE_PENDING,
|
|
amount=self.order.total,
|
|
)
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
)
|
|
assert 'Test dummy' in response.content.decode()
|
|
assert '+ €12.00' in response.content.decode()
|
|
self.client.post(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{
|
|
'payment': 'testdummy'
|
|
}
|
|
)
|
|
self.order.refresh_from_db()
|
|
with scopes_disabled():
|
|
p = self.order.payments.last()
|
|
assert p.provider == 'testdummy'
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CREATED
|
|
p0 = self.order.payments.first()
|
|
assert p0.state == OrderPayment.PAYMENT_STATE_CANCELED
|
|
assert p0.provider == 'banktransfer'
|
|
fee = self.order.fees.get(fee_type=OrderFee.FEE_TYPE_PAYMENT)
|
|
assert fee.value == Decimal('12.00')
|
|
assert self.order.total == Decimal('23.00') + fee.value
|
|
assert self.order.invoices.count() == 3
|
|
|
|
def test_change_paymentmethod_giftcard_partial(self):
|
|
with scopes_disabled():
|
|
self.order.payments.create(
|
|
provider='manual',
|
|
state=OrderPayment.PAYMENT_STATE_CONFIRMED,
|
|
amount=Decimal('10.00'),
|
|
)
|
|
gc = self.orga.issued_gift_cards.create(currency="EUR")
|
|
gc.transactions.create(value=10, acceptor=self.orga)
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
)
|
|
assert 'Gift card' in response.content.decode()
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{
|
|
'payment': 'giftcard',
|
|
'payment_giftcard-code': gc.secret
|
|
}
|
|
)
|
|
with scopes_disabled():
|
|
p = self.order.payments.last()
|
|
self.assertRedirects(
|
|
response,
|
|
'/%s/%s/order/%s/%s/pay/%s/confirm' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret, p.pk),
|
|
)
|
|
self.client.post(
|
|
'/%s/%s/order/%s/%s/pay/%s/confirm' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret, p.pk),
|
|
{}
|
|
)
|
|
self.order.refresh_from_db()
|
|
p.refresh_from_db()
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
|
|
assert self.order.status == Order.STATUS_PENDING
|
|
assert gc.value == Decimal('0.00')
|
|
assert self.order.pending_sum == Decimal('3.00')
|
|
|
|
def test_change_paymentmethod_giftcard_swap_card(self):
|
|
with scopes_disabled():
|
|
self.order.payments.create(
|
|
provider='manual',
|
|
state=OrderPayment.PAYMENT_STATE_CONFIRMED,
|
|
amount=Decimal('10.00'),
|
|
)
|
|
gc = self.orga.issued_gift_cards.create(currency="EUR")
|
|
gc.transactions.create(value=10, acceptor=self.orga)
|
|
self.ticket.issue_giftcard = True
|
|
self.ticket.save()
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{
|
|
'payment': 'giftcard',
|
|
'payment_giftcard-code': gc.secret
|
|
}
|
|
)
|
|
assert "You cannot pay with gift cards when buying a gift card." in response.content.decode()
|
|
|
|
def test_change_paymentmethod_giftcard_wrong_currency(self):
|
|
with scopes_disabled():
|
|
gc = self.orga.issued_gift_cards.create(currency="USD")
|
|
gc.transactions.create(value=10, acceptor=self.orga)
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{
|
|
'payment': 'giftcard',
|
|
'payment_giftcard-code': gc.secret
|
|
}
|
|
)
|
|
assert "This gift card does not support this currency." in response.content.decode()
|
|
|
|
def test_change_paymentmethod_giftcard_in_test_mode(self):
|
|
with scopes_disabled():
|
|
self.order.testmode = True
|
|
self.order.save()
|
|
gc = self.orga.issued_gift_cards.create(currency="EUR")
|
|
gc.transactions.create(value=10, acceptor=self.orga)
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{
|
|
'payment': 'giftcard',
|
|
'payment_giftcard-code': gc.secret
|
|
}
|
|
)
|
|
assert "Only test gift cards can be used in test mode." in response.content.decode()
|
|
|
|
def test_change_paymentmethod_giftcard_not_in_test_mode(self):
|
|
with scopes_disabled():
|
|
gc = self.orga.issued_gift_cards.create(currency="EUR", testmode=True)
|
|
gc.transactions.create(value=10, acceptor=self.orga)
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{
|
|
'payment': 'giftcard',
|
|
'payment_giftcard-code': gc.secret
|
|
}
|
|
)
|
|
assert "This gift card can only be used in test mode." in response.content.decode()
|
|
|
|
def test_change_paymentmethod_giftcard_empty(self):
|
|
with scopes_disabled():
|
|
gc = self.orga.issued_gift_cards.create(currency="EUR")
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{
|
|
'payment': 'giftcard',
|
|
'payment_giftcard-code': gc.secret
|
|
}
|
|
)
|
|
assert "All credit on this gift card has been used." in response.content.decode()
|
|
|
|
def test_change_paymentmethod_giftcard_wrong_organizer(self):
|
|
with scopes_disabled():
|
|
o = Organizer.objects.create(slug='Foo', name='bar')
|
|
self.orga.issued_gift_cards.create(currency="EUR")
|
|
gc = o.issued_gift_cards.create(currency="EUR")
|
|
gc.transactions.create(value=10, acceptor=self.orga)
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{
|
|
'payment': 'giftcard',
|
|
'payment_giftcard-code': gc.secret
|
|
}
|
|
)
|
|
assert "This gift card is not known." in response.content.decode()
|
|
|
|
def test_change_paymentmethod_giftcard(self):
|
|
with scopes_disabled():
|
|
self.order.payments.create(
|
|
provider='manual',
|
|
state=OrderPayment.PAYMENT_STATE_CONFIRMED,
|
|
amount=Decimal('10.00'),
|
|
)
|
|
gc = self.orga.issued_gift_cards.create(currency="EUR")
|
|
gc.transactions.create(value=100, acceptor=self.orga)
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
)
|
|
assert 'Gift card' in response.content.decode()
|
|
response = self.client.post(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
{
|
|
'payment': 'giftcard',
|
|
'payment_giftcard-code': gc.secret
|
|
}
|
|
)
|
|
with scopes_disabled():
|
|
p = self.order.payments.last()
|
|
self.assertRedirects(
|
|
response,
|
|
'/%s/%s/order/%s/%s/pay/%s/confirm' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret, p.pk),
|
|
)
|
|
self.client.post(
|
|
'/%s/%s/order/%s/%s/pay/%s/confirm' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret, p.pk),
|
|
{}
|
|
)
|
|
self.order.refresh_from_db()
|
|
p.refresh_from_db()
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
|
|
assert self.order.status == Order.STATUS_PAID
|
|
assert gc.value == Decimal('87.00')
|
|
|
|
def test_change_paymentmethod_customeraccount_giftcard_offered(self):
|
|
with scopes_disabled():
|
|
self.orga.settings.customer_accounts = True
|
|
customer = self.orga.customers.create(email='john@example.org', is_verified=True)
|
|
customer.set_password('foo')
|
|
customer.save()
|
|
self.order.customer = customer
|
|
self.order.payments.create(
|
|
provider='manual',
|
|
state=OrderPayment.PAYMENT_STATE_CONFIRMED,
|
|
amount=Decimal('10.00'),
|
|
)
|
|
gc = self.orga.issued_gift_cards.create(currency="EUR", customer=customer)
|
|
gc.transactions.create(value=100, acceptor=self.orga)
|
|
ugc = customer.usable_gift_cards()
|
|
assert len(ugc) == 1
|
|
r = self.client.post('/%s/account/login' % (self.orga.slug), {
|
|
'email': 'john@example.org',
|
|
'password': 'foo',
|
|
})
|
|
assert r.status_code == 302
|
|
r = self.client.get('/%s/account/' % (self.orga.slug))
|
|
assert r.status_code == 200
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
|
)
|
|
assert 'Gift card' in response.content.decode()
|
|
assert '1 available' in response.content.decode()
|
|
|
|
def test_answer_download_token(self):
|
|
with scopes_disabled():
|
|
q = self.event.questions.create(question="Foo", type="F")
|
|
q.items.add(self.ticket)
|
|
a = self.ticket_pos.answers.create(question=q, answer="file")
|
|
val = SimpleUploadedFile("testfile.txt", b"file_content")
|
|
a.file.save("testfile.txt", val)
|
|
a.save()
|
|
|
|
self.event.settings.set('ticket_download', True)
|
|
del self.event.settings['ticket_download_date']
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/answer/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret, a.pk)
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
match = re.search(r"\?token=([^'\"&]+)", response.content.decode())
|
|
assert match
|
|
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/answer/%s/?token=%s' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret, a.pk, match.group(1))
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
client2 = self.client_class()
|
|
response = client2.get(
|
|
'/%s/%s/order/%s/%s/answer/%s/?token=%s' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.order.secret, a.pk, match.group(1))
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
def test_require_login_for_order_access_disabled_unauth(self):
|
|
self.orga.settings.customer_accounts = True
|
|
|
|
with scopes_disabled():
|
|
customer = self.orga.customers.create(email='john@example.org', is_verified=True)
|
|
customer.set_password("foo")
|
|
customer.save()
|
|
|
|
self.order.customer = customer
|
|
self.order.save()
|
|
|
|
self.orga.settings.customer_accounts_require_login_for_order_access = False
|
|
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
assert len(doc.select(".cart-row")) > 0
|
|
assert "pending" in doc.select(".order-details")[0].text.lower()
|
|
assert "Peter" in response.content.decode()
|
|
assert "Lukas" not in response.content.decode()
|
|
|
|
response = self.client.get(
|
|
'/%s/%s/ticket/%s/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.ticket_pos.positionid, self.ticket_pos.web_secret)
|
|
)
|
|
assert response.status_code == 200
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
assert len(doc.select(".cart-row")) > 0
|
|
assert "pending" in doc.select(".order-details")[0].text.lower()
|
|
assert "Peter" in response.content.decode()
|
|
assert "Lukas" not in response.content.decode()
|
|
|
|
def test_require_login_for_order_access_enabled_unauth(self):
|
|
self.orga.settings.customer_accounts = True
|
|
|
|
with scopes_disabled():
|
|
customer = self.orga.customers.create(email='john@example.org', is_verified=True)
|
|
customer.set_password("foo")
|
|
customer.save()
|
|
|
|
self.order.customer = customer
|
|
self.order.save()
|
|
|
|
self.orga.settings.customer_accounts_require_login_for_order_access = True
|
|
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 302
|
|
|
|
response = self.client.get(
|
|
'/%s/%s/ticket/%s/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.ticket_pos.positionid, self.ticket_pos.web_secret)
|
|
)
|
|
assert response.status_code == 200
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
assert len(doc.select(".cart-row")) > 0
|
|
assert "pending" in doc.select(".order-details")[0].text.lower()
|
|
assert "Peter" in response.content.decode()
|
|
assert "Lukas" not in response.content.decode()
|
|
|
|
def test_require_login_for_order_access_disabled_auth(self):
|
|
self.orga.settings.customer_accounts = True
|
|
|
|
with scopes_disabled():
|
|
customer = self.orga.customers.create(email='john@example.org', is_verified=True)
|
|
customer.set_password("foo")
|
|
customer.save()
|
|
|
|
self.order.customer = customer
|
|
self.order.save()
|
|
|
|
self.orga.settings.customer_accounts_require_login_for_order_access = False
|
|
|
|
response = self.client.post('/%s/account/login' % (self.orga.slug), {
|
|
'email': 'john@example.org',
|
|
'password': 'foo',
|
|
})
|
|
assert response.status_code == 302
|
|
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
assert len(doc.select(".cart-row")) > 0
|
|
assert "pending" in doc.select(".order-details")[0].text.lower()
|
|
assert "Peter" in response.content.decode()
|
|
assert "Lukas" not in response.content.decode()
|
|
|
|
response = self.client.get(
|
|
'/%s/%s/ticket/%s/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.ticket_pos.positionid, self.ticket_pos.web_secret)
|
|
)
|
|
assert response.status_code == 200
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
assert len(doc.select(".cart-row")) > 0
|
|
assert "pending" in doc.select(".order-details")[0].text.lower()
|
|
assert "Peter" in response.content.decode()
|
|
assert "Lukas" not in response.content.decode()
|
|
|
|
def test_require_login_for_order_access_enabled_auth(self):
|
|
self.orga.settings.customer_accounts = True
|
|
|
|
with scopes_disabled():
|
|
customer = self.orga.customers.create(email='john@example.org', is_verified=True)
|
|
customer.set_password("foo")
|
|
customer.save()
|
|
|
|
self.order.customer = customer
|
|
self.order.save()
|
|
|
|
self.orga.settings.customer_accounts_require_login_for_order_access = True
|
|
|
|
response = self.client.post('/%s/account/login' % (self.orga.slug), {
|
|
'email': 'john@example.org',
|
|
'password': 'foo',
|
|
})
|
|
assert response.status_code == 302
|
|
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
assert len(doc.select(".cart-row")) > 0
|
|
assert "pending" in doc.select(".order-details")[0].text.lower()
|
|
assert "Peter" in response.content.decode()
|
|
assert "Lukas" not in response.content.decode()
|
|
|
|
response = self.client.get(
|
|
'/%s/%s/ticket/%s/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
|
self.ticket_pos.positionid, self.ticket_pos.web_secret)
|
|
)
|
|
assert response.status_code == 200
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
assert len(doc.select(".cart-row")) > 0
|
|
assert "pending" in doc.select(".order-details")[0].text.lower()
|
|
assert "Peter" in response.content.decode()
|
|
assert "Lukas" not in response.content.decode()
|
|
|
|
def test_require_login_for_order_access_accounts_disabled(self):
|
|
self.orga.settings.customer_accounts = True
|
|
|
|
with scopes_disabled():
|
|
customer = self.orga.customers.create(email='john@example.org', is_verified=True)
|
|
customer.set_password("foo")
|
|
customer.save()
|
|
|
|
self.order.customer = customer
|
|
self.order.save()
|
|
|
|
self.orga.settings.customer_accounts = False
|
|
self.orga.settings.customer_accounts_require_login_for_order_access = True
|
|
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
assert len(doc.select(".cart-row")) > 0
|
|
assert "pending" in doc.select(".order-details")[0].text.lower()
|
|
assert "Peter" in response.content.decode()
|
|
assert "Lukas" not in response.content.decode()
|
|
|
|
def test_require_login_for_order_access_enabled_wrong_customer(self):
|
|
self.orga.settings.customer_accounts = True
|
|
|
|
with scopes_disabled():
|
|
customer = self.orga.customers.create(email='john@example.org', is_verified=True)
|
|
customer.set_password("foo")
|
|
customer.save()
|
|
|
|
self.order.customer = customer
|
|
self.order.save()
|
|
|
|
with scopes_disabled():
|
|
customer = self.orga.customers.create(email='jill@example.org', is_verified=True)
|
|
customer.set_password("bar")
|
|
customer.save()
|
|
|
|
self.orga.settings.customer_accounts_require_login_for_order_access = True
|
|
|
|
response = self.client.post('/%s/account/login' % (self.orga.slug), {
|
|
'email': 'jill@example.org',
|
|
'password': 'bar',
|
|
})
|
|
assert response.status_code == 302
|
|
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
def test_require_login_for_order_access_enabled_unverified_account(self):
|
|
self.orga.settings.customer_accounts = True
|
|
|
|
with scopes_disabled():
|
|
customer = self.orga.customers.create(email='john@example.org', is_verified=False)
|
|
customer.set_password("foo")
|
|
customer.save()
|
|
|
|
self.order.customer = customer
|
|
self.order.save()
|
|
|
|
self.orga.settings.customer_accounts_require_login_for_order_access = True
|
|
|
|
response = self.client.get(
|
|
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
|
)
|
|
assert response.status_code == 200
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
assert len(doc.select(".cart-row")) > 0
|
|
assert "pending" in doc.select(".order-details")[0].text.lower()
|
|
assert "Peter" in response.content.decode()
|
|
assert "Lukas" not in response.content.decode()
|