forked from CGM_Public/pretix_original
Compare commits
1 Commits
loadtest
...
searchinde
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
173cf751da |
@@ -129,7 +129,7 @@ Database replica settings
|
||||
-------------------------
|
||||
|
||||
If you use a replicated database setup, pretix expects that the default database connection always points to the primary database node.
|
||||
Routing read queries to a replica on database layer is **strongly** discouraged since this can lead to inaccurate such as more tickets
|
||||
Routing read queries to a replica on databse layer is **strongly** discouraged since this can lead to inaccurate such as more tickets
|
||||
being sold than are actually available.
|
||||
|
||||
However, pretix can still make use of a database replica to keep some expensive queries with that can tolerate some latency from your
|
||||
|
||||
@@ -15,7 +15,6 @@ name multi-lingual string The event's ful
|
||||
slug string A short form of the name, used e.g. in URLs.
|
||||
live boolean If ``true``, the event ticket shop is publicly
|
||||
available.
|
||||
testmode boolean If ``true``, the ticket shop is in test mode.
|
||||
currency string The currency this event is handled in.
|
||||
date_from datetime The event's start date
|
||||
date_to datetime The event's end date (or ``null``)
|
||||
@@ -46,10 +45,6 @@ plugins list A list of packa
|
||||
|
||||
Filters have been added to the list of events.
|
||||
|
||||
.. versionchanged:: 2.5
|
||||
|
||||
The ``testmode`` attribute has been added.
|
||||
|
||||
Endpoints
|
||||
---------
|
||||
|
||||
@@ -84,7 +79,6 @@ Endpoints
|
||||
"name": {"en": "Sample Conference"},
|
||||
"slug": "sampleconf",
|
||||
"live": false,
|
||||
"testmode": false,
|
||||
"currency": "EUR",
|
||||
"date_from": "2017-12-27T10:00:00Z",
|
||||
"date_to": null,
|
||||
@@ -143,7 +137,6 @@ Endpoints
|
||||
"name": {"en": "Sample Conference"},
|
||||
"slug": "sampleconf",
|
||||
"live": false,
|
||||
"testmode": false,
|
||||
"currency": "EUR",
|
||||
"date_from": "2017-12-27T10:00:00Z",
|
||||
"date_to": null,
|
||||
@@ -190,7 +183,6 @@ Endpoints
|
||||
"name": {"en": "Sample Conference"},
|
||||
"slug": "sampleconf",
|
||||
"live": false,
|
||||
"testmode": false,
|
||||
"currency": "EUR",
|
||||
"date_from": "2017-12-27T10:00:00Z",
|
||||
"date_to": null,
|
||||
@@ -219,7 +211,6 @@ Endpoints
|
||||
"name": {"en": "Sample Conference"},
|
||||
"slug": "sampleconf",
|
||||
"live": false,
|
||||
"testmode": false,
|
||||
"currency": "EUR",
|
||||
"date_from": "2017-12-27T10:00:00Z",
|
||||
"date_to": null,
|
||||
@@ -268,7 +259,6 @@ Endpoints
|
||||
"name": {"en": "Sample Conference"},
|
||||
"slug": "sampleconf",
|
||||
"live": false,
|
||||
"testmode": false,
|
||||
"currency": "EUR",
|
||||
"date_from": "2017-12-27T10:00:00Z",
|
||||
"date_to": null,
|
||||
@@ -297,7 +287,6 @@ Endpoints
|
||||
"name": {"en": "Sample Conference"},
|
||||
"slug": "sampleconf",
|
||||
"live": false,
|
||||
"testmode": false,
|
||||
"currency": "EUR",
|
||||
"date_from": "2017-12-27T10:00:00Z",
|
||||
"date_to": null,
|
||||
@@ -358,7 +347,6 @@ Endpoints
|
||||
"name": {"en": "Sample Conference"},
|
||||
"slug": "sampleconf",
|
||||
"live": false,
|
||||
"testmode": false,
|
||||
"currency": "EUR",
|
||||
"date_from": "2017-12-27T10:00:00Z",
|
||||
"date_to": null,
|
||||
|
||||
@@ -26,8 +26,6 @@ status string Order status, o
|
||||
* ``p`` – paid
|
||||
* ``e`` – expired
|
||||
* ``c`` – canceled
|
||||
testmode boolean If ``True``, this order was created when the event was in
|
||||
test mode. Only orders in test mode can be deleted.
|
||||
secret string The secret contained in the link sent to the customer
|
||||
email string The customer email address
|
||||
locale string The locale used for communication with this customer
|
||||
@@ -133,10 +131,6 @@ last_modified datetime Last modificati
|
||||
``order.status`` can no longer be ``r``, ``…/mark_canceled/`` now accepts a ``cancellation_fee`` parameter and
|
||||
``…/mark_refunded/`` has been deprecated.
|
||||
|
||||
.. versionchanged:: 2.5:
|
||||
|
||||
The ``testmode`` attribute has been added and ``DELETE`` has been implemented for orders.
|
||||
|
||||
.. _order-position-resource:
|
||||
|
||||
Order position resource
|
||||
@@ -278,7 +272,6 @@ List of all orders
|
||||
{
|
||||
"code": "ABC12",
|
||||
"status": "p",
|
||||
"testmode": false,
|
||||
"secret": "k24fiuwvu8kxz3y1",
|
||||
"email": "tester@example.org",
|
||||
"locale": "en",
|
||||
@@ -377,14 +370,11 @@ List of all orders
|
||||
``status``. Default: ``datetime``
|
||||
:query string code: Only return orders that match the given order code
|
||||
:query string status: Only return orders in the given order status (see above)
|
||||
:query boolean testmode: Only return orders with ``testmode`` set to ``true`` or ``false``
|
||||
:query boolean require_approval: If set to ``true`` or ``false``, only categories with this value for the field
|
||||
``require_approval`` will be returned.
|
||||
:query string email: Only return orders created with the given email address
|
||||
:query string locale: Only return orders with the given customer locale
|
||||
:query datetime modified_since: Only return orders that have changed since the given date. Be careful: We only
|
||||
recommend using this in combination with ``testmode=false``, since test mode orders can vanish at any time and
|
||||
you will not notice it using this method.
|
||||
:query datetime modified_since: Only return orders that have changed since the given date
|
||||
:param organizer: The ``slug`` field of the organizer to fetch
|
||||
:param event: The ``slug`` field of the event to fetch
|
||||
:resheader X-Page-Generated: The server time at the beginning of the operation. If you're using this API to fetch
|
||||
@@ -419,7 +409,6 @@ Fetching individual orders
|
||||
{
|
||||
"code": "ABC12",
|
||||
"status": "p",
|
||||
"testmode": false,
|
||||
"secret": "k24fiuwvu8kxz3y1",
|
||||
"email": "tester@example.org",
|
||||
"locale": "en",
|
||||
@@ -563,37 +552,6 @@ Order ticket download
|
||||
:statuscode 409: The file is not yet ready and will now be prepared. Retry the request after waiting for a few
|
||||
seconds.
|
||||
|
||||
Deleting orders
|
||||
---------------
|
||||
|
||||
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/orders/(code)/
|
||||
|
||||
Deletes an order. Works only if the order has ``testmode`` set to ``true``.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
DELETE /api/v1/organizers/bigevents/events/sampleconf/orders/ABC12/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 204 No Content
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to fetch
|
||||
:param event: The ``slug`` field of the event to fetch
|
||||
:param code: The ``code`` field of the order to delete
|
||||
:statuscode 204: no error
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to delete this resource **or** the order may not be deleted.
|
||||
:statuscode 404: The requested order does not exist.
|
||||
|
||||
Creating orders
|
||||
---------------
|
||||
|
||||
@@ -648,7 +606,6 @@ Creating orders
|
||||
or in state ``confirmed``, depending on this value. If you create a paid order, the ``order_paid`` signal will
|
||||
**not** be sent out to plugins and no email will be sent. If you want that behavior, create an unpaid order and
|
||||
then call the ``mark_paid`` API method.
|
||||
* ``testmode`` (optional) – Defaults to ``false``
|
||||
* ``consume_carts`` (optional) – A list of cart IDs. All cart positions with these IDs will be deleted if the
|
||||
order creation is successful. Any quotas that become free by this operation will be credited to your order
|
||||
creation.
|
||||
|
||||
@@ -114,8 +114,6 @@ The provider class
|
||||
|
||||
.. autoattribute:: is_meta
|
||||
|
||||
.. autoattribute:: test_mode_message
|
||||
|
||||
|
||||
Additional views
|
||||
----------------
|
||||
|
||||
@@ -82,12 +82,6 @@ Orders
|
||||
^^^^^^
|
||||
|
||||
If a customer completes the checkout process, an **Order** will be created containing all the entered information.
|
||||
An order can be in one of currently four states that are listed in the diagram below:
|
||||
An order can be in one of currently five states that are listed in the diagram below:
|
||||
|
||||
.. image:: /images/order_states.png
|
||||
|
||||
There are additional "fake" states that are displayed like states but not represented as states in the system:
|
||||
|
||||
* An order is considered **canceled (with paid fee)** if it is in **paid** status but does not include any non-cancelled positions.
|
||||
|
||||
* An order is considered **requiring approval** if it is in **pending** status with the ``require_approval`` attribute set to ``True``.
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 42 KiB |
@@ -4,6 +4,7 @@ Pending: Order is expecting payment\nOrder reduces quotas
|
||||
Expired: Payment period is over\nOrder does not affect quotas
|
||||
Paid: Order was successful\nOrder reduces quotas
|
||||
Canceled: Order has been canceled\nOrder does not affect quotas
|
||||
Refunded: Order has been refunded\nOrder does not affect quotas
|
||||
|
||||
[*] --> Pending: customer\nplaces order
|
||||
Pending --> Paid: successful payment
|
||||
@@ -11,9 +12,8 @@ Pending --> Expired: automatically\nor manually\non admin action
|
||||
Expired --> Paid: if payment is received\nonly if quota left
|
||||
Expired --> Canceled
|
||||
Expired --> Pending: manually\non admin action
|
||||
Paid --> Canceled: manually on\nadmin action\nor if an external\npayment provider\nnotifies about a\npayment refund
|
||||
Paid --> Refunded: manually on\nadmin action\nor if an external\npayment provider\nnotifies about a\npayment refund
|
||||
Pending --> Canceled: on admin or\ncustomer action
|
||||
Paid -> Pending: manually on admin action
|
||||
[*] --> Paid: customer\nplaces free order
|
||||
|
||||
@enduml
|
||||
|
||||
@@ -111,7 +111,6 @@ submodule
|
||||
subpath
|
||||
Symfony
|
||||
systemd
|
||||
testmode
|
||||
testutils
|
||||
timestamp
|
||||
tuples
|
||||
|
||||
@@ -4,10 +4,22 @@ FAQ and Troubleshooting
|
||||
How can I test my shop before taking it live?
|
||||
---------------------------------------------
|
||||
|
||||
On your event dashboard, click on the first tile that shows your shop status. On the lower part of this page, you can
|
||||
place your event into "test mode". In "test mode", everything behaves the same, but orders created during test mode can
|
||||
later be fully deleted. Be sure to actually delete them when or after you turn off test mode, since test mode orders
|
||||
still count toward your quotas and are included in your reports.
|
||||
There are multiple ways to do this.
|
||||
|
||||
First, you could just create some orders in your real shop and cancel/refund them later. If you don't want to process
|
||||
real payments for the tests, you can either use a "manual" payment method like bank transfer and just mark the orders
|
||||
as paid with the button in the backend, or if you want to use e.g. Stripe, you can configure pretix to use your keys
|
||||
for the Stripe test system and use their test credit cars. Read our :ref:`Stripe documentation <stripe>` for more
|
||||
information.
|
||||
|
||||
Second, you could create a separate event, just for testing. In the last step of the :ref:`event creation process <event_create>`,
|
||||
you can specify that you want to copy all settings from your real event, so you don't have to do all of it twice.
|
||||
|
||||
We are planning to add a dedicated test mode in a later version of pretix.
|
||||
|
||||
If you are using the hosted service at pretix.eu and want to get rid of the test orders completely, contact us at
|
||||
support@pretix.eu and we can remove them for you. Please note that we only are able to do that *before* you have
|
||||
received any real orders (i.e. taken the shop public). We won't charge any fees for test orders or test events.
|
||||
|
||||
How do I delete an event?
|
||||
-------------------------
|
||||
|
||||
@@ -48,7 +48,7 @@ class EventSerializer(I18nAwareModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = ('name', 'slug', 'live', 'testmode', 'currency', 'date_from',
|
||||
fields = ('name', 'slug', 'live', 'currency', 'date_from',
|
||||
'date_to', 'date_admission', 'is_public', 'presale_start',
|
||||
'presale_end', 'location', 'has_subevents', 'meta_data', 'plugins')
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ from rest_framework.reverse import reverse
|
||||
|
||||
from pretix.api.serializers.i18n import I18nAwareModelSerializer
|
||||
from pretix.base.channels import get_all_sales_channels
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import (
|
||||
Checkin, Invoice, InvoiceAddress, InvoiceLine, Order, OrderPosition,
|
||||
Question, QuestionAnswer,
|
||||
@@ -141,21 +140,20 @@ class PdfDataSerializer(serializers.Field):
|
||||
res = {}
|
||||
|
||||
ev = instance.subevent or instance.order.event
|
||||
with language(instance.order.locale):
|
||||
# This needs to have some extra performance improvements to avoid creating hundreds of queries when
|
||||
# we serialize a list.
|
||||
# This needs to have some extra performance improvements to avoid creating hundreds of queries when
|
||||
# we serialize a list.
|
||||
|
||||
if 'vars' not in self.context:
|
||||
self.context['vars'] = get_variables(self.context['request'].event)
|
||||
if 'vars' not in self.context:
|
||||
self.context['vars'] = get_variables(self.context['request'].event)
|
||||
|
||||
for k, f in self.context['vars'].items():
|
||||
res[k] = f['evaluate'](instance, instance.order, ev)
|
||||
for k, f in self.context['vars'].items():
|
||||
res[k] = f['evaluate'](instance, instance.order, ev)
|
||||
|
||||
if not hasattr(ev, '_cached_meta_data'):
|
||||
ev._cached_meta_data = ev.meta_data
|
||||
if not hasattr(ev, '_cached_meta_data'):
|
||||
ev._cached_meta_data = ev.meta_data
|
||||
|
||||
for k, v in ev._cached_meta_data.items():
|
||||
res['meta:' + k] = v
|
||||
for k, v in ev._cached_meta_data.items():
|
||||
res['meta:' + k] = v
|
||||
|
||||
return res
|
||||
|
||||
@@ -231,7 +229,7 @@ class OrderSerializer(I18nAwareModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Order
|
||||
fields = ('code', 'status', 'testmode', 'secret', 'email', 'locale', 'datetime', 'expires', 'payment_date',
|
||||
fields = ('code', 'status', 'secret', 'email', 'locale', 'datetime', 'expires', 'payment_date',
|
||||
'payment_provider', 'fees', 'total', 'comment', 'invoice_address', 'positions', 'downloads',
|
||||
'checkin_attention', 'last_modified', 'payments', 'refunds', 'require_approval', 'sales_channel')
|
||||
|
||||
@@ -413,7 +411,7 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Order
|
||||
fields = ('code', 'status', 'testmode', 'email', 'locale', 'payment_provider', 'fees', 'comment', 'sales_channel',
|
||||
fields = ('code', 'status', 'email', 'locale', 'payment_provider', 'fees', 'comment', 'sales_channel',
|
||||
'invoice_address', 'positions', 'checkin_attention', 'payment_info', 'consume_carts')
|
||||
|
||||
def validate_payment_provider(self, pp):
|
||||
|
||||
@@ -16,7 +16,7 @@ from rest_framework.exceptions import (
|
||||
APIException, NotFound, PermissionDenied, ValidationError,
|
||||
)
|
||||
from rest_framework.filters import OrderingFilter
|
||||
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin
|
||||
from rest_framework.mixins import CreateModelMixin
|
||||
from rest_framework.response import Response
|
||||
|
||||
from pretix.api.models import OAuthAccessToken
|
||||
@@ -51,10 +51,10 @@ class OrderFilter(FilterSet):
|
||||
|
||||
class Meta:
|
||||
model = Order
|
||||
fields = ['code', 'status', 'email', 'locale', 'testmode', 'require_approval']
|
||||
fields = ['code', 'status', 'email', 'locale', 'require_approval']
|
||||
|
||||
|
||||
class OrderViewSet(DestroyModelMixin, CreateModelMixin, viewsets.ReadOnlyModelViewSet):
|
||||
class OrderViewSet(CreateModelMixin, viewsets.ReadOnlyModelViewSet):
|
||||
serializer_class = OrderSerializer
|
||||
queryset = Order.objects.none()
|
||||
filter_backends = (DjangoFilterBackend, OrderingFilter)
|
||||
@@ -378,13 +378,6 @@ class OrderViewSet(DestroyModelMixin, CreateModelMixin, viewsets.ReadOnlyModelVi
|
||||
def perform_create(self, serializer):
|
||||
serializer.save()
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
if not instance.testmode:
|
||||
raise PermissionDenied('Only test mode orders can be deleted.')
|
||||
|
||||
with transaction.atomic():
|
||||
self.get_object().gracefully_delete(user=self.request.user if self.request.user.is_authenticated else None, auth=self.request.auth)
|
||||
|
||||
|
||||
class OrderPositionFilter(FilterSet):
|
||||
order = django_filters.CharFilter(field_name='order', lookup_expr='code__iexact')
|
||||
|
||||
@@ -113,7 +113,7 @@ def register_default_webhook_events(sender, **kwargs):
|
||||
_('New order placed'),
|
||||
),
|
||||
ParametrizedOrderWebhookEvent(
|
||||
'pretix.event.order.placed.require_approval',
|
||||
'pretix.event.order.placed.required_approval',
|
||||
_('New order requires approval'),
|
||||
),
|
||||
ParametrizedOrderWebhookEvent(
|
||||
|
||||
@@ -27,7 +27,7 @@ class CustomSMTPBackend(EmailBackend):
|
||||
if code != 250:
|
||||
logger.warn('Error testing mail settings, code %d, resp: %s' % (code, resp))
|
||||
raise SMTPResponseException(code, resp)
|
||||
(code, resp) = self.connection.rcpt('testdummy@pretix.eu')
|
||||
(code, resp) = self.connection.rcpt('test@example.com')
|
||||
if (code != 250) and (code != 251):
|
||||
logger.warn('Error testing mail settings, code %d, resp: %s' % (code, resp))
|
||||
raise SMTPResponseException(code, resp)
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import logging
|
||||
|
||||
import i18nfield.forms
|
||||
from django import forms
|
||||
from django.forms.models import ModelFormMetaclass
|
||||
from django.utils import six
|
||||
from django.utils.crypto import get_random_string
|
||||
from formtools.wizard.views import SessionWizardView
|
||||
from hierarkey.forms import HierarkeyForm
|
||||
|
||||
from pretix.base.models import Event
|
||||
@@ -73,29 +71,3 @@ class SettingsForm(i18nfield.forms.I18nFormMixin, HierarkeyForm):
|
||||
fname = '%s/%s.%s.%s' % (self.obj.slug, name, nonce, name.split('.')[-1])
|
||||
# TODO: make sure pub is always correct
|
||||
return 'pub/' + fname
|
||||
|
||||
|
||||
class PrefixForm(forms.Form):
|
||||
prefix = forms.CharField(widget=forms.HiddenInput)
|
||||
|
||||
|
||||
class SafeSessionWizardView(SessionWizardView):
|
||||
def get_prefix(self, request, *args, **kwargs):
|
||||
if hasattr(request, '_session_wizard_prefix'):
|
||||
return request._session_wizard_prefix
|
||||
prefix_form = PrefixForm(self.request.POST, prefix=super().get_prefix(request, *args, **kwargs))
|
||||
if not prefix_form.is_valid():
|
||||
request._session_wizard_prefix = get_random_string(length=24)
|
||||
else:
|
||||
request._session_wizard_prefix = prefix_form.cleaned_data['prefix']
|
||||
return request._session_wizard_prefix
|
||||
|
||||
def get_context_data(self, form, **kwargs):
|
||||
context = super().get_context_data(form=form, **kwargs)
|
||||
context['wizard']['prefix_form'] = PrefixForm(
|
||||
prefix=super().get_prefix(self.request),
|
||||
initial={
|
||||
'prefix': self.get_prefix(self.request)
|
||||
}
|
||||
)
|
||||
return context
|
||||
|
||||
@@ -9,7 +9,7 @@ import vat_moss.exchange_rates
|
||||
from django.contrib.staticfiles import finders
|
||||
from django.dispatch import receiver
|
||||
from django.utils.formats import date_format, localize
|
||||
from django.utils.translation import pgettext, ugettext
|
||||
from django.utils.translation import pgettext
|
||||
from PIL.Image import BICUBIC
|
||||
from reportlab.lib import pagesizes
|
||||
from reportlab.lib.enums import TA_LEFT
|
||||
@@ -267,13 +267,6 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
canvas.saveState()
|
||||
canvas.setFont(self.font_regular, 8)
|
||||
|
||||
if self.invoice.order.testmode:
|
||||
canvas.saveState()
|
||||
canvas.setFont('OpenSansBd', 30)
|
||||
canvas.setFillColorRGB(32, 0, 0)
|
||||
canvas.drawRightString(self.pagesize[0] - 20 * mm, (297 - 100) * mm, ugettext('TEST MODE'))
|
||||
canvas.restoreState()
|
||||
|
||||
for i, line in enumerate(self.invoice.footer_text.split('\n')[::-1]):
|
||||
canvas.drawCentredString(self.pagesize[0] / 2, 25 + (3.5 * i) * mm, line.strip())
|
||||
|
||||
|
||||
30
src/pretix/base/management/commands/rebuild_search_index.py
Normal file
30
src/pretix/base/management/commands/rebuild_search_index.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.core.paginator import Paginator
|
||||
|
||||
from pretix.base.models import Order, OrderSearchIndex
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Rebuild search index"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--clean', action='store_true', dest='clean',
|
||||
help="Clear search index before run.",
|
||||
)
|
||||
|
||||
def iter_pages(self, qs):
|
||||
paginator = Paginator(qs, 500)
|
||||
for index in range(paginator.num_pages):
|
||||
yield paginator.get_page(index + 1)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
if options.get('clean'):
|
||||
OrderSearchIndex.objects.all().delete()
|
||||
qs = Order.objects.select_related('event', 'event__organizer', 'invoice_address').prefetch_related('all_positions', 'payments')
|
||||
for page in self.iter_pages(qs):
|
||||
if options.get('clean'):
|
||||
OrderSearchIndex.objects.bulk_create([o.index() for o in page])
|
||||
else:
|
||||
for o in page:
|
||||
o.index()
|
||||
@@ -129,22 +129,13 @@ def get_language_from_request(request: HttpRequest) -> str:
|
||||
if _supported is None:
|
||||
_supported = OrderedDict(settings.LANGUAGES)
|
||||
|
||||
if request.path.startswith(get_script_prefix() + 'control'):
|
||||
return (
|
||||
get_language_from_user_settings(request)
|
||||
or get_language_from_session_or_cookie(request)
|
||||
or get_language_from_browser(request)
|
||||
or get_language_from_event(request)
|
||||
or get_default_language()
|
||||
)
|
||||
else:
|
||||
return (
|
||||
get_language_from_session_or_cookie(request)
|
||||
or get_language_from_user_settings(request)
|
||||
or get_language_from_browser(request)
|
||||
or get_language_from_event(request)
|
||||
or get_default_language()
|
||||
)
|
||||
return (
|
||||
get_language_from_user_settings(request)
|
||||
or get_language_from_session_or_cookie(request)
|
||||
or get_language_from_browser(request)
|
||||
or get_language_from_event(request)
|
||||
or get_default_language()
|
||||
)
|
||||
|
||||
|
||||
def _parse_csp(header):
|
||||
|
||||
49
src/pretix/base/migrations/0110_auto_20190214_1041.py
Normal file
49
src/pretix/base/migrations/0110_auto_20190214_1041.py
Normal file
@@ -0,0 +1,49 @@
|
||||
# Generated by Django 2.1 on 2019-02-14 10:41
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def create_extension(apps, schema_editor):
|
||||
if 'postgresql' in schema_editor.connection.settings_dict['ENGINE']:
|
||||
schema_editor.execute("CREATE EXTENSION IF NOT EXISTS pg_trgm;")
|
||||
|
||||
|
||||
def create_trigram_index(apps, schema_editor):
|
||||
if 'postgresql' in schema_editor.connection.settings_dict['ENGINE']:
|
||||
schema_editor.execute("CREATE INDEX pretixbase_ordersearchindex_s ON pretixbase_ordersearchindex USING gin (search_body gin_trgm_ops);")
|
||||
schema_editor.execute("CREATE INDEX pretixbase_ordersearchindex_spp ON pretixbase_ordersearchindex USING gin (payment_providers gin_trgm_ops);")
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('pretixbase', '0109_auto_20190208_1432'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(create_extension),
|
||||
migrations.CreateModel(
|
||||
name='OrderSearchIndex',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('search_body', models.TextField()),
|
||||
('payment_providers', models.TextField()),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ordersearchindex',
|
||||
name='event',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pretixbase.Event'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ordersearchindex',
|
||||
name='order',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pretixbase.Order', unique=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ordersearchindex',
|
||||
name='organizer',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pretixbase.Organizer'),
|
||||
),
|
||||
migrations.RunPython(create_trigram_index)
|
||||
]
|
||||
@@ -1,23 +0,0 @@
|
||||
# Generated by Django 2.1.5 on 2019-02-19 12:45
|
||||
|
||||
import django.db.models.deletion
|
||||
import jsonfallback.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
import pretix.base.models.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0109_auto_20190208_1432'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='plugins',
|
||||
field=models.TextField(blank=True, default='', verbose_name='Plugins'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
@@ -1,27 +0,0 @@
|
||||
# Generated by Django 2.1.5 on 2019-02-19 09:49
|
||||
|
||||
import django.db.models.deletion
|
||||
import jsonfallback.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
import pretix.base.models.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0110_auto_20190219_1245'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='testmode',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='testmode',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
||||
@@ -7,6 +7,7 @@ from .event import (
|
||||
Event, Event_SettingsStore, EventLock, EventMetaProperty, EventMetaValue,
|
||||
RequiredAction, SubEvent, SubEventMetaValue, generate_invite_token,
|
||||
)
|
||||
from .index import OrderSearchIndex
|
||||
from .invoices import Invoice, InvoiceLine, invoice_filename
|
||||
from .items import (
|
||||
Item, ItemAddOn, ItemCategory, ItemVariation, Question, QuestionOption,
|
||||
|
||||
@@ -77,13 +77,6 @@ class LoggingMixin:
|
||||
|
||||
logentry = LogEntry(content_object=self, user=user, action_type=action, event=event, **kwargs)
|
||||
if isinstance(data, dict):
|
||||
sensitivekeys = ['password', 'secret', 'api_key']
|
||||
|
||||
for sensitivekey in sensitivekeys:
|
||||
for k, v in data.items():
|
||||
if (sensitivekey in k) and v:
|
||||
data[k] = "********"
|
||||
|
||||
logentry.data = json.dumps(data, cls=CustomJSONEncoder)
|
||||
elif data:
|
||||
raise TypeError("You should only supply dictionaries as log data.")
|
||||
|
||||
@@ -242,8 +242,6 @@ class Event(EventMixin, LoggedModel):
|
||||
|
||||
:param organizer: The organizer this event belongs to
|
||||
:type organizer: Organizer
|
||||
:param testmode: This event is in test mode
|
||||
:type testmode: bool
|
||||
:param name: This event's full title
|
||||
:type name: str
|
||||
:param slug: A short, alphanumeric, all-lowercase name for use in URLs. The slug has to
|
||||
@@ -273,7 +271,6 @@ class Event(EventMixin, LoggedModel):
|
||||
settings_namespace = 'event'
|
||||
CURRENCY_CHOICES = [(c.alpha_3, c.alpha_3 + " - " + c.name) for c in settings.CURRENCIES]
|
||||
organizer = models.ForeignKey(Organizer, related_name="events", on_delete=models.PROTECT)
|
||||
testmode = models.BooleanField(default=False)
|
||||
name = I18nCharField(
|
||||
max_length=200,
|
||||
verbose_name=_("Event name"),
|
||||
@@ -324,7 +321,7 @@ class Event(EventMixin, LoggedModel):
|
||||
verbose_name=_("Location"),
|
||||
)
|
||||
plugins = models.TextField(
|
||||
null=False, blank=True,
|
||||
null=True, blank=True,
|
||||
verbose_name=_("Plugins"),
|
||||
)
|
||||
comment = models.TextField(
|
||||
|
||||
11
src/pretix/base/models/index.py
Normal file
11
src/pretix/base/models/index.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from django.db import models
|
||||
|
||||
DELIMITER = "\x1F"
|
||||
|
||||
|
||||
class OrderSearchIndex(models.Model):
|
||||
order = models.ForeignKey('Order', unique=True, null=False, on_delete=models.CASCADE)
|
||||
event = models.ForeignKey('Event', null=False, on_delete=models.CASCADE)
|
||||
organizer = models.ForeignKey('Organizer', null=False, on_delete=models.CASCADE)
|
||||
search_body = models.TextField()
|
||||
payment_providers = models.TextField()
|
||||
@@ -118,8 +118,8 @@ class Invoice(models.Model):
|
||||
self.invoice_from,
|
||||
(self.invoice_from_zipcode or "") + " " + (self.invoice_from_city or ""),
|
||||
str(self.invoice_from_country),
|
||||
pgettext("invoice", "VAT-ID: %s") % self.invoice_from_vat_id if self.invoice_from_vat_id else "",
|
||||
pgettext("invoice", "Tax ID: %s") % self.invoice_from_tax_id if self.invoice_from_tax_id else "",
|
||||
pgettext("invoice", "VAT-ID: %s" % self.invoice_from_vat_id) if self.invoice_from_vat_id else "",
|
||||
pgettext("invoice", "Tax ID: %s" % self.invoice_from_tax_id) if self.invoice_from_tax_id else "",
|
||||
]
|
||||
return '\n'.join([p.strip() for p in parts if p and p.strip()])
|
||||
|
||||
@@ -150,8 +150,6 @@ class Invoice(models.Model):
|
||||
if not self.prefix:
|
||||
self.prefix = self.event.settings.invoice_numbers_prefix or (self.event.slug.upper() + '-')
|
||||
if not self.invoice_no:
|
||||
if self.order.testmode:
|
||||
self.prefix += 'TEST-'
|
||||
for i in range(10):
|
||||
if self.event.settings.get('invoice_numbers_consecutive'):
|
||||
self.invoice_no = self._get_numeric_invoice_number()
|
||||
@@ -235,6 +233,3 @@ class InvoiceLine(models.Model):
|
||||
|
||||
class Meta:
|
||||
ordering = ('position', 'pk')
|
||||
|
||||
def __str__(self):
|
||||
return 'Line {} of invoice {}'.format(self.position, self.invoice)
|
||||
|
||||
@@ -76,8 +76,6 @@ class Order(LockModel, LoggedModel):
|
||||
:type event: Event
|
||||
:param email: The email of the person who ordered this
|
||||
:type email: str
|
||||
:param testmode: Whether this is a test mode order
|
||||
:type testmode: bool
|
||||
:param locale: The locale of this order
|
||||
:type locale: str
|
||||
:param secret: A secret string that is required to modify the order
|
||||
@@ -123,7 +121,6 @@ class Order(LockModel, LoggedModel):
|
||||
verbose_name=_("Status"),
|
||||
db_index=True
|
||||
)
|
||||
testmode = models.BooleanField(default=False)
|
||||
event = models.ForeignKey(
|
||||
Event,
|
||||
verbose_name=_("Event"),
|
||||
@@ -188,23 +185,6 @@ class Order(LockModel, LoggedModel):
|
||||
def __str__(self):
|
||||
return self.full_code
|
||||
|
||||
def gracefully_delete(self, user=None, auth=None):
|
||||
if not self.testmode:
|
||||
raise TypeError("Only test mode orders can be deleted.")
|
||||
self.event.log_action(
|
||||
'pretix.event.order.deleted', user=user, auth=auth,
|
||||
data={
|
||||
'code': self.code,
|
||||
}
|
||||
)
|
||||
OrderPosition.all.filter(order=self, addon_to__isnull=False).delete()
|
||||
OrderPosition.all.filter(order=self).delete()
|
||||
OrderFee.all.filter(order=self).delete()
|
||||
self.refunds.all().delete()
|
||||
self.payments.all().delete()
|
||||
self.event.cache.delete('complain_testmode_orders')
|
||||
self.delete()
|
||||
|
||||
@property
|
||||
def fees(self):
|
||||
"""
|
||||
@@ -510,10 +490,6 @@ class Order(LockModel, LoggedModel):
|
||||
charset = list('ABCDEFGHJKLMNPQRSTUVWXYZ3789')
|
||||
while True:
|
||||
code = get_random_string(length=settings.ENTROPY['order_code'], allowed_chars=charset)
|
||||
if self.testmode:
|
||||
# Subtle way to recognize test orders while debugging: They all contain a 0 at the second place,
|
||||
# even though zeros are not used outside test mode.
|
||||
code = code[0] + "0" + code[2:]
|
||||
if not Order.objects.filter(event__organizer=self.event.organizer, code=code).exists():
|
||||
self.code = code
|
||||
return
|
||||
@@ -685,9 +661,6 @@ class Order(LockModel, LoggedModel):
|
||||
if not self.email:
|
||||
return
|
||||
|
||||
for k, v in self.event.meta_data.items():
|
||||
context['meta_' + k] = v
|
||||
|
||||
with language(self.locale):
|
||||
recipient = self.email
|
||||
try:
|
||||
@@ -720,6 +693,45 @@ class Order(LockModel, LoggedModel):
|
||||
continue
|
||||
yield op
|
||||
|
||||
def index(self, save=True):
|
||||
from .index import OrderSearchIndex
|
||||
indexed_strings = [
|
||||
self.code,
|
||||
self.full_code,
|
||||
self.email,
|
||||
self.comment,
|
||||
]
|
||||
try:
|
||||
indexed_strings.append(self.invoice_address.name_cached)
|
||||
indexed_strings.append(self.invoice_address.company)
|
||||
except InvoiceAddress.DoesNotExist:
|
||||
pass
|
||||
for p in self.all_positions.all():
|
||||
indexed_strings.append(p.attendee_name_cached)
|
||||
indexed_strings.append(p.attendee_email)
|
||||
indexed_strings.append(p.secret)
|
||||
pprovs = set()
|
||||
for p in self.payments.all():
|
||||
pprovs.add(p.provider)
|
||||
if save:
|
||||
return OrderSearchIndex.objects.update_or_create(
|
||||
order=self,
|
||||
defaults={
|
||||
'event': self.event,
|
||||
'organizer': self.event.organizer,
|
||||
'search_body': '\x1E'.join([str(v) for v in indexed_strings if v]),
|
||||
'payment_providers': '\x1E' + '\x1E'.join([str(v) for v in pprovs if v]) + '\x1E',
|
||||
}
|
||||
)[0]
|
||||
else:
|
||||
return OrderSearchIndex(
|
||||
order=self,
|
||||
event=self.event,
|
||||
organizer=self.event.organizer,
|
||||
search_body='\x1E'.join([str(v) for v in indexed_strings if v]),
|
||||
payment_providers='\x1E' + '\x1E'.join([str(v) for v in pprovs if v]) + '\x1E',
|
||||
)
|
||||
|
||||
|
||||
def answerfile_name(instance, filename: str) -> str:
|
||||
secret = get_random_string(length=32, allowed_chars=string.ascii_letters + string.digits)
|
||||
@@ -1064,9 +1076,6 @@ class OrderPayment(models.Model):
|
||||
class Meta:
|
||||
ordering = ('local_id',)
|
||||
|
||||
def __str__(self):
|
||||
return self.full_id
|
||||
|
||||
@property
|
||||
def info_data(self):
|
||||
"""
|
||||
@@ -1373,9 +1382,6 @@ class OrderRefund(models.Model):
|
||||
class Meta:
|
||||
ordering = ('local_id',)
|
||||
|
||||
def __str__(self):
|
||||
return self.full_id
|
||||
|
||||
@property
|
||||
def info_data(self):
|
||||
"""
|
||||
|
||||
@@ -177,7 +177,6 @@ class ParametrizedOrderNotificationType(NotificationType):
|
||||
n.add_attribute(_('Event'), order.event.name)
|
||||
n.add_attribute(_('Order code'), order.code)
|
||||
n.add_attribute(_('Order total'), money_filter(order.total, logentry.event.currency))
|
||||
n.add_attribute(_('Pending amount'), money_filter(order.pending_sum, logentry.event.currency))
|
||||
n.add_attribute(_('Order date'), date_format(order.datetime, 'SHORT_DATETIME_FORMAT'))
|
||||
n.add_attribute(_('Order status'), order.get_status_display())
|
||||
n.add_attribute(_('Order positions'), str(order.positions.count()))
|
||||
|
||||
@@ -88,18 +88,6 @@ class BasePaymentProvider:
|
||||
"""
|
||||
return self.settings.get('_enabled', as_type=bool)
|
||||
|
||||
@property
|
||||
def test_mode_message(self) -> str:
|
||||
"""
|
||||
If this property is set to a string, this will be displayed when this payment provider is selected
|
||||
while the event is in test mode. You should use it to explain to your user how your plugin behaves,
|
||||
e.g. if it falls back to a test mode automatically as well or if actual payments will be performed.
|
||||
|
||||
If you do not set this (or, return ``None``), pretix will show a default message warning the user
|
||||
that this plugin does not support test mode payments.
|
||||
"""
|
||||
return None
|
||||
|
||||
def calculate_fee(self, price: Decimal) -> Decimal:
|
||||
"""
|
||||
Calculate the fee for this payment provider which will be added to
|
||||
@@ -725,11 +713,6 @@ class ManualPayment(BasePaymentProvider):
|
||||
identifier = 'manual'
|
||||
verbose_name = _('Manual payment')
|
||||
|
||||
@property
|
||||
def test_mode_message(self):
|
||||
return _('In test mode, you can just manually mark this order as paid in the backend after it has been '
|
||||
'created.')
|
||||
|
||||
@property
|
||||
def is_implicit(self):
|
||||
return 'pretix.plugins.manualpayment' not in self.event.plugins
|
||||
|
||||
@@ -670,15 +670,15 @@ class CartManager:
|
||||
self._check_max_cart_size()
|
||||
self._calculate_expiry()
|
||||
|
||||
# with self.event.lock() as now_dt:
|
||||
with transaction.atomic():
|
||||
self.now_dt = now()
|
||||
self._extend_expiry_of_valid_existing_positions()
|
||||
err = self._delete_out_of_timeframe()
|
||||
err = self.extend_expired_positions() or err
|
||||
err = self._perform_operations() or err
|
||||
if err:
|
||||
raise CartError(err)
|
||||
with self.event.lock() as now_dt:
|
||||
with transaction.atomic():
|
||||
self.now_dt = now_dt
|
||||
self._extend_expiry_of_valid_existing_positions()
|
||||
err = self._delete_out_of_timeframe()
|
||||
err = self.extend_expired_positions() or err
|
||||
err = self._perform_operations() or err
|
||||
if err:
|
||||
raise CartError(err)
|
||||
|
||||
|
||||
def update_tax_rates(event: Event, cart_id: str, invoice_address: InvoiceAddress):
|
||||
|
||||
@@ -79,10 +79,6 @@ def mail(email: str, subject: str, template: Union[str, LazyI18nString],
|
||||
headers = headers or {}
|
||||
|
||||
with language(locale):
|
||||
if isinstance(context, dict) and event:
|
||||
for k, v in event.meta_data.items():
|
||||
context['meta_' + k] = v
|
||||
|
||||
if isinstance(context, dict) and order:
|
||||
try:
|
||||
context.update({
|
||||
@@ -130,8 +126,6 @@ def mail(email: str, subject: str, template: Union[str, LazyI18nString],
|
||||
body_plain += "\r\n\r\n-- \r\n"
|
||||
|
||||
if order:
|
||||
if order.testmode:
|
||||
subject = "[TESTMODE] " + subject
|
||||
body_plain += _(
|
||||
"You are receiving this email because you placed an order for {event}."
|
||||
).format(event=event.name)
|
||||
|
||||
@@ -533,7 +533,6 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d
|
||||
datetime=now_dt,
|
||||
locale=locale,
|
||||
total=total,
|
||||
testmode=event.testmode,
|
||||
meta_info=json.dumps(meta_info or {}),
|
||||
require_approval=any(p.item.require_approval for p in positions),
|
||||
sales_channel=sales_channel
|
||||
@@ -597,16 +596,16 @@ def _perform_order(event: str, payment_provider: str, position_ids: List[str],
|
||||
except InvoiceAddress.DoesNotExist:
|
||||
pass
|
||||
|
||||
# with event.lock() as now_dt:
|
||||
now_dt = now()
|
||||
positions = list(CartPosition.objects.filter(id__in=position_ids).select_related('item', 'variation', 'subevent'))
|
||||
if len(positions) == 0:
|
||||
raise OrderError(error_messages['empty'])
|
||||
if len(position_ids) != len(positions):
|
||||
raise OrderError(error_messages['internal'])
|
||||
_check_positions(event, now_dt, positions, address=addr)
|
||||
order = _create_order(event, email, positions, now_dt, pprov,
|
||||
locale=locale, address=addr, meta_info=meta_info, sales_channel=sales_channel)
|
||||
with event.lock() as now_dt:
|
||||
positions = list(CartPosition.objects.filter(
|
||||
id__in=position_ids).select_related('item', 'variation', 'subevent'))
|
||||
if len(positions) == 0:
|
||||
raise OrderError(error_messages['empty'])
|
||||
if len(position_ids) != len(positions):
|
||||
raise OrderError(error_messages['internal'])
|
||||
_check_positions(event, now_dt, positions, address=addr)
|
||||
order = _create_order(event, email, positions, now_dt, pprov,
|
||||
locale=locale, address=addr, meta_info=meta_info, sales_channel=sales_channel)
|
||||
|
||||
invoice = order.invoices.last() # Might be generated by plugin already
|
||||
if event.settings.get('invoice_generate') == 'True' and invoice_qualified(order):
|
||||
@@ -823,7 +822,7 @@ class OrderChangeManager:
|
||||
if keep_price:
|
||||
price = TaxedPrice(gross=position.price, net=position.price - position.tax_value,
|
||||
tax=position.tax_value, rate=position.tax_rate,
|
||||
name=position.tax_rule.name if position.tax_rule else None)
|
||||
name=position.tax_rule.name)
|
||||
else:
|
||||
price = get_price(item, variation, voucher=position.voucher, subevent=position.subevent,
|
||||
invoice_address=self._invoice_address)
|
||||
@@ -963,7 +962,7 @@ class OrderChangeManager:
|
||||
)
|
||||
self.order.save()
|
||||
elif self.order.status in (Order.STATUS_PENDING, Order.STATUS_EXPIRED) and self._totaldiff < 0:
|
||||
if self.order.pending_sum <= Decimal('0.00') and not self.order.require_approval:
|
||||
if self.order.pending_sum <= Decimal('0.00'):
|
||||
self.order.status = Order.STATUS_PAID
|
||||
self.order.save()
|
||||
elif self.open_payment:
|
||||
@@ -983,7 +982,7 @@ class OrderChangeManager:
|
||||
}, user=self.user, auth=self.auth)
|
||||
|
||||
def _check_paid_to_free(self):
|
||||
if self.order.total == 0 and (self._totaldiff < 0 or (self.split_order and self.split_order.total > 0)) and not self.order.require_approval:
|
||||
if self.order.total == 0 and (self._totaldiff < 0 or (self.split_order and self.split_order.total > 0)):
|
||||
# if the order becomes free, mark it paid using the 'free' provider
|
||||
# this could happen if positions have been made cheaper or removed (_totaldiff < 0)
|
||||
# or positions got split off to a new order (split_order with positive total)
|
||||
@@ -998,7 +997,7 @@ class OrderChangeManager:
|
||||
except Quota.QuotaExceededException:
|
||||
raise OrderError(self.error_messages['paid_to_free_exceeded'])
|
||||
|
||||
if self.split_order and self.split_order.total == 0 and not self.split_order.require_approval:
|
||||
if self.split_order and self.split_order.total == 0:
|
||||
p = self.split_order.payments.create(
|
||||
state=OrderPayment.PAYMENT_STATE_CREATED,
|
||||
provider='free',
|
||||
@@ -1126,7 +1125,6 @@ class OrderChangeManager:
|
||||
split_order.code = None
|
||||
split_order.datetime = now()
|
||||
split_order.secret = generate_secret()
|
||||
split_order.require_approval = self.order.require_approval and any(p.item.require_approval for p in split_positions)
|
||||
split_order.save()
|
||||
split_order.log_action('pretix.event.order.changed.split_from', user=self.user, auth=self.auth, data={
|
||||
'original_order': self.order.code
|
||||
|
||||
@@ -84,7 +84,7 @@ def markdown_compile(source):
|
||||
source,
|
||||
extensions=[
|
||||
'markdown.extensions.sane_lists',
|
||||
'markdown.extensions.nl2br'
|
||||
# 'markdown.extensions.nl2br', # TODO: Enable, but check backwards-compatibility issues e.g. with mails
|
||||
]
|
||||
),
|
||||
tags=ALLOWED_TAGS,
|
||||
|
||||
@@ -52,15 +52,6 @@ def contextprocessor(request):
|
||||
|
||||
ctx['has_domain'] = request.event.organizer.domains.exists()
|
||||
|
||||
if not request.event.testmode:
|
||||
complain_testmode_orders = request.event.cache.get('complain_testmode_orders')
|
||||
if complain_testmode_orders is None:
|
||||
complain_testmode_orders = request.event.orders.filter(testmode=True).exists()
|
||||
request.event.cache.set('complain_testmode_orders', complain_testmode_orders, 30)
|
||||
ctx['complain_testmode_orders'] = complain_testmode_orders
|
||||
else:
|
||||
ctx['complain_testmode_orders'] = False
|
||||
|
||||
if not request.event.live and ctx['has_domain']:
|
||||
child_sess = request.session.get('child_session_{}'.format(request.event.pk))
|
||||
s = SessionStore()
|
||||
|
||||
@@ -973,13 +973,6 @@ class MailSettingsForm(SettingsForm):
|
||||
self.fields['mail_html_renderer'].choices = [
|
||||
(r.identifier, r.verbose_name) for r in event.get_html_mail_renderers().values()
|
||||
]
|
||||
keys = list(event.meta_data.keys())
|
||||
for k, v in self.fields.items():
|
||||
if k.startswith('mail_text_'):
|
||||
v.help_text = str(v.help_text) + ', ' + ', '.join({
|
||||
'{meta_' + p + '}' for p in keys
|
||||
})
|
||||
v.validators[0].limit_value += ['{meta_' + p + '}' for p in keys]
|
||||
|
||||
def clean(self):
|
||||
data = self.cleaned_data
|
||||
|
||||
@@ -212,7 +212,6 @@ class EventOrderFilterForm(OrderFilterForm):
|
||||
('overpaid', _('Overpaid')),
|
||||
('underpaid', _('Underpaid')),
|
||||
('pendingpaid', _('Pending (but fully paid)')),
|
||||
('testmode', _('Test mode')),
|
||||
),
|
||||
required=False,
|
||||
)
|
||||
@@ -299,10 +298,6 @@ class EventOrderFilterForm(OrderFilterForm):
|
||||
status=Order.STATUS_PENDING,
|
||||
require_approval=True
|
||||
)
|
||||
elif fdata.get('status') == 'testmode':
|
||||
qs = qs.filter(
|
||||
testmode=True
|
||||
)
|
||||
elif fdata.get('status') == 'cp':
|
||||
s = OrderPosition.objects.filter(
|
||||
order=OuterRef('pk')
|
||||
|
||||
@@ -160,7 +160,6 @@ class ItemCreateForm(I18nModelForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.event = kwargs['event']
|
||||
self.user = kwargs.pop('user')
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.fields['category'].queryset = self.instance.event.categories.all()
|
||||
@@ -240,9 +239,6 @@ class ItemCreateForm(I18nModelForm):
|
||||
if self.cleaned_data.get('quota_option') == self.EXISTING and self.cleaned_data.get('quota_add_existing') is not None:
|
||||
quota = self.cleaned_data.get('quota_add_existing')
|
||||
quota.items.add(self.instance)
|
||||
quota.log_action('pretix.event.quota.changed', user=self.user, data={
|
||||
'item_added': self.instance.pk
|
||||
})
|
||||
elif self.cleaned_data.get('quota_option') == self.NEW:
|
||||
quota_name = self.cleaned_data.get('quota_add_new_name')
|
||||
quota_size = self.cleaned_data.get('quota_add_new_size')
|
||||
@@ -251,11 +247,6 @@ class ItemCreateForm(I18nModelForm):
|
||||
event=self.event, name=quota_name, size=quota_size
|
||||
)
|
||||
quota.items.add(self.instance)
|
||||
quota.log_action('pretix.event.quota.added', user=self.user, data={
|
||||
'name': quota_name,
|
||||
'size': quota_size,
|
||||
'items': [self.instance.pk]
|
||||
})
|
||||
|
||||
if self.cleaned_data.get('has_variations'):
|
||||
if self.cleaned_data.get('copy_from') and self.cleaned_data.get('copy_from').has_variations:
|
||||
|
||||
@@ -172,7 +172,6 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
|
||||
'pretix.event.order.paid': _('The order has been marked as paid.'),
|
||||
'pretix.event.order.refunded': _('The order has been refunded.'),
|
||||
'pretix.event.order.canceled': _('The order has been canceled.'),
|
||||
'pretix.event.order.deleted': _('The test mode order {code} has been deleted.'),
|
||||
'pretix.event.order.placed': _('The order has been created.'),
|
||||
'pretix.event.order.placed.require_approval': _('The order requires approval before it can continue to be processed.'),
|
||||
'pretix.event.order.approved': _('The order has been approved.'),
|
||||
@@ -269,8 +268,6 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
|
||||
'pretix.event.plugins.disabled': _('A plugin has been disabled.'),
|
||||
'pretix.event.live.activated': _('The shop has been taken live.'),
|
||||
'pretix.event.live.deactivated': _('The shop has been taken offline.'),
|
||||
'pretix.event.testmode.activated': _('The shop has been taken into test mode.'),
|
||||
'pretix.event.testmode.deactivated': _('The test mode has been disabled.'),
|
||||
'pretix.event.added': _('The event has been created.'),
|
||||
'pretix.event.changed': _('The event settings have been changed.'),
|
||||
'pretix.event.question.option.added': _('An answer option has been added to the question.'),
|
||||
|
||||
@@ -235,14 +235,6 @@
|
||||
</ul>
|
||||
</div>
|
||||
<ul class="nav" id="side-menu">
|
||||
{% if request.event and request.event.testmode %}
|
||||
<li class="testmode">
|
||||
<a href="{% url "control:event.live" event=request.event.slug organizer=request.organizer.slug %}">
|
||||
<i class="fa fa-warning fa-fw"></i>
|
||||
{% trans "TEST MODE" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% block nav %}
|
||||
{% for nav in nav_items %}
|
||||
<li>
|
||||
@@ -325,20 +317,6 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if complain_testmode_orders %}
|
||||
<div class="alert alert-warning">
|
||||
{% blocktrans trimmed %}
|
||||
Your event contains <strong>test mode orders</strong> even though <strong>test mode has been disabled</strong>.
|
||||
You should delete those orders to make sure they do not show up in your reports and statistics and block people from
|
||||
actually buying tickets.
|
||||
{% endblocktrans %}
|
||||
<strong>
|
||||
<a href="{% url "control:event.orders" event=request.event.slug organizer=request.organizer.slug %}?status=testmode">
|
||||
{% trans "Show all test mode orders" %}
|
||||
</a>
|
||||
</strong>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if warning_update_check_active %}
|
||||
<div class="alert alert-info">
|
||||
<a href="{% url "control:global.update" %}">
|
||||
|
||||
@@ -89,9 +89,6 @@
|
||||
{% if e.order.status == "n" %}
|
||||
<span class="label label-warning">{% trans "unpaid" %}</span>
|
||||
{% endif %}
|
||||
{% if e.order.testmode %}
|
||||
<span class="label label-warning">{% trans "TEST MODE" %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ e.item }}{% if e.variation %} – {{ e.variation }}{% endif %}</td>
|
||||
<td>{{ e.order.email }}</td>
|
||||
|
||||
@@ -3,113 +3,49 @@
|
||||
{% load bootstrap3 %}
|
||||
{% block content %}
|
||||
<h1>{% trans "Shop status" %}</h1>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{% trans "Shop visibility" %}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{% if request.event.live %}
|
||||
<p>
|
||||
{% trans "Your shop is currently live. If you take it down, it will only be visible to you and your team." %}
|
||||
</p>
|
||||
<form action="" method="post" class="text-right">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="live" value="false">
|
||||
<button type="submit" class="btn btn-lg btn-danger btn-save">
|
||||
{% trans "Go offline" %}
|
||||
</button>
|
||||
</form>
|
||||
{% else %}
|
||||
{% if issues|length > 0 %}
|
||||
<p>
|
||||
{% trans "Your ticket shop is currently not live. It is thus only visible to you and your team, not to any visitors." %}
|
||||
</p>
|
||||
<div class="alert alert-warning">
|
||||
<p>
|
||||
{% trans "To publish your ticket shop, you first need to resolve the following issues:" %}
|
||||
</p>
|
||||
<ul>
|
||||
{% for issue in issues %}
|
||||
<li>{{ issue|safe }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="test-right">
|
||||
<button type="submit" class="btn btn-primary btn-lg btn-save" disabled>
|
||||
{% trans "Go live" %}
|
||||
</button>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>
|
||||
{% trans "Your ticket shop is currently not live. It is thus only visible to you and your team, not to any visitors." %}
|
||||
</p>
|
||||
<p>
|
||||
{% trans "If you want to, you can publish your ticket shop now." %}
|
||||
</p>
|
||||
<form action="" method="post" class="text-right">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="live" value="true">
|
||||
<button type="submit" class="btn btn-primary btn-lg btn-save">
|
||||
{% trans "Go live" %}
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{% trans "Test mode" %}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{% if request.event.testmode %}
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="testmode" value="false">
|
||||
<p>
|
||||
{% trans "Your shop is currently in test mode. All orders are not persistant and can be deleted at any point." %}
|
||||
</p>
|
||||
<div class="form-inline">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" name="delete" value="yes" />
|
||||
{% trans "Permanently delete all orders created in test mode" %}
|
||||
</label>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<button type="submit" class="btn btn-lg btn-primary btn-save">
|
||||
{% trans "Disable test mode" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% else %}
|
||||
<p>
|
||||
{% trans "Your shop is currently in production mode." %}
|
||||
</p>
|
||||
<p>
|
||||
{% trans "If you want to do some test orders, you can enable test mode for your shop. As long as the shop is in test mode, all orders that are created are marked as test orders and can be deleted again." %}
|
||||
<strong>
|
||||
{% trans "Please note that test orders still count into your quotas, actually use vouchers and might perform actual payments. The only difference is that you can delete test orders. Use at your own risk!" %}
|
||||
</strong>
|
||||
</p>
|
||||
<p>
|
||||
{% trans "Also, test mode only covers the main web shop. Orders created through other sales channels such as the box office or resellers module are still created as production orders." %}
|
||||
</p>
|
||||
{% if actual_orders %}
|
||||
<div class="alert alert-danger">
|
||||
{% trans "It looks like you already have some real orders in your shop. We do not recommend enabling test mode if your customers already know your shop, as it will confuse them." %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<form action="" method="post" class="text-right">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="testmode" value="true">
|
||||
<button type="submit" class="btn btn-danger btn-lg btn-save">
|
||||
{% trans "Enable test mode" %}
|
||||
</button>
|
||||
</form>
|
||||
{% if request.event.live %}
|
||||
<p>
|
||||
{% trans "Your shop is currently live. If you take it down, it will only be visible to you and your team." %}
|
||||
</p>
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="live" value="false">
|
||||
|
||||
{% endif %}
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Go offline" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% else %}
|
||||
<p>
|
||||
{% trans "Your ticket shop is currently not live. It is thus only visible to you and your team, not to any visitors." %}
|
||||
</p>
|
||||
{% if issues|length > 0 %}
|
||||
<div class="alert alert-warning">
|
||||
<p>
|
||||
{% trans "To publish your ticket shop, you first need to resolve the following issues:" %}
|
||||
</p>
|
||||
<ul>
|
||||
{% for issue in issues %}
|
||||
<li>{{ issue|safe }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>
|
||||
{% trans "If you want to, you can publish your ticket shop now." %}
|
||||
</p>
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="live" value="true">
|
||||
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Go live" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -90,11 +90,6 @@
|
||||
<span class="fa fa-eraser"></span>
|
||||
{% trans "Delete personal data" %}
|
||||
</a>
|
||||
<a href="{% url "control:events.add" %}?clone={{ request.event.pk }}"
|
||||
class="btn btn-default btn-lg">
|
||||
<span class="fa fa-copy"></span>
|
||||
{% trans "Clone event" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
<form action="" method="post" class="form-horizontal">
|
||||
{% csrf_token %}
|
||||
{{ wizard.management_form }}
|
||||
{{ wizard.prefix_form }}
|
||||
{% bootstrap_form_errors form %}
|
||||
{% block form %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -72,13 +72,11 @@
|
||||
<a href="?{% url_replace request 'ordering' '-sum_tickets_paid' %}"><i class="fa fa-caret-down"></i></a>
|
||||
<a href="?{% url_replace request 'ordering' 'sum_tickets_paid' %}"><i class="fa fa-caret-up"></i></a>
|
||||
</th>
|
||||
<th>
|
||||
<th class="text-right">
|
||||
{% trans "Status" %}
|
||||
<a href="?{% url_replace request 'ordering' '-live' %}"><i class="fa fa-caret-down"></i></a>
|
||||
<a href="?{% url_replace request 'ordering' 'live' %}"><i class="fa fa-caret-up"></i></a>
|
||||
</th>
|
||||
<th class="text-right">
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -119,7 +117,7 @@
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<td class="text-right">
|
||||
{% if not e.live %}
|
||||
<span class="label label-danger">{% trans "Shop disabled" %}</span>
|
||||
{% elif e.presale_has_ended %}
|
||||
@@ -130,17 +128,6 @@
|
||||
<span class="label label-success">{% trans "On sale" %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<a href="{% url "control:event.index" organizer=e.organizer.slug event=e.slug %}"
|
||||
class="btn btn-sm btn-default" title="{% trans "Open event dashboard" %}"
|
||||
data-toggle="tooltip">
|
||||
<span class="fa fa-eye"></span>
|
||||
</a>
|
||||
<a href="{% url "control:events.add" %}?clone={{ e.pk }}" class="btn btn-sm btn-default"
|
||||
title="{% trans "Clone event" %}" data-toggle="tooltip">
|
||||
<span class="fa fa-copy"></span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
{% extends "pretixcontrol/event/base.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}
|
||||
{% trans "Delete order" %}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% trans "Delete order" %}
|
||||
</h1>
|
||||
<p>{% blocktrans trimmed %}
|
||||
Do you really want to delete this order? <strong>You really cannot revert this action and we can't either.</strong>
|
||||
{% endblocktrans %}</p>
|
||||
|
||||
<form method="post" href="">
|
||||
{% csrf_token %}
|
||||
<div class="row checkout-button-row">
|
||||
<div class="col-md-4">
|
||||
<a class="btn btn-block btn-default btn-lg"
|
||||
href="{% url "control:event.order" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}">
|
||||
{% trans "No, take me back" %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-4 col-md-offset-4">
|
||||
<button class="btn btn-block btn-danger btn-lg" type="submit">
|
||||
{% trans "Yes, delete order" %}
|
||||
</button>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -16,9 +16,6 @@
|
||||
{% blocktrans trimmed with code=order.code %}
|
||||
Order details: {{ code }}
|
||||
{% endblocktrans %}
|
||||
{% if order.testmode %}
|
||||
<span class="label label-warning">{% trans "TEST MODE" %}</span>
|
||||
{% endif %}
|
||||
{% include "pretixcontrol/orders/fragment_order_status.html" with order=order class="pull-right" %}
|
||||
</h1>
|
||||
{% if 'can_change_orders' in request.eventpermset %}
|
||||
@@ -27,13 +24,6 @@
|
||||
{% csrf_token %}
|
||||
<div class="btn-toolbar" role="toolbar">
|
||||
<div class="btn-group" role="group">
|
||||
{% if order.testmode %}
|
||||
<a href="{% url "control:event.order.delete" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}"
|
||||
class="btn btn-danger">
|
||||
<span class="fa fa-trash"></span>
|
||||
{% trans "Delete" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if order.require_approval and order.status == 'n' %}
|
||||
<a href="{% url "control:event.order.approve" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}"
|
||||
class="btn btn-primary">
|
||||
@@ -280,10 +270,6 @@
|
||||
</form>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<button type="submit" data-toggle="qrcode" data-qrcode="{{ line.secret }}"
|
||||
class="btn btn-xs btn-default">
|
||||
<span class="fa fa-qrcode"></span> {% trans "Show ticket code" %}
|
||||
</button>
|
||||
{% eventsignal event "pretix.control.signals.order_position_buttons" order=order position=line request=request %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
@@ -114,11 +114,9 @@
|
||||
<strong>
|
||||
<a
|
||||
href="{% url "control:event.order" event=request.event.slug organizer=request.event.organizer.slug code=o.code %}">
|
||||
{{ o.code }}</a>
|
||||
{{ o.code }}
|
||||
</a>
|
||||
</strong>
|
||||
{% if o.testmode %}
|
||||
<span class="label label-warning">{% trans "TEST MODE" %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ o.email|default_if_none:"" }}
|
||||
|
||||
@@ -55,9 +55,6 @@
|
||||
<a href="{% url "control:event.order" event=request.event.slug organizer=request.event.organizer.slug code=r.order.code %}">
|
||||
{{ r.order.code }}</a>-R-{{ r.local_id }}
|
||||
</strong>
|
||||
{% if r.order.testmode %}
|
||||
<span class="label label-warning">{% trans "TEST MODE" %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ r.payment_provider.verbose_name }}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
<tr>
|
||||
<th>{% trans "Event name" %}</th>
|
||||
<th>{% trans "Start date" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -26,28 +25,13 @@
|
||||
href="{% url "control:event.index" organizer=e.organizer.slug event=e.slug %}">{{ e.name }}</a></strong>
|
||||
</td>
|
||||
<td>{{ e.get_date_from_display }}</td>
|
||||
<td class="text-right">
|
||||
<a href="{% url "control:event.index" organizer=e.organizer.slug event=e.slug %}"
|
||||
class="btn btn-sm btn-default" title="{% trans "Open event dashboard" %}"
|
||||
data-toggle="tooltip">
|
||||
<span class="fa fa-eye"></span>
|
||||
</a>
|
||||
{% if "can_create_events" in request.orgapermset %}
|
||||
<a href="{% url "control:events.add" %}?clone={{ e.pk }}" class="btn btn-sm btn-default"
|
||||
title="{% trans "Clone event" %}" data-toggle="tooltip">
|
||||
<span class="fa fa-copy"></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% if "can_create_events" in request.orgapermset %}
|
||||
<a href="{% url "control:events.add" %}" class="btn btn-default">
|
||||
<span class="fa fa-plus"></span>
|
||||
{% trans "Create a new event" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
<a href="{% url "control:events.add" %}" class="btn btn-default">
|
||||
<span class="fa fa-plus"></span>
|
||||
{% trans "Create a new event" %}
|
||||
</a>
|
||||
{% endblock %}
|
||||
|
||||
@@ -59,11 +59,9 @@
|
||||
<td>
|
||||
<strong>
|
||||
<a href="{% url "control:event.order" event=o.event.slug organizer=o.event.organizer.slug code=o.code %}">
|
||||
{{ o.event.slug|upper }}-{{ o.code }}</a>
|
||||
{{ o.event.slug|upper }}-{{ o.code }}
|
||||
</a>
|
||||
</strong>
|
||||
{% if o.testmode %}
|
||||
<span class="label label-warning">{% trans "TEST MODE" %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ o.event.name }}</td>
|
||||
<td>
|
||||
|
||||
@@ -222,8 +222,6 @@ urlpatterns = [
|
||||
name='event.order.approve'),
|
||||
url(r'^orders/(?P<code>[0-9A-Z]+)/deny$', orders.OrderDeny.as_view(),
|
||||
name='event.order.deny'),
|
||||
url(r'^orders/(?P<code>[0-9A-Z]+)/delete$', orders.OrderDelete.as_view(),
|
||||
name='event.order.delete'),
|
||||
url(r'^orders/(?P<code>[0-9A-Z]+)/info', orders.OrderModifyInformation.as_view(),
|
||||
name='event.order.info'),
|
||||
url(r'^orders/(?P<code>[0-9A-Z]+)/sendmail$', orders.OrderSendMail.as_view(),
|
||||
|
||||
@@ -183,20 +183,8 @@ def shop_state_widget(sender, **kwargs):
|
||||
'priority': 1000,
|
||||
'content': '<div class="shopstate">{t1}<br><span class="{cls}"><span class="fa {icon}"></span> {state}</span>{t2}</div>'.format(
|
||||
t1=_('Your ticket shop is'), t2=_('Click here to change'),
|
||||
state=_('live') if sender.live and not sender.testmode else (
|
||||
_('live and in test mode') if sender.live else (
|
||||
_('not yet public') if not sender.testmode else (
|
||||
_('in private test mode')
|
||||
)
|
||||
)
|
||||
),
|
||||
icon='fa-check-circle' if sender.live and not sender.testmode else (
|
||||
'fa-warning' if sender.live else (
|
||||
'fa-times-circle' if not sender.testmode else (
|
||||
'fa-times-circle'
|
||||
)
|
||||
)
|
||||
),
|
||||
state=_('live') if sender.live else _('not yet public'),
|
||||
icon='fa-check-circle' if sender.live else 'fa-times-circle',
|
||||
cls='live' if sender.live else 'off'
|
||||
),
|
||||
'url': reverse('control:event.live', kwargs={
|
||||
|
||||
@@ -573,13 +573,9 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
||||
locales[str(idx)] = val[0]
|
||||
return locales
|
||||
|
||||
@cached_property
|
||||
def meta_properties(self):
|
||||
return [p.name for p in self.request.organizer.meta_properties.all()]
|
||||
|
||||
@cached_property
|
||||
def items(self):
|
||||
kv = {
|
||||
return {
|
||||
'mail_text_order_placed': ['total', 'currency', 'date', 'invoice_company', 'total_with_currency',
|
||||
'event', 'payment_info', 'url', 'invoice_name'],
|
||||
'mail_text_order_paid': ['event', 'url', 'invoice_name', 'invoice_company', 'payment_info'],
|
||||
@@ -600,10 +596,6 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
||||
'mail_text_order_denied': ['total', 'currency', 'date', 'invoice_company',
|
||||
'total_with_currency', 'event', 'url', 'invoice_name'],
|
||||
}
|
||||
for v in kv.values():
|
||||
for p in self.meta_properties:
|
||||
v.append('meta_' + p)
|
||||
return kv
|
||||
|
||||
@cached_property
|
||||
def base_data(self):
|
||||
@@ -615,7 +607,7 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
||||
orders = [' - {} - {}'.format(self.generate_order_fullname(self.request.event.slug, order['code']),
|
||||
self.generate_order_url(order['code'], order['secret']))
|
||||
for order in user_orders]
|
||||
d = {
|
||||
return {
|
||||
'event': self.request.event.name,
|
||||
'total': 42.23,
|
||||
'total_with_currency': LazyCurrencyNumber(42.23, self.request.event.currency),
|
||||
@@ -628,11 +620,8 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
||||
'invoice_name': _('John Doe'),
|
||||
'invoice_company': _('Sample Corporation'),
|
||||
'common': _('An individial text with a reason can be inserted here.'),
|
||||
'payment_info': _('Please transfer money to this bank account: 9999-9999-9999-9999'),
|
||||
'payment_info': _('Please transfer money to this bank account: 9999-9999-9999-9999')
|
||||
}
|
||||
for k, v in self.request.event.meta_data.items():
|
||||
d['meta_' + k] = v
|
||||
return d
|
||||
|
||||
def generate_order_url(self, code, secret):
|
||||
return build_absolute_uri('presale:event.order', kwargs={
|
||||
@@ -690,8 +679,7 @@ class MailSettingsRendererPreview(MailSettingsPreview):
|
||||
expires=now(), code="PREVIEW", total=119)
|
||||
item = request.event.items.create(name=ugettext("Sample product"), default_price=42.23,
|
||||
description=ugettext("Sample product description"))
|
||||
order.positions.create(item=item, attendee_name_parts={'_legacy': ugettext("John Doe")},
|
||||
price=item.default_price)
|
||||
order.positions.create(item=item, attendee_name_parts={'full_name': ugettext("John Doe")}, price=item.default_price)
|
||||
v = renderers[request.GET.get('renderer')].render(
|
||||
v,
|
||||
str(request.event.settings.mail_text_signature),
|
||||
@@ -854,55 +842,23 @@ class EventLive(EventPermissionRequiredMixin, TemplateView):
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['issues'] = self.request.event.live_issues
|
||||
ctx['actual_orders'] = self.request.event.orders.filter(testmode=False).exists()
|
||||
return ctx
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
if request.POST.get("live") == "true" and not self.request.event.live_issues:
|
||||
with transaction.atomic():
|
||||
request.event.live = True
|
||||
request.event.save()
|
||||
self.request.event.log_action(
|
||||
'pretix.event.live.activated', user=self.request.user, data={}
|
||||
)
|
||||
request.event.live = True
|
||||
request.event.save()
|
||||
self.request.event.log_action(
|
||||
'pretix.event.live.activated', user=self.request.user, data={}
|
||||
)
|
||||
messages.success(self.request, _('Your shop is live now!'))
|
||||
elif request.POST.get("live") == "false":
|
||||
with transaction.atomic():
|
||||
request.event.live = False
|
||||
request.event.save()
|
||||
self.request.event.log_action(
|
||||
'pretix.event.live.deactivated', user=self.request.user, data={}
|
||||
)
|
||||
request.event.live = False
|
||||
request.event.save()
|
||||
self.request.event.log_action(
|
||||
'pretix.event.live.deactivated', user=self.request.user, data={}
|
||||
)
|
||||
messages.success(self.request, _('We\'ve taken your shop down. You can re-enable it whenever you want!'))
|
||||
elif request.POST.get("testmode") == "true":
|
||||
with transaction.atomic():
|
||||
request.event.testmode = True
|
||||
request.event.save()
|
||||
self.request.event.log_action(
|
||||
'pretix.event.testmode.activated', user=self.request.user, data={}
|
||||
)
|
||||
messages.success(self.request, _('Your shop is now in test mode!'))
|
||||
elif request.POST.get("testmode") == "false":
|
||||
with transaction.atomic():
|
||||
request.event.testmode = False
|
||||
request.event.save()
|
||||
self.request.event.log_action(
|
||||
'pretix.event.testmode.deactivated', user=self.request.user, data={
|
||||
'delete': (request.POST.get("delete") == "yes")
|
||||
}
|
||||
)
|
||||
request.event.cache.delete('complain_testmode_orders')
|
||||
if request.POST.get("delete") == "yes":
|
||||
try:
|
||||
with transaction.atomic():
|
||||
for order in request.event.orders.filter(testmode=True):
|
||||
order.gracefully_delete(user=self.request.user)
|
||||
except ProtectedError:
|
||||
messages.error(self.request, _('An order could not be deleted as some constraints (e.g. data '
|
||||
'created by plug-ins) do not allow it.'))
|
||||
else:
|
||||
request.event.cache.set('complain_testmode_orders', False, 30)
|
||||
messages.success(self.request, _('We\'ve disabled test mode for you. Let\'s sell some real tickets!'))
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
|
||||
@@ -817,7 +817,7 @@ class ItemCreate(EventPermissionRequiredMixin, CreateView):
|
||||
"""
|
||||
newinst = Item(event=self.request.event)
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs.update({'instance': newinst, 'user': self.request.user})
|
||||
kwargs.update({'instance': newinst})
|
||||
return kwargs
|
||||
|
||||
def form_invalid(self, form):
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.db import transaction
|
||||
from django.db.models import (
|
||||
F, IntegerField, Max, Min, OuterRef, Prefetch, Subquery, Sum,
|
||||
@@ -13,9 +12,9 @@ from django.utils.functional import cached_property
|
||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||
from django.views import View
|
||||
from django.views.generic import ListView
|
||||
from formtools.wizard.views import SessionWizardView
|
||||
from i18nfield.strings import LazyI18nString
|
||||
|
||||
from pretix.base.forms import SafeSessionWizardView
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import Event, Organizer, Quota, Team
|
||||
from pretix.control.forms.event import (
|
||||
@@ -95,13 +94,10 @@ class EventList(PaginationMixin, ListView):
|
||||
|
||||
|
||||
def condition_copy(wizard):
|
||||
return (
|
||||
not wizard.clone_from and
|
||||
EventWizardCopyForm.copy_from_queryset(wizard.request.user).exists()
|
||||
)
|
||||
return EventWizardCopyForm.copy_from_queryset(wizard.request.user).exists()
|
||||
|
||||
|
||||
class EventWizard(SafeSessionWizardView):
|
||||
class EventWizard(SessionWizardView):
|
||||
form_list = [
|
||||
('foundation', EventWizardFoundationForm),
|
||||
('basics', EventWizardBasicsForm),
|
||||
@@ -116,49 +112,6 @@ class EventWizard(SafeSessionWizardView):
|
||||
'copy': condition_copy
|
||||
}
|
||||
|
||||
def get_form_initial(self, step):
|
||||
initial = super().get_form_initial(step)
|
||||
if self.clone_from:
|
||||
if step == 'foundation':
|
||||
initial['organizer'] = self.clone_from.organizer
|
||||
initial['locales'] = self.clone_from.settings.locales
|
||||
initial['has_subevents'] = self.clone_from.has_subevents
|
||||
elif step == 'basics':
|
||||
initial['name'] = self.clone_from.name
|
||||
initial['slug'] = self.clone_from.slug + '-2'
|
||||
initial['currency'] = self.clone_from.currency
|
||||
initial['date_from'] = self.clone_from.date_from
|
||||
initial['date_to'] = self.clone_from.date_to
|
||||
initial['presale_start'] = self.clone_from.presale_start
|
||||
initial['presale_end'] = self.clone_from.presale_end
|
||||
initial['location'] = self.clone_from.location
|
||||
initial['timezone'] = self.clone_from.settings.timezone
|
||||
initial['locale'] = self.clone_from.settings.locale
|
||||
if self.clone_from.settings.tax_rate_default:
|
||||
initial['tax_rate'] = self.clone_from.settings.tax_rate_default.rate
|
||||
|
||||
return initial
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.clone_from = None
|
||||
if 'clone' in self.request.GET:
|
||||
try:
|
||||
clone_from = Event.objects.select_related('organizer').get(pk=self.request.GET.get("clone"))
|
||||
except Event.DoesNotExist:
|
||||
allow = False
|
||||
else:
|
||||
allow = (
|
||||
request.user.has_event_permission(clone_from.organizer, clone_from,
|
||||
'can_change_event_settings', request)
|
||||
and request.user.has_event_permission(clone_from.organizer, clone_from,
|
||||
'can_change_items', request)
|
||||
)
|
||||
if not allow:
|
||||
messages.error(self.request, _('You do not have permission to clone this event.'))
|
||||
else:
|
||||
self.clone_from = clone_from
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, form, **kwargs):
|
||||
ctx = super().get_context_data(form, **kwargs)
|
||||
ctx['has_organizer'] = self.request.user.teams.filter(can_create_events=True).exists()
|
||||
@@ -180,13 +133,6 @@ class EventWizard(SafeSessionWizardView):
|
||||
}
|
||||
if step != 'foundation':
|
||||
fdata = self.get_cleaned_data_for_step('foundation')
|
||||
if fdata is None:
|
||||
fdata = {
|
||||
'organizer': Organizer(slug='_nonexisting'),
|
||||
'has_subevents': False,
|
||||
'locales': ['en']
|
||||
}
|
||||
# The show must go on, we catch this error in render()
|
||||
kwargs.update(fdata)
|
||||
return kwargs
|
||||
|
||||
@@ -240,8 +186,6 @@ class EventWizard(SafeSessionWizardView):
|
||||
if copy_data and copy_data['copy_from_event']:
|
||||
from_event = copy_data['copy_from_event']
|
||||
event.copy_data_from(from_event)
|
||||
elif self.clone_from:
|
||||
event.copy_data_from(self.clone_from)
|
||||
elif event.has_subevents:
|
||||
event.checkin_lists.create(
|
||||
name=str(se),
|
||||
@@ -265,7 +209,7 @@ class EventWizard(SafeSessionWizardView):
|
||||
event.settings.set('locale', basics_data['locale'])
|
||||
event.settings.set('locales', foundation_data['locales'])
|
||||
|
||||
if (copy_data and copy_data['copy_from_event']) or self.clone_from or event.has_subevents:
|
||||
if (copy_data and copy_data['copy_from_event']) or event.has_subevents:
|
||||
return redirect(reverse('control:event.settings', kwargs={
|
||||
'organizer': event.organizer.slug,
|
||||
'event': event.slug,
|
||||
|
||||
@@ -11,9 +11,7 @@ from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.core.files import File
|
||||
from django.db import transaction
|
||||
from django.db.models import (
|
||||
Count, IntegerField, OuterRef, ProtectedError, Subquery,
|
||||
)
|
||||
from django.db.models import Count, IntegerField, OuterRef, Subquery
|
||||
from django.http import (
|
||||
FileResponse, Http404, HttpResponseNotAllowed, JsonResponse,
|
||||
)
|
||||
@@ -400,35 +398,6 @@ class OrderApprove(OrderView):
|
||||
})
|
||||
|
||||
|
||||
class OrderDelete(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
if self.order.testmode:
|
||||
try:
|
||||
with transaction.atomic():
|
||||
self.order.gracefully_delete(user=self.request.user)
|
||||
messages.success(self.request, _('The order has been deleted.'))
|
||||
return redirect(reverse('control:event.orders', kwargs={
|
||||
'event': self.request.event.slug,
|
||||
'organizer': self.request.organizer.slug,
|
||||
}))
|
||||
except ProtectedError:
|
||||
messages.error(self.request, _('The order could not be deleted as some constraints (e.g. data created '
|
||||
'by plug-ins) do not allow it.'))
|
||||
return self.get(self.request, *self.args, **self.kwargs)
|
||||
|
||||
return redirect(self.get_order_url())
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
if not self.order.testmode:
|
||||
messages.error(self.request, _('Only orders created in test mode can be deleted.'))
|
||||
return redirect(self.get_order_url())
|
||||
return render(self.request, 'pretixcontrol/order/delete.html', {
|
||||
'order': self.order,
|
||||
})
|
||||
|
||||
|
||||
class OrderDeny(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ class OrderSearch(PaginationMixin, ListView):
|
||||
return ctx
|
||||
|
||||
def get_queryset(self):
|
||||
qs = Order.objects.using(settings.DATABASE_REPLICA)
|
||||
qs = Order.objects.select_related('invoice_address').using(settings.DATABASE_REPLICA)
|
||||
|
||||
if not self.request.user.has_active_staff_session(self.request.session.session_key):
|
||||
qs = qs.filter(
|
||||
@@ -59,47 +59,9 @@ class OrderSearch(PaginationMixin, ListView):
|
||||
if self.filter_form.is_valid():
|
||||
qs = self.filter_form.filter_qs(qs)
|
||||
|
||||
if self.filter_form.cleaned_data.get('query'):
|
||||
"""
|
||||
We need to work around a bug in PostgreSQL's (and likely MySQL's) query plan optimizer here.
|
||||
The database lacks statistical data to predict how common our search filter is and therefore
|
||||
assumes that it is cheaper to first ORDER *all* orders in the system (since we got an index on
|
||||
datetime), then filter out with a full scan until OFFSET/LIMIT condition is fulfilled. If we
|
||||
look for something rare (such as an email address used once within hundreds of thousands of
|
||||
orders, this ends up to be pathologically slow.
|
||||
|
||||
For some search queries on pretix.eu, we see search times of >30s, just due to the ORDER BY and
|
||||
LIMIT clause. Without them. the query runs in roughly 0.6s. This heuristical approach tries to
|
||||
detect these cases and rewrite the query as a nested subquery that strongly suggests sorting
|
||||
before filtering. However, since even that fails in some cases because PostgreSQL thinks it knows
|
||||
better, we literally force it by evaluating the subquery explicitly. We only do this for n<=200,
|
||||
to avoid memory leaks – and problems with maximum parameter count on SQLite. In cases where the
|
||||
search query yields lots of results, this will actually be slower since it requires two queries,
|
||||
sorry.
|
||||
|
||||
Phew.
|
||||
"""
|
||||
|
||||
page = self.kwargs.get(self.page_kwarg) or self.request.GET.get(self.page_kwarg) or 1
|
||||
limit = self.get_paginate_by(None)
|
||||
offset = (page - 1) * limit
|
||||
resultids = list(qs.order_by().values_list('id', flat=True)[:201])
|
||||
if len(resultids) <= 200 and len(resultids) <= offset + limit:
|
||||
qs = Order.objects.using(settings.DATABASE_REPLICA).filter(
|
||||
id__in=resultids
|
||||
)
|
||||
|
||||
"""
|
||||
We use prefetch_related here instead of select_related for a reason, even though select_related
|
||||
would be the common choice for a foreign key and doesn't require an additional database query.
|
||||
The problem is, that if our results contain the same event 25 times, select_related will create
|
||||
25 Django objects which will all try to pull their ownsettings cache to show the event properly,
|
||||
leading to lots of unnecessary queries. Due to the way prefetch_related works differently, it
|
||||
will only create one shared Django object.
|
||||
"""
|
||||
return qs.only(
|
||||
'id', 'invoice_address__name_cached', 'invoice_address__name_parts', 'code', 'event', 'email',
|
||||
'datetime', 'total', 'status', 'require_approval', 'testmode'
|
||||
'datetime', 'total', 'status', 'require_approval'
|
||||
).prefetch_related(
|
||||
'event', 'event__organizer'
|
||||
).select_related('invoice_address')
|
||||
)
|
||||
|
||||
@@ -32,7 +32,7 @@ def get_sizes(size, imgsize):
|
||||
(0, int((imgsize[1] * wfactor - imgsize[1] * hfactor) / 2),
|
||||
imgsize[0] * hfactor, int((imgsize[1] * wfactor + imgsize[1] * wfactor) / 2))
|
||||
elif wfactor > hfactor:
|
||||
return (int(size[0]), int(imgsize[1] * wfactor)), \
|
||||
return (int(size[0]), int(imgsize[1] * hfactor)), \
|
||||
(0, int((imgsize[1] * wfactor - size[1]) / 2), size[0], int((imgsize[1] * wfactor + size[1]) / 2))
|
||||
else:
|
||||
return (int(imgsize[0] * hfactor), int(size[1])), \
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-02-20 16:52+0000\n"
|
||||
"POT-Creation-Date: 2019-02-01 16:27+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: none\n"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-02-20 16:52+0000\n"
|
||||
"POT-Creation-Date: 2019-02-01 16:27+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: none\n"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-02-20 16:52+0000\n"
|
||||
"POT-Creation-Date: 2019-02-01 16:27+0000\n"
|
||||
"PO-Revision-Date: 2018-04-24 14:22+0000\n"
|
||||
"Last-Translator: Pernille Thorsen <perth@aarhus.dk>\n"
|
||||
"Language-Team: Danish <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-02-20 16:52+0000\n"
|
||||
"POT-Creation-Date: 2019-02-01 16:27+0000\n"
|
||||
"PO-Revision-Date: 2018-12-02 15:44+0000\n"
|
||||
"Last-Translator: Alexander Schwartz <alexander.schwartz@gmx.net>\n"
|
||||
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-02-20 16:52+0000\n"
|
||||
"POT-Creation-Date: 2019-02-01 16:27+0000\n"
|
||||
"PO-Revision-Date: 2018-12-02 13:41+0000\n"
|
||||
"Last-Translator: Alexander Schwartz <alexander.schwartz@gmx.net>\n"
|
||||
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-02-20 16:52+0000\n"
|
||||
"POT-Creation-Date: 2019-02-01 16:27+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-02-20 16:52+0000\n"
|
||||
"POT-Creation-Date: 2019-02-01 16:27+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: none\n"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,9 +7,9 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-02-20 16:52+0000\n"
|
||||
"PO-Revision-Date: 2019-02-20 03:00+0000\n"
|
||||
"Last-Translator: oocf <oswaldocerna@gmail.com>\n"
|
||||
"POT-Creation-Date: 2019-02-01 16:27+0000\n"
|
||||
"PO-Revision-Date: 2018-11-29 09:01+0000\n"
|
||||
"Last-Translator: arabestia <sergioadalbertor@gmail.com>\n"
|
||||
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
"js/es/>\n"
|
||||
"Language: es\n"
|
||||
@@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 3.4\n"
|
||||
"X-Generator: Weblate 3.1.1\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||
@@ -62,14 +62,19 @@ msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:45
|
||||
#: pretix/static/pretixbase/js/asynctask.js:101
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Your request has been queued on the server and will now be processed. If "
|
||||
#| "this takes longer than two minutes, please contact us or go back in your "
|
||||
#| "browser and try again."
|
||||
msgid ""
|
||||
"Your request arrived on the server but we still wait for it to be processed. "
|
||||
"If this takes longer than two minutes, please contact us or go back in your "
|
||||
"browser and try again."
|
||||
msgstr ""
|
||||
"Su solicitud llegó al servidor pero seguimos esperando a que sea procesada. "
|
||||
"Si toma más de dos minutos, por favor contáctenos o regrese a la página "
|
||||
"anterior en su navegador y pruebe de nuevo."
|
||||
"Su solicitud ha sido enviada al servidor y será procesada en breve. Si el "
|
||||
"proceso tomara más de dos minutos, por favor, contáctenos o regrese a la "
|
||||
"página anterior en su navegador y pruebe de nuevo."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:66
|
||||
#: pretix/static/pretixbase/js/asynctask.js:124
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: French\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-02-20 16:52+0000\n"
|
||||
"POT-Creation-Date: 2019-02-01 16:27+0000\n"
|
||||
"PO-Revision-Date: 2018-10-28 10:23+0000\n"
|
||||
"Last-Translator: Arnaud Vergnet <keplyx@gmail.com>\n"
|
||||
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-02-20 16:52+0000\n"
|
||||
"POT-Creation-Date: 2019-02-01 16:27+0000\n"
|
||||
"PO-Revision-Date: 2019-01-02 08:20+0000\n"
|
||||
"Last-Translator: amefad <fame@libero.it>\n"
|
||||
"Language-Team: Italian <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-02-20 16:52+0000\n"
|
||||
"POT-Creation-Date: 2019-02-01 16:27+0000\n"
|
||||
"PO-Revision-Date: 2019-01-08 12:30+0000\n"
|
||||
"Last-Translator: Maarten van den Berg <maartenberg1@gmail.com>\n"
|
||||
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-02-20 16:52+0000\n"
|
||||
"POT-Creation-Date: 2019-02-01 16:27+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: none\n"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-02-20 16:52+0000\n"
|
||||
"POT-Creation-Date: 2019-02-01 16:27+0000\n"
|
||||
"PO-Revision-Date: 2019-01-08 21:00+0000\n"
|
||||
"Last-Translator: Maarten van den Berg <maartenberg1@gmail.com>\n"
|
||||
"Language-Team: Dutch (informal) <https://translate.pretix.eu/projects/pretix/"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-02-20 16:52+0000\n"
|
||||
"POT-Creation-Date: 2019-02-01 16:27+0000\n"
|
||||
"PO-Revision-Date: 2018-10-22 04:23+0000\n"
|
||||
"Last-Translator: Samir C. Costa <samirfor@gmail.com>\n"
|
||||
"Language-Team: Portuguese (Brazil) <https://translate.pretix.eu/projects/"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-02-20 16:52+0000\n"
|
||||
"POT-Creation-Date: 2019-02-01 16:27+0000\n"
|
||||
"PO-Revision-Date: 2019-01-02 08:21+0000\n"
|
||||
"Last-Translator: Alexey Zh <write2aracon@gmail.com>\n"
|
||||
"Language-Team: Russian <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-02-20 16:52+0000\n"
|
||||
"POT-Creation-Date: 2019-02-01 16:27+0000\n"
|
||||
"PO-Revision-Date: 2018-09-03 06:36+0000\n"
|
||||
"Last-Translator: Yunus Fırat Pişkin <firat.piskin@idvlabs.com>\n"
|
||||
"Language-Team: Turkish <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
|
||||
@@ -101,11 +101,6 @@ class BankTransfer(BasePaymentProvider):
|
||||
def public_name(self):
|
||||
return str(self.settings.get('public_name', as_type=LazyI18nString) or self.verbose_name)
|
||||
|
||||
@property
|
||||
def test_mode_message(self):
|
||||
return _('In test mode, you can just manually mark this order as paid in the backend after it has been '
|
||||
'created.')
|
||||
|
||||
@property
|
||||
def settings_form_fields(self):
|
||||
d = OrderedDict(
|
||||
|
||||
@@ -5,6 +5,4 @@ register = template.Library()
|
||||
|
||||
@register.filter
|
||||
def ibanformat(value):
|
||||
if not value:
|
||||
return ''
|
||||
return ' '.join(value[i:i + 4] for i in range(0, len(value), 4))
|
||||
|
||||
@@ -320,7 +320,6 @@ class CSVCheckinList(CheckInListMixin, ListExporter):
|
||||
|
||||
headers.append(_('Company'))
|
||||
headers.append(_('Voucher code'))
|
||||
headers.append(_('Order date'))
|
||||
yield headers
|
||||
|
||||
for op in qs:
|
||||
@@ -373,7 +372,6 @@ class CSVCheckinList(CheckInListMixin, ListExporter):
|
||||
|
||||
row.append(ia.company)
|
||||
row.append(op.voucher.code if op.voucher else "")
|
||||
row.append(op.order.datetime.astimezone(self.event.timezone).strftime('%Y-%m-%d'))
|
||||
yield row
|
||||
|
||||
def get_filename(self):
|
||||
|
||||
@@ -36,18 +36,6 @@ class Paypal(BasePaymentProvider):
|
||||
super().__init__(event)
|
||||
self.settings = SettingsSandbox('payment', 'paypal', event)
|
||||
|
||||
@property
|
||||
def test_mode_message(self):
|
||||
if self.settings.connect_client_id and not self.settings.secret:
|
||||
# in OAuth mode, sandbox mode needs to be set global
|
||||
is_sandbox = self.settings.connect_endpoint == 'sandbox'
|
||||
else:
|
||||
is_sandbox = self.settings.get('endpoint') == 'sandbox'
|
||||
if is_sandbox:
|
||||
return _('The PayPal sandbox is being used, you can test without actually sending money but you will need a '
|
||||
'PayPal sandbox user to log in.')
|
||||
return None
|
||||
|
||||
@property
|
||||
def settings_form_fields(self):
|
||||
if self.settings.connect_client_id and not self.settings.secret:
|
||||
|
||||
@@ -63,11 +63,6 @@
|
||||
</p>
|
||||
<h2>{% trans "3. Start scanning tickets" %}</h2>
|
||||
<script type="text/javascript" src="{% static "pretixplugins/pretixdroid/pretixdroid.js" %}"></script>
|
||||
{% if request.event.testmode %}
|
||||
<div class="alert-info">
|
||||
{% trans "Test mode orders will only be scanned if you scan online. If you scan in asynchronous mode, test mode orders won't be there." %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -332,7 +332,6 @@ class ApiDownloadView(ApiView):
|
||||
order__event=self.event,
|
||||
order__status__in=[Order.STATUS_PAID] + ([Order.STATUS_PENDING] if self.config.list.include_pending else
|
||||
[]),
|
||||
order__testmode=False,
|
||||
subevent=self.config.list.subevent
|
||||
).annotate(
|
||||
last_checked_in=Subquery(cqs)
|
||||
|
||||
@@ -118,8 +118,7 @@ class ReportlabExportMixin:
|
||||
|
||||
canvas.setFont('OpenSans', 10)
|
||||
canvas.drawString(15 * mm, self.pagesize[1] - 15 * mm,
|
||||
"%s – %s – %s" % (self.event.organizer.name, self.event.name,
|
||||
self.event.get_date_range_display()))
|
||||
"%s – %s" % (self.event.organizer.name, self.event.name))
|
||||
canvas.drawRightString(self.pagesize[0] - 15 * mm, self.pagesize[1] - 15 * mm,
|
||||
settings.PRETIX_INSTANCE_NAME)
|
||||
canvas.setStrokeColorRGB(0, 0, 0)
|
||||
|
||||
@@ -14,7 +14,6 @@ from django.template.loader import get_template
|
||||
from django.urls import reverse
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.http import urlquote
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import pgettext, ugettext, ugettext_lazy as _
|
||||
from django_countries import countries
|
||||
@@ -112,8 +111,6 @@ class StripeSettingsHolder(BasePaymentProvider):
|
||||
('live', pgettext('stripe', 'Live')),
|
||||
('test', pgettext('stripe', 'Testing')),
|
||||
),
|
||||
help_text=_('If your event is in test mode, we will always use Stripe\'s test API, '
|
||||
'regardless of this setting.')
|
||||
)),
|
||||
]
|
||||
else:
|
||||
@@ -233,21 +230,6 @@ class StripeMethod(BasePaymentProvider):
|
||||
super().__init__(event)
|
||||
self.settings = SettingsSandbox('payment', 'stripe', event)
|
||||
|
||||
@property
|
||||
def test_mode_message(self):
|
||||
if self.settings.connect_client_id and not self.settings.secret_key:
|
||||
is_testmode = True
|
||||
else:
|
||||
is_testmode = '_test_' in self.settings.secret_key
|
||||
if is_testmode:
|
||||
return mark_safe(
|
||||
_('The Stripe plugin is operating in test mode. You can use one of <a {args}>many test '
|
||||
'cards</a> to perform a transaction. No money will actually be transferred.').format(
|
||||
args='href="https://stripe.com/docs/testing#cards" target="_blank"'
|
||||
)
|
||||
)
|
||||
return None
|
||||
|
||||
@property
|
||||
def settings_form_fields(self):
|
||||
return {}
|
||||
@@ -280,7 +262,7 @@ class StripeMethod(BasePaymentProvider):
|
||||
@property
|
||||
def api_kwargs(self):
|
||||
if self.settings.connect_client_id and self.settings.connect_user_id:
|
||||
if self.settings.get('endpoint', 'live') == 'live' and not self.event.testmode:
|
||||
if self.settings.get('endpoint', 'live') == 'live':
|
||||
kwargs = {
|
||||
'api_key': self.settings.connect_secret_key,
|
||||
'stripe_account': self.settings.connect_user_id
|
||||
|
||||
@@ -34,14 +34,7 @@ def html_head_presale(sender, request=None, **kwargs):
|
||||
url = resolve(request.path_info)
|
||||
if provider.settings.get('_enabled', as_type=bool) and ("checkout" in url.url_name or "order.pay" in url.url_name):
|
||||
template = get_template('pretixplugins/stripe/presale_head.html')
|
||||
ctx = {
|
||||
'event': sender,
|
||||
'settings': provider.settings,
|
||||
'testmode': (
|
||||
(provider.settings.get('endpoint', 'live') == 'test' or sender.testmode)
|
||||
and provider.settings.publishable_test_key
|
||||
)
|
||||
}
|
||||
ctx = {'event': sender, 'settings': provider.settings}
|
||||
return template.render(ctx)
|
||||
else:
|
||||
return ""
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{% compress css %}
|
||||
<link type="text/css" rel="stylesheet" href="{% static "pretixplugins/stripe/pretix-stripe.css" %}">
|
||||
{% endcompress %}
|
||||
{% if testmode %}
|
||||
{% if settings.endpoint == "test" and settings.publishable_test_key %}
|
||||
<script type="text/plain" id="stripe_pubkey">{{ settings.publishable_test_key }}</script>
|
||||
{% else %}
|
||||
<script type="text/plain" id="stripe_pubkey">{{ settings.publishable_key }}</script>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user