mirror of
https://github.com/pretix/pretix.git
synced 2026-03-03 10:52:26 +00:00
Compare commits
10 Commits
datasync-l
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
959e926a67 | ||
|
|
876ddf1321 | ||
|
|
005b1d54d3 | ||
|
|
2066471086 | ||
|
|
a25bca7471 | ||
|
|
da43984ad2 | ||
|
|
7cce1c9219 | ||
|
|
cb9c4466f9 | ||
|
|
3398cda74b | ||
|
|
e5c8f19984 |
@@ -19,6 +19,7 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
# 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/>.
|
# <https://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from collections import Counter, defaultdict
|
from collections import Counter, defaultdict
|
||||||
@@ -1215,6 +1216,18 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
|
|||||||
raise ValidationError('The given payment provider is not known.')
|
raise ValidationError('The given payment provider is not known.')
|
||||||
return pp
|
return pp
|
||||||
|
|
||||||
|
def validate_payment_info(self, info):
|
||||||
|
if info:
|
||||||
|
try:
|
||||||
|
obj = json.loads(info)
|
||||||
|
except ValueError:
|
||||||
|
raise ValidationError('payment_info must be valid JSON.')
|
||||||
|
|
||||||
|
if not isinstance(obj, dict):
|
||||||
|
# only objects are allowed
|
||||||
|
raise ValidationError('payment_info must be a JSON object.')
|
||||||
|
return info
|
||||||
|
|
||||||
def validate_expires(self, expires):
|
def validate_expires(self, expires):
|
||||||
if expires < now():
|
if expires < now():
|
||||||
raise ValidationError('Expiration date must be in the future.')
|
raise ValidationError('Expiration date must be in the future.')
|
||||||
|
|||||||
@@ -365,9 +365,10 @@ class TeamInviteSerializer(serializers.ModelSerializer):
|
|||||||
def _send_invite(self, instance):
|
def _send_invite(self, instance):
|
||||||
mail(
|
mail(
|
||||||
instance.email,
|
instance.email,
|
||||||
_('pretix account invitation'),
|
_('Account invitation'),
|
||||||
'pretixcontrol/email/invitation.txt',
|
'pretixcontrol/email/invitation.txt',
|
||||||
{
|
{
|
||||||
|
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||||
'user': self,
|
'user': self,
|
||||||
'organizer': self.context['organizer'].name,
|
'organizer': self.context['organizer'].name,
|
||||||
'team': instance.team.name,
|
'team': instance.team.name,
|
||||||
|
|||||||
@@ -346,7 +346,8 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
|
|||||||
{
|
{
|
||||||
'user': self,
|
'user': self,
|
||||||
'messages': msg,
|
'messages': msg,
|
||||||
'url': build_absolute_uri('control:user.settings')
|
'url': build_absolute_uri('control:user.settings'),
|
||||||
|
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||||
},
|
},
|
||||||
event=None,
|
event=None,
|
||||||
user=self,
|
user=self,
|
||||||
@@ -391,6 +392,7 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
|
|||||||
'user': self,
|
'user': self,
|
||||||
'reason': msg,
|
'reason': msg,
|
||||||
'code': code,
|
'code': code,
|
||||||
|
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||||
},
|
},
|
||||||
event=None,
|
event=None,
|
||||||
user=self,
|
user=self,
|
||||||
@@ -430,6 +432,7 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
|
|||||||
mail(
|
mail(
|
||||||
self.email, _('Password recovery'), 'pretixcontrol/email/forgot.txt',
|
self.email, _('Password recovery'), 'pretixcontrol/email/forgot.txt',
|
||||||
{
|
{
|
||||||
|
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||||
'user': self,
|
'user': self,
|
||||||
'url': (build_absolute_uri('control:auth.forgot.recover')
|
'url': (build_absolute_uri('control:auth.forgot.recover')
|
||||||
+ '?id=%d&token=%s' % (self.id, default_token_generator.make_token(self)))
|
+ '?id=%d&token=%s' % (self.id, default_token_generator.make_token(self)))
|
||||||
|
|||||||
@@ -176,6 +176,7 @@ def shred(self, event: Event, fileid: str, confirm_code: str, user: int=None, lo
|
|||||||
_('Data shredding completed'),
|
_('Data shredding completed'),
|
||||||
'pretixbase/email/shred_completed.txt',
|
'pretixbase/email/shred_completed.txt',
|
||||||
{
|
{
|
||||||
|
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||||
'user': user,
|
'user': user,
|
||||||
'organizer': event.organizer.name,
|
'organizer': event.organizer.name,
|
||||||
'event': str(event.name),
|
'event': str(event.name),
|
||||||
|
|||||||
@@ -13,5 +13,5 @@ Start time: {{ start_time }} (new data added after this time might not have been
|
|||||||
|
|
||||||
Best regards,
|
Best regards,
|
||||||
|
|
||||||
Your pretix team
|
Your {{ instance }} team
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
|
|||||||
34
src/pretix/base/templatetags/anonymize_email.py
Normal file
34
src/pretix/base/templatetags/anonymize_email.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
#
|
||||||
|
from django import template
|
||||||
|
from django.utils.html import mark_safe
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter("anon_email")
|
||||||
|
def anon_email(value):
|
||||||
|
"""Replaces @ with [at] and . with [dot] for anonymization."""
|
||||||
|
if not isinstance(value, str):
|
||||||
|
return value
|
||||||
|
value = value.replace("@", "[at]").replace(".", "[dot]")
|
||||||
|
return mark_safe(''.join(['&#{0};'.format(ord(char)) for char in value]))
|
||||||
@@ -518,6 +518,7 @@ def pretixcontrol_orderposition_blocked_display(sender: Event, orderposition, bl
|
|||||||
'The order requires approval before it can continue to be processed.'),
|
'The order requires approval before it can continue to be processed.'),
|
||||||
'pretix.event.order.approved': _('The order has been approved.'),
|
'pretix.event.order.approved': _('The order has been approved.'),
|
||||||
'pretix.event.order.denied': _('The order has been denied (comment: "{comment}").'),
|
'pretix.event.order.denied': _('The order has been denied (comment: "{comment}").'),
|
||||||
|
'pretix.event.order.vatid.validated': _('The customer VAT ID has been verified.'),
|
||||||
'pretix.event.order.contact.changed': _('The email address has been changed from "{old_email}" '
|
'pretix.event.order.contact.changed': _('The email address has been changed from "{old_email}" '
|
||||||
'to "{new_email}".'),
|
'to "{new_email}".'),
|
||||||
'pretix.event.order.contact.confirmed': _(
|
'pretix.event.order.contact.confirmed': _(
|
||||||
|
|||||||
@@ -9,5 +9,5 @@ Please do never give this code to another person. Our support team will never as
|
|||||||
If this code was not requested by you, please contact us immediately.
|
If this code was not requested by you, please contact us immediately.
|
||||||
|
|
||||||
Best regards,
|
Best regards,
|
||||||
Your pretix team
|
Your {{ instance }} team
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ you requested a new password. Please go to the following page to reset your pass
|
|||||||
{{ url }}
|
{{ url }}
|
||||||
|
|
||||||
Best regards,
|
Best regards,
|
||||||
Your pretix team
|
Your {{ instance }} team
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{% load i18n %}{% blocktrans with url=url|safe %}Hello,
|
{% load i18n %}{% blocktrans with url=url|safe %}Hello,
|
||||||
|
|
||||||
you have been invited to a team on pretix, a platform to perform event
|
you have been invited to a team on {{ instance }}, a platform to perform event
|
||||||
ticket sales.
|
ticket sales.
|
||||||
|
|
||||||
Organizer: {{ organizer }}
|
Organizer: {{ organizer }}
|
||||||
@@ -13,5 +13,5 @@ If you do not want to join, you can safely ignore or delete this email.
|
|||||||
|
|
||||||
Best regards,
|
Best regards,
|
||||||
|
|
||||||
Your pretix team
|
Your {{ instance }} team
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{% load i18n %}{% blocktrans with url=url|safe messages=messages|safe %}Hello,
|
{% load i18n %}{% blocktrans with url=url|safe messages=messages|safe %}Hello,
|
||||||
|
|
||||||
this is to inform you that the account information of your pretix account has been
|
this is to inform you that the account information of your {{ instance }} account has been
|
||||||
changed. In particular, the following changes have been performed:
|
changed. In particular, the following changes have been performed:
|
||||||
|
|
||||||
{{ messages }}
|
{{ messages }}
|
||||||
@@ -12,5 +12,5 @@ You can review and change your account settings here:
|
|||||||
{{ url }}
|
{{ url }}
|
||||||
|
|
||||||
Best regards,
|
Best regards,
|
||||||
Your pretix team
|
Your {{ instance }} team
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
|
|||||||
@@ -1641,9 +1641,17 @@ class OrderCheckVATID(OrderView):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
normalized_id = validate_vat_id(ia.vat_id, str(ia.country))
|
normalized_id = validate_vat_id(ia.vat_id, str(ia.country))
|
||||||
ia.vat_id_validated = True
|
with transaction.atomic():
|
||||||
ia.vat_id = normalized_id
|
ia.vat_id_validated = True
|
||||||
ia.save()
|
ia.vat_id = normalized_id
|
||||||
|
ia.save()
|
||||||
|
self.order.log_action(
|
||||||
|
'pretix.event.order.vatid.validated',
|
||||||
|
data={
|
||||||
|
'vat_id': normalized_id,
|
||||||
|
},
|
||||||
|
user=self.request.user,
|
||||||
|
)
|
||||||
except VATIDFinalError as e:
|
except VATIDFinalError as e:
|
||||||
messages.error(self.request, e.message)
|
messages.error(self.request, e.message)
|
||||||
except VATIDTemporaryError:
|
except VATIDTemporaryError:
|
||||||
|
|||||||
@@ -1039,9 +1039,10 @@ class TeamMemberView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
|
|||||||
def _send_invite(self, instance):
|
def _send_invite(self, instance):
|
||||||
mail(
|
mail(
|
||||||
instance.email,
|
instance.email,
|
||||||
_('pretix account invitation'),
|
_('Account invitation'),
|
||||||
'pretixcontrol/email/invitation.txt',
|
'pretixcontrol/email/invitation.txt',
|
||||||
{
|
{
|
||||||
|
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||||
'user': self,
|
'user': self,
|
||||||
'organizer': self.request.organizer.name,
|
'organizer': self.request.organizer.name,
|
||||||
'team': instance.team.name,
|
'team': instance.team.name,
|
||||||
|
|||||||
@@ -802,31 +802,37 @@ class PaypalMethod(BasePaymentProvider):
|
|||||||
all_captures_completed = False
|
all_captures_completed = False
|
||||||
else:
|
else:
|
||||||
any_captures = True
|
any_captures = True
|
||||||
if not (any_captures and all_captures_completed):
|
|
||||||
|
# Payment has at least one capture, but it is not yet completed
|
||||||
|
if any_captures and not all_captures_completed:
|
||||||
messages.warning(request, _('PayPal has not yet approved the payment. We will inform you as '
|
messages.warning(request, _('PayPal has not yet approved the payment. We will inform you as '
|
||||||
'soon as the payment completed.'))
|
'soon as the payment completed.'))
|
||||||
payment.info = json.dumps(pp_captured_order.dict())
|
payment.info = json.dumps(pp_captured_order.dict())
|
||||||
payment.state = OrderPayment.PAYMENT_STATE_PENDING
|
payment.state = OrderPayment.PAYMENT_STATE_PENDING
|
||||||
payment.save()
|
payment.save()
|
||||||
return
|
return
|
||||||
|
# Payment has at least one capture and all captures are completed
|
||||||
|
elif any_captures and all_captures_completed:
|
||||||
|
if pp_captured_order.status != 'COMPLETED':
|
||||||
|
payment.fail(info=pp_captured_order.dict())
|
||||||
|
logger.error('Invalid state: %s' % repr(pp_captured_order.dict()))
|
||||||
|
raise PaymentException(
|
||||||
|
_('We were unable to process your payment. See below for details on how to proceed.')
|
||||||
|
)
|
||||||
|
|
||||||
if pp_captured_order.status != 'COMPLETED':
|
if payment.state == OrderPayment.PAYMENT_STATE_CONFIRMED:
|
||||||
payment.fail(info=pp_captured_order.dict())
|
logger.warning('PayPal success event even though order is already marked as paid')
|
||||||
logger.error('Invalid state: %s' % repr(pp_captured_order.dict()))
|
return
|
||||||
raise PaymentException(
|
|
||||||
_('We were unable to process your payment. See below for details on how to proceed.')
|
|
||||||
)
|
|
||||||
|
|
||||||
if payment.state == OrderPayment.PAYMENT_STATE_CONFIRMED:
|
try:
|
||||||
logger.warning('PayPal success event even though order is already marked as paid')
|
payment.info = json.dumps(pp_captured_order.dict())
|
||||||
|
payment.save(update_fields=['info'])
|
||||||
|
payment.confirm()
|
||||||
|
except Quota.QuotaExceededException as e:
|
||||||
|
raise PaymentException(str(e))
|
||||||
|
# Payment has not any captures yet - so it's probably in created status
|
||||||
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
|
||||||
payment.info = json.dumps(pp_captured_order.dict())
|
|
||||||
payment.save(update_fields=['info'])
|
|
||||||
payment.confirm()
|
|
||||||
except Quota.QuotaExceededException as e:
|
|
||||||
raise PaymentException(str(e))
|
|
||||||
finally:
|
finally:
|
||||||
if 'payment_paypal_oid' in request.session:
|
if 'payment_paypal_oid' in request.session:
|
||||||
del request.session['payment_paypal_oid']
|
del request.session['payment_paypal_oid']
|
||||||
@@ -836,7 +842,7 @@ class PaypalMethod(BasePaymentProvider):
|
|||||||
try:
|
try:
|
||||||
if (
|
if (
|
||||||
payment.info
|
payment.info
|
||||||
and payment.info_data['purchase_units'][0]['payments']['captures'][0]['status'] == 'pending'
|
and payment.info_data['purchase_units'][0]['payments']['captures'][0]['status'] == 'PENDING'
|
||||||
):
|
):
|
||||||
retry = False
|
retry = False
|
||||||
except (KeyError, IndexError):
|
except (KeyError, IndexError):
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
{% load eventurl %}
|
{% load eventurl %}
|
||||||
{% load safelink %}
|
{% load safelink %}
|
||||||
{% load rich_text %}
|
{% load rich_text %}
|
||||||
|
{% load anonymize_email %}
|
||||||
{% block thetitle %}
|
{% block thetitle %}
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
{{ messages|join:" " }} ::
|
{{ messages|join:" " }} ::
|
||||||
@@ -219,7 +220,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block footernav %}
|
{% block footernav %}
|
||||||
{% if request.event.settings.contact_mail %}
|
{% if request.event.settings.contact_mail %}
|
||||||
<li><a href="mailto:{{ request.event.settings.contact_mail }}" target="_blank" rel="noopener">{% trans "Contact" %}</a></li>
|
<li><a href="{{ 'mailto:'|add:request.event.settings.contact_mail|anon_email }}" target="_blank" rel="noopener">{% trans "Contact" %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if request.event.settings.privacy_url %}
|
{% if request.event.settings.privacy_url %}
|
||||||
<li><a href="{% safelink request.event.settings.privacy_url %}" target="_blank" rel="noopener">{% trans "Privacy policy" %}</a></li>
|
<li><a href="{% safelink request.event.settings.privacy_url %}" target="_blank" rel="noopener">{% trans "Privacy policy" %}</a></li>
|
||||||
|
|||||||
@@ -21,4 +21,5 @@
|
|||||||
<script type="text/javascript" src="{% static "pretixpresale/js/ui/cart.js" %}"></script>
|
<script type="text/javascript" src="{% static "pretixpresale/js/ui/cart.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "pretixpresale/js/ui/iframe.js" %}"></script>
|
<script type="text/javascript" src="{% static "pretixpresale/js/ui/iframe.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "pretixbase/js/addressform.js" %}"></script>
|
<script type="text/javascript" src="{% static "pretixbase/js/addressform.js" %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static "pretixbase/js/deanonymize_email.js" %}"></script>
|
||||||
{% endcompress %}
|
{% endcompress %}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
{% load thumb %}
|
{% load thumb %}
|
||||||
{% load eventurl %}
|
{% load eventurl %}
|
||||||
{% load safelink %}
|
{% load safelink %}
|
||||||
|
{% load anonymize_email %}
|
||||||
{% block thetitle %}
|
{% block thetitle %}
|
||||||
{% block title %}{% endblock %}{% if url_name != "organizer.index" %} :: {% endif %}{{ organizer.name }}
|
{% block title %}{% endblock %}{% if url_name != "organizer.index" %} :: {% endif %}{{ organizer.name }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -97,7 +98,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block footernav %}
|
{% block footernav %}
|
||||||
{% if not request.event and request.organizer.settings.contact_mail %}
|
{% if not request.event and request.organizer.settings.contact_mail %}
|
||||||
<li><a href="mailto:{{ request.organizer.settings.contact_mail }}" target="_blank" rel="noopener">{% trans "Contact" %}</a></li>
|
<li><a href="{{ 'mailto:'|add:request.organizer.settings.contact_mail|anon_email }}" target="_blank" rel="noopener">{% trans "Contact" %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not request.event and request.organizer.settings.privacy_url %}
|
{% if not request.event and request.organizer.settings.privacy_url %}
|
||||||
<li><a href="{% safelink request.organizer.settings.privacy_url %}" target="_blank" rel="noopener">{% trans "Privacy policy" %}</a></li>
|
<li><a href="{% safelink request.organizer.settings.privacy_url %}" target="_blank" rel="noopener">{% trans "Privacy policy" %}</a></li>
|
||||||
|
|||||||
7
src/pretix/static/pretixbase/js/deanonymize_email.js
Normal file
7
src/pretix/static/pretixbase/js/deanonymize_email.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
document.querySelectorAll('a[href^="mailto:"]').forEach(function(link) {
|
||||||
|
// Replace [at] with @ and the [dot] with . in both the href and the displayed text (if needed)
|
||||||
|
link.href = link.href.replace('[at]', '@').replace('[dot]', '.');
|
||||||
|
link.textContent = link.textContent.replace('[at]', '@').replace('[dot]', '.');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -895,6 +895,41 @@ def test_order_create_payment_info_optional(token_client, organizer, event, item
|
|||||||
assert json.loads(p.info) == res['payment_info']
|
assert json.loads(p.info) == res['payment_info']
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_order_create_payment_info_valid_object(token_client, organizer, event, item, quota, question):
|
||||||
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
||||||
|
res['positions'][0]['item'] = item.pk
|
||||||
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
||||||
|
|
||||||
|
res["payment_info"] = [{"should": "fail"}]
|
||||||
|
resp = token_client.post(
|
||||||
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
||||||
|
organizer.slug, event.slug
|
||||||
|
), format='json', data=res
|
||||||
|
)
|
||||||
|
assert resp.status_code == 400
|
||||||
|
|
||||||
|
res['payment_info'] = {
|
||||||
|
'foo': {
|
||||||
|
'bar': [1, 2],
|
||||||
|
'test': False
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resp = token_client.post(
|
||||||
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
||||||
|
organizer.slug, event.slug
|
||||||
|
), format='json', data=res
|
||||||
|
)
|
||||||
|
assert resp.status_code == 201
|
||||||
|
with scopes_disabled():
|
||||||
|
o = Order.objects.get(code=resp.data['code'])
|
||||||
|
|
||||||
|
p = o.payments.first()
|
||||||
|
assert p.provider == "banktransfer"
|
||||||
|
assert p.amount == o.total
|
||||||
|
assert json.loads(p.info) == res['payment_info']
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_order_create_position_secret_optional(token_client, organizer, event, item, quota, question):
|
def test_order_create_position_secret_optional(token_client, organizer, event, item, quota, question):
|
||||||
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
||||||
|
|||||||
Reference in New Issue
Block a user