Add flag testmode_supported to sales channels (#1455)

* Add testmode-support-flag to SalesChannels

* Make saleschannels/testmode-warnings even more dangerous!

* Add warning for payment-methods that do support testmode but are being used in a non-testmode order caused by a saleschannel in a testmode-shop.

* Remove redundant testmode_supported-flag for WebshopSalesChannel

* Raise error on API when sales_channel does not support testmode

* Tests

* Fix style issue after merge
This commit is contained in:
Martin Gross
2019-10-21 10:07:02 +02:00
committed by Raphael Michel
parent e8a2f7e349
commit 2b18621c76
14 changed files with 171 additions and 24 deletions

View File

@@ -675,6 +675,20 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
raise ValidationError(errs)
return data
def validate_testmode(self, testmode):
if 'sales_channel' in self.initial_data:
try:
sales_channel = get_all_sales_channels()[self.initial_data['sales_channel']]
if testmode and not sales_channel.testmode_supported:
raise ValidationError('This sales channel does not provide support for testmode.')
except KeyError:
# We do not need to raise a ValidationError here, since there is another check to validate the
# sales_channel
pass
return testmode
def create(self, validated_data):
fees_data = validated_data.pop('fees') if 'fees' in validated_data else []
positions_data = validated_data.pop('positions') if 'positions' in validated_data else []

View File

@@ -35,6 +35,13 @@ class SalesChannel:
"""
return "circle"
@property
def testmode_supported(self) -> bool:
"""
Indication, if a saleschannels supports test mode orders
"""
return True
def get_all_sales_channels():
global _ALL_CHANNELS

View File

@@ -17,6 +17,7 @@ from django.utils.translation import ugettext as _
from django_scopes import scopes_disabled
from pretix.api.models import OAuthApplication
from pretix.base.channels import get_all_sales_channels
from pretix.base.email import get_email_context
from pretix.base.i18n import LazyLocaleException, language
from pretix.base.models import (
@@ -595,6 +596,8 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d
meta_info: dict=None, sales_channel: str='web', gift_cards: list=None,
shown_total=None):
p = None
sales_channel = get_all_sales_channels()[sales_channel]
with transaction.atomic():
checked_gift_cards = []
if gift_cards:
@@ -622,10 +625,10 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d
datetime=now_dt,
locale=locale,
total=total,
testmode=event.testmode,
testmode=True if sales_channel.testmode_supported and event.testmode else False,
meta_info=json.dumps(meta_info or {}),
require_approval=any(p.item.require_approval for p in positions),
sales_channel=sales_channel
sales_channel=sales_channel.identifier
)
order.set_expires(now_dt, event.subevents.filter(id__in=[p.subevent_id for p in positions]))
order.save()

View File

@@ -247,7 +247,7 @@ class AddOnsStep(CartMixin, AsyncAction, TemplateFlowStep):
quota_cache=quota_cache,
item_cache=item_cache,
subevent=cartpos.subevent,
sales_channel=self.request.sales_channel
sales_channel=self.request.sales_channel.identifier
)
}
@@ -306,7 +306,7 @@ class AddOnsStep(CartMixin, AsyncAction, TemplateFlowStep):
return self.do(self.request.event.id, data, get_or_create_cart_id(self.request),
invoice_address=self.invoice_address.pk, locale=get_language(),
sales_channel=request.sales_channel)
sales_channel=request.sales_channel.identifier)
class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
@@ -711,7 +711,7 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
return self.do(self.request.event.id, self.payment_provider.identifier if self.payment_provider else None,
[p.id for p in self.positions], self.cart_session.get('email'),
translation.get_language(), self.invoice_address.pk, meta_info,
request.sales_channel, self.cart_session.get('gift_cards'),
request.sales_channel.identifier, self.cart_session.get('gift_cards'),
self.cart_session.get('shown_total'))
def get_success_message(self, value):

View File

@@ -66,11 +66,19 @@
<div class="clearfix"></div>
</div>
{% if request.event.testmode %}
<div class="alert alert-warning">
<strong>
{% trans "This ticket shop is currently in test mode. Please do not perform any real purchases as your order might be deleted without notice." %}
</strong>
</div>
{% if request.sales_channel.testmode_supported %}
<div class="alert alert-warning">
<strong>
{% trans "This ticket shop is currently in test mode. Please do not perform any real purchases as your order might be deleted without notice." %}
</strong>
</div>
{% else %}
<div class="alert alert-danger">
<strong>
{% trans "Orders made through this sales channel cannot be deleted - even if the ticket shop is in test mode!" %}
</strong>
</div>
{% endif %}
{% endif %}
{% if messages %}
{% for message in messages %}
@@ -82,11 +90,19 @@
{% block content %}
{% endblock %}
{% if request.event.testmode %}
<div class="alert alert-testmode alert-warning">
<strong>
{% trans "This ticket shop is currently in test mode. Please do not perform any real purchases as your order might be deleted without notice." %}
</strong>
</div>
{% if request.sales_channel.testmode_supported %}
<div class="alert alert-testmode alert-warning">
<strong>
{% trans "This ticket shop is currently in test mode. Please do not perform any real purchases as your order might be deleted without notice." %}
</strong>
</div>
{% else %}
<div class="alert alert-testmode alert-danger">
<strong>
{% trans "Orders made through this sales channel cannot be deleted - even if the ticket shop is in test mode!" %}
</strong>
</div>
{% endif %}
{% endif %}
{% endblock %}
{% block footer %}

View File

@@ -36,6 +36,14 @@
<div class="alert alert-info">
{{ p.provider.test_mode_message }}
</div>
{% if not request.event.sales_channel.testmode_supported %}
<div class="alert alert-danger">
{% trans "This sales channel does not provide support for testmode." %}
<strong>
{% trans "If you continue, you might pay an actual order with non-existing money!" %}
</strong>
</div>
{% endif %}
{% else %}
<div class="alert alert-warning">
{% trans "This payment provider does not provide support for testmode." %}

View File

@@ -11,6 +11,7 @@ from django.urls import resolve
from django.utils.translation import ugettext_lazy as _
from django_scopes import scope
from pretix.base.channels import WebshopSalesChannel
from pretix.base.middleware import LocaleMiddleware
from pretix.base.models import Event, Organizer
from pretix.multidomain.urlreverse import get_domain
@@ -103,7 +104,7 @@ def _detect_event(request, require_live=True, require_plugin=None):
if not hasattr(request, 'sales_channel'):
# The environ lookup is only relevant during unit testing
request.sales_channel = request.environ.get('PRETIX_SALES_CHANNEL', 'web')
request.sales_channel = request.environ.get('PRETIX_SALES_CHANNEL', WebshopSalesChannel())
for receiver, response in process_request.send(request.event, request=request):
if response:
return response

View File

@@ -401,7 +401,7 @@ class CartAdd(EventViewMixin, CartActionMixin, AsyncAction, View):
items = self._items_from_post_data()
if items:
return self.do(self.request.event.id, items, cart_id, translation.get_language(),
self.invoice_address.pk, widget_data, self.request.sales_channel)
self.invoice_address.pk, widget_data, self.request.sales_channel.identifier)
else:
if 'ajax' in self.request.GET or 'ajax' in self.request.POST:
return JsonResponse({
@@ -424,7 +424,7 @@ class RedeemView(NoSearchIndexViewMixin, EventViewMixin, TemplateView):
# Fetch all items
items, display_add_to_cart = get_grouped_items(self.request.event, self.subevent,
voucher=self.voucher, channel=self.request.sales_channel)
voucher=self.voucher, channel=self.request.sales_channel.identifier)
# Calculate how many options the user still has. If there is only one option, we can
# check the box right away ;)

View File

@@ -294,7 +294,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
if not self.request.event.has_subevents or self.subevent:
# Fetch all items
items, display_add_to_cart = get_grouped_items(self.request.event, self.subevent,
channel=self.request.sales_channel)
channel=self.request.sales_channel.identifier)
context['itemnum'] = len(items)
# Regroup those by category
@@ -335,7 +335,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
ebd = defaultdict(list)
add_subevents_for_days(
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel).using(settings.DATABASE_REPLICA), self.request),
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel.identifier).using(settings.DATABASE_REPLICA), self.request),
before, after, ebd, set(), self.request.event,
kwargs.get('cart_namespace')
)
@@ -345,7 +345,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
context['years'] = range(now().year - 2, now().year + 3)
else:
context['subevent_list'] = self.request.event.subevents_sorted(
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel).using(settings.DATABASE_REPLICA), self.request)
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel.identifier).using(settings.DATABASE_REPLICA), self.request)
)
context['show_cart'] = (

View File

@@ -393,7 +393,7 @@ class WidgetAPIProductList(EventListMixin, View):
else:
if hasattr(self.request, 'event'):
evs = self.request.event.subevents_sorted(
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel), self.request)
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel.identifier), self.request)
)
tz = pytz.timezone(request.event.settings.timezone)
data['events'] = [