Compare commits

..

47 Commits

Author SHA1 Message Date
Raphael Michel
dba9a56a67 Bump to 2.7.1 2019-05-14 09:16:14 +02:00
Raphael Michel
15f445cb3d Fix #1279 -- Incorrect initial price value in widget in German locale 2019-05-14 09:10:12 +02:00
Raphael Michel
c9f6c71c81 Fix #1282 -- Work around issues in file backends 2019-05-14 09:10:12 +02:00
Raphael Michel
2032d36ad6 Set original_price to TaxedPrice even with variations 2019-05-14 09:10:12 +02:00
Raphael Michel
ffb4cf08d1 Assign flag for zh_Hans 2019-05-14 09:10:12 +02:00
Raphael Michel
e89aaf4059 Bump to 2.7.0 2019-05-09 16:20:40 +02:00
Raphael Michel
db270b3bf2 Update from Weblate (#1276)
Update from Weblate
2019-05-09 14:48:51 +02:00
Raphael Michel
d8b78c3a7a Translated on translate.pretix.eu (German)
Currently translated at 100.0% (3067 of 3067 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2019-05-09 12:48:23 +00:00
Raphael Michel
67c448a29e Translated on translate.pretix.eu (German (informal))
Currently translated at 100.0% (3067 of 3067 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2019-05-09 12:48:22 +00:00
Raphael Michel
5b7906f2a1 Translated on translate.pretix.eu (German (informal))
Currently translated at 99.8% (3060 of 3067 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2019-05-09 12:45:12 +00:00
Raphael Michel
0612d42607 Translated on translate.pretix.eu (German)
Currently translated at 99.8% (3060 of 3067 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2019-05-09 12:45:12 +00:00
Raphael Michel
83f866034a Update po files
[CI skip]

Signed-off-by: Raphael Michel <mail@raphaelmichel.de>
2019-05-09 14:45:03 +02:00
Raphael Michel
b1fa214869 Clarify a description 2019-05-09 14:44:06 +02:00
Raphael Michel
aa53b5235a Update po files
[CI skip]

Signed-off-by: Raphael Michel <mail@raphaelmichel.de>
2019-05-09 14:39:58 +02:00
Raphael Michel
61a13256a0 Update from Weblate (#1269)
Update from Weblate
2019-05-09 14:39:16 +02:00
Raphael Michel
64e2336014 Added translation on translate.pretix.eu (Slovenian) 2019-05-09 11:47:16 +00:00
Bostjan Marusic
3411abd1e6 Added translation on translate.pretix.eu (Slovenian) 2019-05-09 11:47:16 +00:00
Allan Nordhøy
2a34e54fae Added translation on translate.pretix.eu (Norwegian Bokmål) 2019-05-09 11:47:16 +00:00
Alvaro Enrique Ruano
9863dc35d6 Translated on translate.pretix.eu (Spanish)
Currently translated at 99.0% (3031 of 3063 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/es/

powered by weblate
2019-05-09 11:47:16 +00:00
Raphael Michel
690883a198 Fix #480 -- Allow plugins to specify a minimum pretix version 2019-05-09 13:46:54 +02:00
Raphael Michel
d8ded08a46 Checkin list PDF: Remove date from headline, it's in the page header now 2019-05-09 10:52:42 +02:00
Raphael Michel
4aab5daa57 Fixing import order 2019-05-09 10:22:09 +02:00
Raphael Michel
e87628c902 Ensure that we document all signals 2019-05-09 10:02:12 +02:00
Raphael Michel
3c7bf46268 Resolve requests/urllib versions 2019-05-09 10:02:12 +02:00
Raphael Michel
a1dacb1897 Remove outdated reference from docs 2019-05-09 10:02:12 +02:00
Raphael Michel
08d5626704 Simplify the future of our migration history 2019-05-09 10:02:12 +02:00
Raphael Michel
c8a1481f93 Fix #1154 -- Add country-typed questions 2019-05-09 10:02:12 +02:00
Raphael Michel
e7c4121745 Add hidden questions 2019-05-09 10:02:12 +02:00
Sohalt
35ddd8dd28 Typo (#1274) 2019-05-08 13:13:27 +02:00
Raphael Michel
e2ec6eb156 Dekodi: Change semantics of signs 2019-05-08 11:47:57 +02:00
Raphael Michel
42edc4c3aa money_filter: Ignore case of currency 2019-05-07 16:20:24 +02:00
Raphael Michel
1cb2f99f3a Tax calculation of "original prices" 2019-05-06 12:33:21 +02:00
Raphael Michel
d4146e08b1 Fix widget tests 2019-05-06 12:06:45 +02:00
Raphael Michel
79ae9b6501 Revert "updatestyles: Fix a TypeError"
This reverts commit 53053f19e4.
2019-05-06 11:43:40 +02:00
Raphael Michel
c23f71a19c Widget: Add voucher explanation text 2019-05-06 11:33:48 +02:00
Raphael Michel
53053f19e4 updatestyles: Fix a TypeError 2019-05-06 11:33:36 +02:00
Raphael Michel
a42b2d76f6 Add scalability to docs word list 2019-05-06 08:50:56 +02:00
Raphael Michel
51392f73a8 Locking optimizations 2019-05-05 17:31:08 +02:00
Raphael Michel
465a5b01b9 Offload more work to database replica 2019-05-05 17:31:08 +02:00
Raphael Michel
74a6004613 Documentation on scaling 2019-05-05 17:31:08 +02:00
Sohalt
f9fc33eba1 Fix #1266 -- Make references to plugin settings clickable links (#1268) 2019-05-02 09:18:42 +02:00
Raphael Michel
363dc74c31 Dekodi: Try to find correct PayPal ID 2019-05-02 09:12:57 +02:00
Raphael Michel
efb598e93a Update from Weblate (#1267)
Update from Weblate
2019-05-01 14:13:47 +02:00
Raphael Michel
bcfaf2801d Translated on translate.pretix.eu (German)
Currently translated at 100.0% (3063 of 3063 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2019-05-01 12:13:28 +00:00
Raphael Michel
98db417fe6 Translated on translate.pretix.eu (German)
Currently translated at 100.0% (99 of 99 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/de/

powered by weblate
2019-05-01 12:13:18 +00:00
Raphael Michel
a03ffd949e Translated on translate.pretix.eu (German (informal))
Currently translated at 100.0% (3063 of 3063 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2019-05-01 12:12:58 +00:00
Raphael Michel
88ef46dee9 Translated on translate.pretix.eu (German (informal))
Currently translated at 100.0% (99 of 99 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/de_Informal/

powered by weblate
2019-05-01 12:12:39 +00:00
91 changed files with 41988 additions and 8910 deletions

View File

@@ -192,7 +192,8 @@ that you have a deep understanding of the semantics of your replication mechanis
If you do have a replica, you *can* tell pretix about it :ref:`in your configuration <config-replica>`.
This way, pretix can offload complex read-only queries to the replica when it is safe to do so.
As of pretix 2.6, this is mainly used for search queries in the backend and therefore does not really accelerate sales throughput, but we plan on expanding this in the future.
As of pretix 2.7, this is mainly used for search queries in the backend and for rendering the
product list and event lists in the frontend, but we plan on expanding this in the future.
Therefore, for now our clear recommendation is: Try to scale your database vertically and put
it on the most powerful machine you have available.
@@ -207,30 +208,29 @@ Having some memory available is good in case of e.g. lots of tasks queuing up du
Feel free to set up a redis cluster for availability but you won't need it for performance in a long time.
The locking issue
-----------------
The limitations
---------------
Currently, there is one big issue with scaling pretix. We take the reliability and correctness
of pretix' core features very seriously and one part of this is that we want to make absolutely
sure that pretix never sells the wrong number of tickets or tickets it otherwise shouldn't sell.
Up to a certain point, pretix scales really well. However, there are a few things that we consider
even more important than scalability, and those are correctness and reliability. We want you to be
able to trust that pretix will not sell more tickets than you intended or run into similar error
cases.
However, due to pretix flexibility and complexity, ensuring this in a high-concurrency environment is really hard.
We're not just counting down integers, since quota availability in pretix depends on a multitude of factors and requires a handful of complex database queries to calculate.
We can't rely on database-level locking alone to get this right.
Combined with pretix' flexibility and complexity, especially around vouchers and quotas, this creates
some hard issues. In many cases, we need to fall back to event-global locking for some actions which
are likely to run with high concurrency and cause harm.
Therefore, we currently use a very drastic option:
During relevant actions that typically occur with high concurrency, such as creating carts and completing orders, we create a system-wide lock for the event and all other such
actions need to wait. In essence, this means **for a given event we're only ever selling
one ticket at a time**.
For every event, only one of these locking actions can be run at the same time. Examples for this are
adding products limited by a quota to a cart, adding items to a cart using a voucher or placing an order
consisting of cart positions that don't have a valid reservation for much longer. In these cases, it is
currently not realistically possible to exceed selling **approx. 500 orders per minute per event**, even
if you add more hardware.
If you have an unlimited number of tickets, we can apply fewer locking and we've reached **approx.
1500 orders per minute per event** in benchmarks, although even more should be possible.
Therefore, it is currently very unlikely that you will be able to exceed **300-400 tickets per minute per event**.
For events up to a few thousand tickets, this isn't a problem and it's not even desirable to be able to sell much faster: If all tickets are reserved in the first minute, the shop shows a big "sold out" and everyone else goes away. Fifteen to thirty minutes later, depending on your settings, the first shopping carts will expire because people didn't actually go through with the purchase and tickets will become available again. This leads to a much more frustrating shopping experience for those trying to get a ticket than if tickets are sold at a slower pace and the first reservations expire before the last reservations are made. Not selling tickets through quickly (e.g. through a queue system) can do a lot to smooth out this process.
That said, we still want to fix this of course and make it possible to achieve much higher
throughput rates. We have some plans on how to soften this limitation, but they require
lots of time and effort to be realized. If you want to use pretix for an event with
10,000+ tickets that will be sold out within minutes, get in touch, we will make it work ;)
We're working to reduce the number of cases in which this is relevant and thereby improve the possible
throughput. If you want to use pretix for an event with 10,000+ tickets that are likely to be sold out
within minutes, please get in touch to discuss possible solutions. We'll work something out for you!
.. _object storage cluster: https://behind.pretix.eu/2018/03/20/high-available-cdn/

View File

@@ -30,6 +30,7 @@ type string The expected ty
* ``D`` date
* ``H`` time
* ``W`` date and time
* ``CC`` country code (ISO 3666-1 alpha-2)
required boolean If ``true``, the question needs to be filled out.
position integer An integer, used for sorting
items list of integers List of item IDs this question is assigned to.
@@ -38,6 +39,8 @@ identifier string An arbitrary st
ask_during_checkin boolean If ``true``, this question will not be asked while
buying the ticket, but will show up when redeeming
the ticket instead.
hidden boolean If ``true``, the question will only be shown in the
backend.
options list of objects In case of question type ``C`` or ``M``, this lists the
available objects. Only writable during creation,
use separate endpoint to modify this later.
@@ -68,6 +71,10 @@ dependency_value string The value ``dep
Write methods have been added. The attribute ``identifier`` has been added to both the resource itself and the
options resource. The ``position`` attribute has been added to the options resource.
.. versionchanged:: 2.7
The attribute ``hidden`` and the question type ``CC`` have been added.
Endpoints
---------
@@ -110,6 +117,7 @@ Endpoints
"position": 1,
"identifier": "WY3TP9SL",
"ask_during_checkin": false,
"hidden": false,
"dependency_question": null,
"dependency_value": null,
"options": [
@@ -177,6 +185,7 @@ Endpoints
"position": 1,
"identifier": "WY3TP9SL",
"ask_during_checkin": false,
"hidden": false,
"dependency_question": null,
"dependency_value": null,
"options": [
@@ -228,6 +237,7 @@ Endpoints
"items": [1, 2],
"position": 1,
"ask_during_checkin": false,
"hidden": false,
"dependency_question": null,
"dependency_value": null,
"options": [
@@ -261,6 +271,7 @@ Endpoints
"position": 1,
"identifier": "WY3TP9SL",
"ask_during_checkin": false,
"hidden": false,
"dependency_question": null,
"dependency_value": null,
"options": [
@@ -332,6 +343,7 @@ Endpoints
"position": 2,
"identifier": "WY3TP9SL",
"ask_during_checkin": false,
"hidden": false,
"dependency_question": null,
"dependency_value": null,
"options": [

View File

@@ -12,7 +12,7 @@ Core
.. automodule:: pretix.base.signals
:members: periodic_task, event_live_issues, event_copy_data, email_filter, register_notification_types,
item_copy_data, register_sales_channels
item_copy_data, register_sales_channels, register_global_settings
Order events
""""""""""""
@@ -26,11 +26,7 @@ Frontend
--------
.. automodule:: pretix.presale.signals
:members: html_head, html_footer, footer_link, front_page_top, front_page_bottom, fee_calculation_for_cart, contact_form_fields, question_form_fields, checkout_confirm_messages, checkout_confirm_page_content, checkout_all_optional, html_page_header, sass_preamble, sass_postamble
.. automodule:: pretix.presale.signals
:members: order_info, order_meta_from_request
:members: html_head, html_footer, footer_link, front_page_top, front_page_bottom, fee_calculation_for_cart, contact_form_fields, question_form_fields, checkout_confirm_messages, checkout_confirm_page_content, checkout_all_optional, html_page_header, sass_preamble, sass_postamble, checkout_flow_steps, order_info, order_meta_from_request
Request flow
""""""""""""

View File

@@ -49,15 +49,19 @@ description string A more verbose description of what your
visible boolean (optional) ``True`` by default, can hide a plugin so it cannot be normally activated.
restricted boolean (optional) ``False`` by default, restricts a plugin such that it can only be enabled
for an event by system administrators / superusers.
compatibility string Specifier for compatible pretix versions.
================== ==================== ===========================================================
A working example would be::
from django.apps import AppConfig
try:
from pretix.base.plugins import PluginConfig
except ImportError:
raise RuntimeError("Please use pretix 2.7 or above to run this plugin!")
from django.utils.translation import ugettext_lazy as _
class PaypalApp(AppConfig):
class PaypalApp(PluginConfig):
name = 'pretix_paypal'
verbose_name = _("PayPal")
@@ -68,6 +72,7 @@ A working example would be::
visible = True
restricted = False
description = _("This plugin allows you to receive payments via PayPal")
compatibility = "pretix>=2.7.0"
default_app_config = 'pretix_paypal.PaypalApp'

View File

@@ -23,7 +23,7 @@ Organizers and events
:members:
.. autoclass:: pretix.base.models.Event
:members: get_date_from_display, get_time_from_display, get_date_to_display, get_date_range_display, presale_has_ended, presale_is_running, cache, lock, get_plugins, get_mail_backend, payment_term_last, get_payment_providers, get_invoice_renderers, active_subevents, invoice_renderer, settings
:members: get_date_from_display, get_time_from_display, get_date_to_display, get_date_range_display, presale_has_ended, presale_is_running, cache, lock, get_plugins, get_mail_backend, payment_term_last, get_payment_providers, get_invoice_renderers, invoice_renderer, settings
.. autoclass:: pretix.base.models.SubEvent
:members: get_date_from_display, get_time_from_display, get_date_to_display, get_date_range_display, presale_has_ended, presale_is_running

View File

@@ -96,6 +96,7 @@ renderer
renderers
reportlab
SaaS
scalability
screenshot
scss
searchable

View File

@@ -1 +1 @@
__version__ = "2.6.0"
__version__ = "2.7.1"

View File

@@ -207,7 +207,8 @@ class QuestionSerializer(I18nAwareModelSerializer):
class Meta:
model = Question
fields = ('id', 'question', 'type', 'required', 'items', 'options', 'position',
'ask_during_checkin', 'identifier', 'dependency_question', 'dependency_value')
'ask_during_checkin', 'identifier', 'dependency_question', 'dependency_value',
'hidden')
def validate_identifier(self, value):
Question._clean_identifier(self.context['event'], value, self.instance)

View File

@@ -30,13 +30,13 @@ class DekodiNREIExporter(BaseExporter):
for l in invoice.lines.all():
positions.append({
'ADes': l.description.replace("<br />", "\n"),
'ANetA': round(float(l.net_value), 2),
'ANetA': round(float((-1 if invoice.is_cancellation else 1) * l.net_value), 2),
'ANo': self.event.slug,
'AQ': -1 if invoice.is_cancellation else 1,
'AVatP': round(float(l.tax_rate), 2),
'DIDt': (l.subevent or invoice.order.event).date_from.isoformat().replace('Z', '+00:00'),
'PosGrossA': round(float((-1 if invoice.is_cancellation else 1) * l.gross_value), 2),
'PosNetA': round(float((-1 if invoice.is_cancellation else 1) * l.net_value), 2),
'PosGrossA': round(float(l.gross_value), 2),
'PosNetA': round(float(l.net_value), 2),
})
gross_total += l.gross_value
net_total += l.net_value
@@ -50,7 +50,7 @@ class DekodiNREIExporter(BaseExporter):
if p.provider == 'paypal':
paypal_email = p.info_data.get('payer', {}).get('payer_info', {}).get('email')
try:
ppid = p.info_data['transactions'][0]['related_resources']['sale']['id']
ppid = p.info_data['transactions'][0]['related_resources'][0]['sale']['id']
except:
ppid = p.info_data.get('id')
payments.append({

View File

@@ -12,6 +12,7 @@ from django.core.exceptions import ValidationError
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from django_countries.fields import CountryField
from pretix.base.forms.widgets import (
BusinessBooleanRadio, DatePickerWidget, SplitDateTimePickerWidget,
@@ -213,6 +214,14 @@ class BaseQuestionsForm(forms.Form):
widget=forms.Textarea,
initial=initial.answer if initial else None,
)
elif q.type == Question.TYPE_COUNTRYCODE:
field = CountryField().formfield(
label=label, required=required,
help_text=help_text,
widget=forms.Select,
empty_label='',
initial=initial.answer if initial else None,
)
elif q.type == Question.TYPE_CHOICE:
field = forms.ModelChoiceField(
queryset=q.options,

View File

@@ -0,0 +1,51 @@
"""
Django, for theoretically very valid reasons, creates migrations for *every single thing*
we change on a model. Even the `help_text`! This makes sense, as we don't know if any
database backend unknown to us might actually use this information for its database schema.
However, pretix only supports PostgreSQL, MySQL, MariaDB and SQLite and we can be pretty
certain that some changes to models will never require a change to the database. In this case,
not creating a migration for certain changes will save us some performance while applying them
*and* allow for a cleaner git history. Win-win!
Only caveat is that we need to do some dirty monkeypatching to achieve it...
"""
from django.core.management.commands.makemigrations import Command as Parent
from django.db import models
from django.db.migrations.operations import models as modelops
from django_countries.fields import CountryField
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("verbose_name")
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("verbose_name_plural")
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("ordering")
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("get_latest_by")
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("default_manager_name")
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("permissions")
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("default_permissions")
IGNORED_ATTRS = [
# (field type, attribute name, blacklist of field sub-types)
(models.Field, 'verbose_name', []),
(models.Field, 'help_text', []),
(models.Field, 'validators', []),
(models.Field, 'editable', [models.DateField, models.DateTimeField, models.DateField, models.BinaryField]),
(models.Field, 'blank', [models.DateField, models.DateTimeField, models.AutoField, models.NullBooleanField,
models.TimeField]),
(models.CharField, 'choices', [CountryField])
]
original_deconstruct = models.Field.deconstruct
def new_deconstruct(self):
name, path, args, kwargs = original_deconstruct(self)
for ftype, attr, blacklist in IGNORED_ATTRS:
if isinstance(self, ftype) and not any(isinstance(self, ft) for ft in blacklist):
kwargs.pop(attr, None)
return name, path, args, kwargs
models.Field.deconstruct = new_deconstruct
class Command(Parent):
pass

View File

@@ -0,0 +1,28 @@
"""
Django tries to be helpful by suggesting to run "makemigrations" in red font on every "migrate"
run when there are things we have no migrations for. Usually, this is intended, and running
"makemigrations" can really screw up the environment of a user, so we want to prevent novice
users from doing that by going really dirty and fitlering it from the output.
"""
import sys
from django.core.management.base import OutputWrapper
from django.core.management.commands.migrate import Command as Parent
class OutputFilter(OutputWrapper):
blacklist = (
"Your models have changes that are not yet reflected",
"Run 'manage.py makemigrations' to make new "
)
def write(self, msg, style_func=None, ending=None):
if any(b in msg for b in self.blacklist):
return
super().write(msg, style_func, ending)
class Command(Parent):
def __init__(self, stdout=None, stderr=None, no_color=False, force_color=False):
super().__init__(stdout, stderr, no_color, force_color)
self.stdout = OutputFilter(stdout or sys.stdout)

View File

@@ -0,0 +1,22 @@
# Generated by Django 2.2 on 2019-05-09 06:54
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', '0118_auto_20190423_0839'),
]
operations = [
migrations.AddField(
model_name='question',
name='hidden',
field=models.BooleanField(default=False, help_text='This question will only show up in the backend.', verbose_name='Hidden question'),
),
]

View File

@@ -0,0 +1,77 @@
# Generated by Django 2.2 on 2019-05-09 07:36
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', '0119_auto_20190509_0654'),
]
operations = [
migrations.AlterField(
model_name='cartposition',
name='attendee_name_parts',
field=jsonfallback.fields.FallbackJSONField(default=dict),
),
migrations.AlterField(
model_name='cartposition',
name='subevent',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='pretixbase.SubEvent'),
),
migrations.AlterField(
model_name='cartposition',
name='voucher',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='pretixbase.Voucher'),
),
migrations.AlterField(
model_name='event',
name='is_public',
field=models.BooleanField(default=True),
),
migrations.AlterField(
model_name='invoiceaddress',
name='name_parts',
field=jsonfallback.fields.FallbackJSONField(default=dict),
),
migrations.AlterField(
model_name='item',
name='sales_channels',
field=pretix.base.models.fields.MultiStringField(default=['web']),
),
migrations.AlterField(
model_name='order',
name='sales_channel',
field=models.CharField(default='web', max_length=190),
),
migrations.AlterField(
model_name='orderposition',
name='attendee_name_parts',
field=jsonfallback.fields.FallbackJSONField(default=dict),
),
migrations.AlterField(
model_name='orderposition',
name='subevent',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='pretixbase.SubEvent'),
),
migrations.AlterField(
model_name='orderposition',
name='voucher',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='pretixbase.Voucher'),
),
migrations.AlterField(
model_name='staffsessionauditlog',
name='method',
field=models.CharField(max_length=255),
),
migrations.AlterField(
model_name='user',
name='email',
field=models.EmailField(db_index=True, max_length=190, null=True, unique=True),
),
]

View File

@@ -164,7 +164,7 @@ class EventMixin:
def annotated(cls, qs, channel='web'):
from pretix.base.models import Item, ItemVariation, Quota
sq_active_item = Item.objects.filter_available(channel=channel).filter(
sq_active_item = Item.objects.using(settings.DATABASE_REPLICA).filter_available(channel=channel).filter(
Q(variations__isnull=True)
& Q(quotas__pk=OuterRef('pk'))
).order_by().values_list('quotas__pk').annotate(
@@ -186,7 +186,7 @@ class EventMixin:
Prefetch(
'quotas',
to_attr='active_quotas',
queryset=Quota.objects.annotate(
queryset=Quota.objects.using(settings.DATABASE_REPLICA).annotate(
active_items=Subquery(sq_active_item, output_field=models.TextField()),
active_variations=Subquery(sq_active_variation, output_field=models.TextField()),
).exclude(

View File

@@ -16,6 +16,7 @@ from django.utils.crypto import get_random_string
from django.utils.functional import cached_property
from django.utils.timezone import is_naive, make_aware, now
from django.utils.translation import pgettext_lazy, ugettext_lazy as _
from django_countries.fields import Country
from i18nfield.fields import I18nCharField, I18nTextField
from pretix.base.models import fields
@@ -332,7 +333,9 @@ class Item(LoggedModel):
require_bundling = models.BooleanField(
verbose_name=_('Only sell this product as part of a bundle'),
default=False,
help_text=_('If this option is set, the product will only be sold as part of bundle products.')
help_text=_('If this option is set, the product will only be sold as part of bundle products. Do '
'<strong>not</strong> check this option if you want to use this product as an add-on product, '
'but only for fixed bundles!')
)
allow_cancel = models.BooleanField(
verbose_name=_('Allow product to be canceled'),
@@ -893,6 +896,8 @@ class Question(LoggedModel):
:param items: A set of ``Items`` objects that this question should be applied to
:param ask_during_checkin: Whether to ask this question during check-in instead of during check-out.
:type ask_during_checkin: bool
:param hidden: Whether to only show the question in the backend
:type hidden: bool
:param identifier: An arbitrary, internal identifier
:type identifier: str
:param dependency_question: This question will only show up if the referenced question is set to `dependency_value`.
@@ -910,6 +915,7 @@ class Question(LoggedModel):
TYPE_DATE = "D"
TYPE_TIME = "H"
TYPE_DATETIME = "W"
TYPE_COUNTRYCODE = "CC"
TYPE_CHOICES = (
(TYPE_NUMBER, _("Number")),
(TYPE_STRING, _("Text (one line)")),
@@ -921,6 +927,7 @@ class Question(LoggedModel):
(TYPE_DATE, _("Date")),
(TYPE_TIME, _("Time")),
(TYPE_DATETIME, _("Date and time")),
(TYPE_COUNTRYCODE, _("Country code (ISO 3166-1 alpha-2)")),
)
event = models.ForeignKey(
@@ -968,6 +975,11 @@ class Question(LoggedModel):
'pretixdesk 0.2 or newer.'),
default=False
)
hidden = models.BooleanField(
verbose_name=_('Hidden question'),
help_text=_('This question will only show up in the backend.'),
default=False
)
dependency_question = models.ForeignKey(
'Question', null=True, blank=True, on_delete=models.SET_NULL, related_name='dependent_questions'
)
@@ -1071,6 +1083,12 @@ class Question(LoggedModel):
return dt
except:
raise ValidationError(_('Invalid datetime input.'))
elif self.type == Question.TYPE_COUNTRYCODE and answer:
c = Country(answer.upper())
if c.name:
return answer
else:
raise ValidationError(_('Unknown country code.'))
return answer
@@ -1290,7 +1308,8 @@ class Quota(LoggedModel):
'cached_availability_state', 'cached_availability_number', 'cached_availability_time',
'cached_availability_paid_orders'
],
clear_cache=False
clear_cache=False,
using='default'
)
if _cache is not None:

View File

@@ -24,7 +24,7 @@ from django.utils.formats import date_format
from django.utils.functional import cached_property
from django.utils.timezone import make_aware, now
from django.utils.translation import pgettext_lazy, ugettext_lazy as _
from django_countries.fields import CountryField
from django_countries.fields import Country, CountryField
from i18nfield.strings import LazyI18nString
from jsonfallback.fields import FallbackJSONField
@@ -860,6 +860,8 @@ class QuestionAnswer(models.Model):
return date_format(d, "TIME_FORMAT")
except ValueError:
return self.answer
elif self.question.type == Question.TYPE_COUNTRYCODE and self.answer:
return Country(self.answer).name or self.answer
else:
return self.answer
@@ -976,7 +978,8 @@ class AbstractPosition(models.Model):
if hasattr(self.item, 'questions_to_ask'):
questions = list(copy.copy(q) for q in self.item.questions_to_ask)
else:
questions = list(copy.copy(q) for q in self.item.questions.filter(ask_during_checkin=False))
questions = list(copy.copy(q) for q in self.item.questions.filter(ask_during_checkin=False,
hidden=False))
else:
questions = list(copy.copy(q) for q in self.item.questions.all())

View File

@@ -1,8 +1,10 @@
import sys
from enum import Enum
from typing import List
from django.apps import apps
from django.apps import AppConfig, apps
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
class PluginType(Enum):
@@ -39,3 +41,22 @@ def get_all_plugins(event=None) -> List[type]:
plugins,
key=lambda m: (0 if m.module.startswith('pretix.') else 1, str(m.name).lower().replace('pretix ', ''))
)
class PluginConfig(AppConfig):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not hasattr(self, 'PretixPluginMeta'):
raise ImproperlyConfigured("A pretix plugin config should have a PretixPluginMeta inner class.")
if hasattr(self.PretixPluginMeta, 'compatibility'):
import pkg_resources
try:
pkg_resources.require(self.PretixPluginMeta.compatibility)
except pkg_resources.VersionConflict as e:
print("Incompatible plugins found!")
print("Plugin {} requires you to have {}, but you installed {}.".format(
self.name, e.req, e.dist
))
sys.exit(1)

View File

@@ -17,6 +17,7 @@ def money_filter(value: Decimal, arg='', hide_currency=False):
raise TypeError("Invalid data type passed to money filter: %r" % type(value))
if not arg:
raise ValueError("No currency passed.")
arg = arg.upper()
places = settings.CURRENCY_PLACES.get(arg, 2)
rounded = value.quantize(Decimal('1') / 10 ** places, ROUND_HALF_UP)

View File

@@ -279,7 +279,7 @@ class EventSettingsForm(SettingsForm):
)
display_net_prices = forms.BooleanField(
label=_("Show net prices instead of gross prices in the product list (not recommended!)"),
help_text=_("Independent of your choice, the cart will show gross prices as this the price that needs to be "
help_text=_("Independent of your choice, the cart will show gross prices as this is the price that needs to be "
"paid"),
required=False
)

View File

@@ -82,6 +82,7 @@ class QuestionForm(I18nModelForm):
'type',
'required',
'ask_during_checkin',
'hidden',
'identifier',
'items',
'dependency_question',

View File

@@ -2,16 +2,6 @@ from django.dispatch import Signal
from pretix.base.signals import DeprecatedSignal, EventPluginSignal
restriction_formset = EventPluginSignal(
providing_args=["item"]
)
"""
This signal is sent out to build configuration forms for all restriction formsets
(see plugin API documentation for details).
As with all plugin signals, the ``sender`` keyword argument will contain the event.
"""
html_page_start = Signal(
providing_args=[]
)

View File

@@ -38,7 +38,10 @@
{% empty %}
<tr>
<td colspan="3">
{% trans "There are no payment providers available. Please go to the plugin settings and activate one or more payment plugins." %}
{% url "control:event.settings.plugins" event=request.event.slug organizer=request.organizer.slug as plugin_settings_url %}
{% blocktrans trimmed with plugin_settings_href='href="'|add:plugin_settings_url|add:'"'|safe %}
There are no payment providers available. Please go to the <a {{ plugin_settings_href }}>plugin settings</a> and activate one or more payment plugins.
{% endblocktrans %}
</td>
</tr>
{% endfor %}

View File

@@ -40,7 +40,10 @@
</div>
{% empty %}
<div class="alert alert-warning">
{% trans "There are no ticket outputs available. Please go to the plugin settings and activate one or more ticket output plugins." %}</em>
{% url "control:event.settings.plugins" event=request.event.slug organizer=request.organizer.slug as plugin_settings_url %}
{% blocktrans trimmed with plugin_settings_href='href="'|add:plugin_settings_url|add:'"'|safe %}
There are no ticket outputs available. Please go to the <a {{ plugin_settings_href }}>plugin settings</a> and activate one or more ticket output plugins.
{% endblocktrans %}
</div>
{% endfor %}
</fieldset>

View File

@@ -109,6 +109,7 @@
{% bootstrap_field form.help_text layout="control" %}
{% bootstrap_field form.identifier layout="control" %}
{% bootstrap_field form.ask_during_checkin layout="control" %}
{% bootstrap_field form.hidden layout="control" %}
<div class="form-group">
<label class="col-md-3 control-label" for="id_dependency_question">

View File

@@ -15,6 +15,7 @@ from django.views.generic import ListView
from django.views.generic.base import TemplateView
from django.views.generic.detail import DetailView, SingleObjectMixin
from django.views.generic.edit import DeleteView
from django_countries.fields import Country
from pretix.base.forms import I18nFormSet
from pretix.base.models import (
@@ -444,6 +445,10 @@ class QuestionView(EventPermissionRequiredMixin, QuestionMixin, ChartContainingV
a['alink'] = a['answer']
a['answer'] = ugettext('Yes') if a['answer'] == 'True' else ugettext('No')
a['answer_bool'] = a['answer'] == 'True'
elif self.object.type == Question.TYPE_COUNTRYCODE:
for a in qs:
a['alink'] = a['answer']
a['answer'] = Country(a['answer']).name or a['answer']
return list(qs)

View File

@@ -76,3 +76,21 @@ class GroupConcat(Aggregate):
function='string_agg',
template="%(function)s(%(field)s::text, '%(separator)s')",
)
class ReplicaRouter:
def db_for_read(self, model, **hints):
return 'default'
def db_for_write(self, model, **hints):
return 'default'
def allow_relation(self, obj1, obj2, **hints):
db_list = ('default', 'replica')
if obj1._state.db in db_list and obj2._state.db in db_list:
return True
return None
def allow_migrate(self, db, app_label, model_name=None, **hintrs):
return True

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+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

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+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

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+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

View File

@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+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

View File

@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"PO-Revision-Date: 2019-04-23 08:55+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+0000\n"
"PO-Revision-Date: 2019-05-01 12:13+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix-js/"
"de/>\n"
@@ -222,10 +222,8 @@ msgid "Click to close"
msgstr "Klicken zum Schließen"
#: pretix/static/pretixcontrol/js/ui/orderchange.js:24
#, fuzzy
#| msgid "Contacting Stripe …"
msgid "Calculating default price…"
msgstr "Kontaktiere Stripe …"
msgstr "Berechne Standardpreis…"
#: pretix/static/pretixcontrol/js/ui/question.js:42
msgid "Others"
@@ -267,7 +265,7 @@ msgstr[1] ""
#: pretix/static/pretixpresale/js/ui/main.js:201
msgid "Please enter a quantity for one of the ticket types."
msgstr ""
msgstr "Bitte tragen Sie eine Menge für eines der Produkte ein."
#: pretix/static/pretixpresale/js/widget/widget.js:15
msgctxt "widget"

File diff suppressed because it is too large Load Diff

View File

@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"PO-Revision-Date: 2019-04-23 08:55+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+0000\n"
"PO-Revision-Date: 2019-05-01 12:12+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
"pretix/pretix-js/de_Informal/>\n"
@@ -221,10 +221,8 @@ msgid "Click to close"
msgstr "Klicken zum Schließen"
#: pretix/static/pretixcontrol/js/ui/orderchange.js:24
#, fuzzy
#| msgid "Contacting Stripe …"
msgid "Calculating default price…"
msgstr "Kontaktiere Stripe …"
msgstr "Berechne Standardpreis…"
#: pretix/static/pretixcontrol/js/ui/question.js:42
msgid "Others"
@@ -266,7 +264,7 @@ msgstr[1] ""
#: pretix/static/pretixpresale/js/ui/main.js:201
msgid "Please enter a quantity for one of the ticket types."
msgstr ""
msgstr "Bitte trage eine Menge für eines der Produkte ein."
#: pretix/static/pretixpresale/js/widget/widget.js:15
msgctxt "widget"

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+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

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+0000\n"
"PO-Revision-Date: 2019-04-24 22:00+0000\n"
"Last-Translator: ThanosTeste <testebasisth@unisystems.eu>\n"
"Language-Team: Greek <https://translate.pretix.eu/projects/pretix/pretix-js/"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+0000\n"
"PO-Revision-Date: 2019-03-31 08:00+0000\n"
"Last-Translator: oocf <oswaldocerna@gmail.com>\n"
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix-"

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: French\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+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

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+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

View File

@@ -0,0 +1,479 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-09 12:45+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"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:68
msgid "Marked as paid"
msgstr ""
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:76
msgid "Comment:"
msgstr ""
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:15
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
msgid "Placed orders"
msgstr ""
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:15
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
msgid "Paid orders"
msgstr ""
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:27
msgid "Total revenue"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
msgid "Contacting Stripe …"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:56
msgid "Total"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:39
#: pretix/static/pretixbase/js/asynctask.js:95
msgid ""
"Your request has been queued on the server and will now be processed. "
"Depending on the size of your event, this might take up to a few minutes."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:45
#: pretix/static/pretixbase/js/asynctask.js:101
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 ""
#: pretix/static/pretixbase/js/asynctask.js:66
#: pretix/static/pretixbase/js/asynctask.js:124
#: pretix/static/pretixcontrol/js/ui/mail.js:23
msgid "An error of type {code} occurred."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:69
msgid ""
"We currently cannot reach the server, but we keep trying. Last error code: "
"{code}"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:115
#: pretix/static/pretixcontrol/js/ui/mail.js:20
msgid "The request took to long. Please try again."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:127
#: pretix/static/pretixcontrol/js/ui/mail.js:25
msgid ""
"We currently cannot reach the server. Please try again. Error code: {code}"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:148
msgid "We are processing your request …"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:156
msgid ""
"We are currently sending your request to the server. If this takes longer "
"than one minute, please check your internet connection and then reload this "
"page and try again."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:193
#: pretix/static/pretixcontrol/js/ui/main.js:20
msgid "Close message"
msgstr ""
#: pretix/static/pretixcontrol/js/clipboard.js:23
msgid "Copied!"
msgstr ""
#: pretix/static/pretixcontrol/js/clipboard.js:29
msgid "Press Ctrl-C to copy!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:43
msgid "Lead Scan QR"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:45
msgid "Check-in QR"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:249
msgid "The PDF background file could not be loaded for the following reason:"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:418
msgid "Group of objects"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:424
msgid "Text object"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:426
msgid "Barcode area"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:428
msgid "Powered by pretix"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:430
msgid "Object"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:434
msgid "Ticket design"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:687
msgid "Saving failed."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:735
msgid "Do you really want to leave the editor without saving your changes?"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:749
msgid "Error while uploading your PDF file, please try again."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/mail.js:18
msgid "An error has occurred."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/mail.js:52
msgid "Generating messages …"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:55
msgid "Unknown error."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:217
msgid "Your color has great contrast and is very easy to read!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:221
msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:225
msgid ""
"Your color has bad contrast for text on white background, please choose a "
"darker shade."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:305
msgid "All"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:306
msgid "None"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:595
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:652
msgid "Click to close"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/orderchange.js:24
msgid "Calculating default price…"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:42
msgid "Others"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:71
msgid "Count"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:120
msgid "Yes"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:121
msgid "No"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/subevent.js:108
msgid "(one more date)"
msgid_plural "({num} more dates)"
msgstr[0] ""
msgstr[1] ""
#: pretix/static/pretixpresale/js/ui/cart.js:39
msgid "The items in your cart are no longer reserved for you."
msgstr ""
#: pretix/static/pretixpresale/js/ui/cart.js:41
msgid "Cart expired"
msgstr ""
#: pretix/static/pretixpresale/js/ui/cart.js:46
msgid "The items in your cart are reserved for you for one minute."
msgid_plural "The items in your cart are reserved for you for {num} minutes."
msgstr[0] ""
msgstr[1] ""
#: pretix/static/pretixpresale/js/ui/main.js:201
msgid "Please enter a quantity for one of the ticket types."
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:15
msgctxt "widget"
msgid "Sold out"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:16
msgctxt "widget"
msgid "Buy"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:17
msgctxt "widget"
msgid "Reserved"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:18
msgctxt "widget"
msgid "FREE"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:19
msgctxt "widget"
msgid "from %(currency)s %(price)s"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:20
msgctxt "widget"
msgid "incl. %(rate)s% %(taxname)s"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:21
msgctxt "widget"
msgid "plus %(rate)s% %(taxname)s"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:22
msgctxt "widget"
msgid "incl. taxes"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:23
msgctxt "widget"
msgid "plus taxes"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:24
#, javascript-format
msgctxt "widget"
msgid "currently available: %s"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:25
msgctxt "widget"
msgid "Only available with a voucher"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:26
#, javascript-format
msgctxt "widget"
msgid "minimum amount to order: %s"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:27
msgctxt "widget"
msgid "Close ticket shop"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:28
msgctxt "widget"
msgid "The ticket shop could not be loaded."
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:29
msgctxt "widget"
msgid "The cart could not be created. Please try again later"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:30
msgctxt "widget"
msgid "Waiting list"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:31
msgctxt "widget"
msgid ""
"You currently have an active cart for this event. If you select more "
"products, they will be added to your existing cart."
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:33
msgctxt "widget"
msgid "Resume checkout"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:34
msgctxt "widget"
msgid ""
"<a href=\"https://pretix.eu\" target=\"_blank\" rel=\"noopener\">event "
"ticketing powered by pretix</a>"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:36
msgctxt "widget"
msgid "Redeem a voucher"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:37
msgctxt "widget"
msgid "Redeem"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:38
msgctxt "widget"
msgid "Voucher code"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:39
msgctxt "widget"
msgid "Close"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:40
msgctxt "widget"
msgid "Continue"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:41
msgctxt "widget"
msgid "See variations"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:42
msgctxt "widget"
msgid "Choose a different event"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:43
msgctxt "widget"
msgid "Choose a different date"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:44
msgctxt "widget"
msgid "Back"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:45
msgctxt "widget"
msgid "Next month"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:46
msgctxt "widget"
msgid "Previous month"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:48
msgid "Mo"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:49
msgid "Tu"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:50
msgid "We"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:51
msgid "Th"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:52
msgid "Fr"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:53
msgid "Sa"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:54
msgid "Su"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:57
msgid "January"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:58
msgid "February"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:59
msgid "March"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:60
msgid "April"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:61
msgid "May"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:62
msgid "June"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:63
msgid "July"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:64
msgid "August"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:65
msgid "September"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:66
msgid "October"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:67
msgid "November"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:68
msgid "December"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+0000\n"
"PO-Revision-Date: 2019-04-27 21:00+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

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+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

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+0000\n"
"PO-Revision-Date: 2019-04-27 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

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+0000\n"
"PO-Revision-Date: 2019-03-15 11:19+0000\n"
"Last-Translator: Serge Bazanski <q3k@hackerspace.pl>\n"
"Language-Team: Polish <https://translate.pretix.eu/projects/pretix/pretix-js/"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+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

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+0000\n"
"PO-Revision-Date: 2019-03-19 09:00+0000\n"
"Last-Translator: Vitor Reis <vitor.reis7@gmail.com>\n"
"Language-Team: Portuguese (Brazil) <https://translate.pretix.eu/projects/"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+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

View File

@@ -0,0 +1,483 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-09 12:45+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: sl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n"
"%100==4 ? 2 : 3;\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:68
msgid "Marked as paid"
msgstr ""
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:76
msgid "Comment:"
msgstr ""
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:15
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
msgid "Placed orders"
msgstr ""
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:15
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
msgid "Paid orders"
msgstr ""
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:27
msgid "Total revenue"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
msgid "Contacting Stripe …"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:56
msgid "Total"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:39
#: pretix/static/pretixbase/js/asynctask.js:95
msgid ""
"Your request has been queued on the server and will now be processed. "
"Depending on the size of your event, this might take up to a few minutes."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:45
#: pretix/static/pretixbase/js/asynctask.js:101
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 ""
#: pretix/static/pretixbase/js/asynctask.js:66
#: pretix/static/pretixbase/js/asynctask.js:124
#: pretix/static/pretixcontrol/js/ui/mail.js:23
msgid "An error of type {code} occurred."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:69
msgid ""
"We currently cannot reach the server, but we keep trying. Last error code: "
"{code}"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:115
#: pretix/static/pretixcontrol/js/ui/mail.js:20
msgid "The request took to long. Please try again."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:127
#: pretix/static/pretixcontrol/js/ui/mail.js:25
msgid ""
"We currently cannot reach the server. Please try again. Error code: {code}"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:148
msgid "We are processing your request …"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:156
msgid ""
"We are currently sending your request to the server. If this takes longer "
"than one minute, please check your internet connection and then reload this "
"page and try again."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:193
#: pretix/static/pretixcontrol/js/ui/main.js:20
msgid "Close message"
msgstr ""
#: pretix/static/pretixcontrol/js/clipboard.js:23
msgid "Copied!"
msgstr ""
#: pretix/static/pretixcontrol/js/clipboard.js:29
msgid "Press Ctrl-C to copy!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:43
msgid "Lead Scan QR"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:45
msgid "Check-in QR"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:249
msgid "The PDF background file could not be loaded for the following reason:"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:418
msgid "Group of objects"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:424
msgid "Text object"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:426
msgid "Barcode area"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:428
msgid "Powered by pretix"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:430
msgid "Object"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:434
msgid "Ticket design"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:687
msgid "Saving failed."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:735
msgid "Do you really want to leave the editor without saving your changes?"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:749
msgid "Error while uploading your PDF file, please try again."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/mail.js:18
msgid "An error has occurred."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/mail.js:52
msgid "Generating messages …"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:55
msgid "Unknown error."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:217
msgid "Your color has great contrast and is very easy to read!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:221
msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:225
msgid ""
"Your color has bad contrast for text on white background, please choose a "
"darker shade."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:305
msgid "All"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:306
msgid "None"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:595
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:652
msgid "Click to close"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/orderchange.js:24
msgid "Calculating default price…"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:42
msgid "Others"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:71
msgid "Count"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:120
msgid "Yes"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:121
msgid "No"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/subevent.js:108
msgid "(one more date)"
msgid_plural "({num} more dates)"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
#: pretix/static/pretixpresale/js/ui/cart.js:39
msgid "The items in your cart are no longer reserved for you."
msgstr ""
#: pretix/static/pretixpresale/js/ui/cart.js:41
msgid "Cart expired"
msgstr ""
#: pretix/static/pretixpresale/js/ui/cart.js:46
msgid "The items in your cart are reserved for you for one minute."
msgid_plural "The items in your cart are reserved for you for {num} minutes."
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
#: pretix/static/pretixpresale/js/ui/main.js:201
msgid "Please enter a quantity for one of the ticket types."
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:15
msgctxt "widget"
msgid "Sold out"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:16
msgctxt "widget"
msgid "Buy"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:17
msgctxt "widget"
msgid "Reserved"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:18
msgctxt "widget"
msgid "FREE"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:19
msgctxt "widget"
msgid "from %(currency)s %(price)s"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:20
msgctxt "widget"
msgid "incl. %(rate)s% %(taxname)s"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:21
msgctxt "widget"
msgid "plus %(rate)s% %(taxname)s"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:22
msgctxt "widget"
msgid "incl. taxes"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:23
msgctxt "widget"
msgid "plus taxes"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:24
#, javascript-format
msgctxt "widget"
msgid "currently available: %s"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:25
msgctxt "widget"
msgid "Only available with a voucher"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:26
#, javascript-format
msgctxt "widget"
msgid "minimum amount to order: %s"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:27
msgctxt "widget"
msgid "Close ticket shop"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:28
msgctxt "widget"
msgid "The ticket shop could not be loaded."
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:29
msgctxt "widget"
msgid "The cart could not be created. Please try again later"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:30
msgctxt "widget"
msgid "Waiting list"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:31
msgctxt "widget"
msgid ""
"You currently have an active cart for this event. If you select more "
"products, they will be added to your existing cart."
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:33
msgctxt "widget"
msgid "Resume checkout"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:34
msgctxt "widget"
msgid ""
"<a href=\"https://pretix.eu\" target=\"_blank\" rel=\"noopener\">event "
"ticketing powered by pretix</a>"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:36
msgctxt "widget"
msgid "Redeem a voucher"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:37
msgctxt "widget"
msgid "Redeem"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:38
msgctxt "widget"
msgid "Voucher code"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:39
msgctxt "widget"
msgid "Close"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:40
msgctxt "widget"
msgid "Continue"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:41
msgctxt "widget"
msgid "See variations"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:42
msgctxt "widget"
msgid "Choose a different event"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:43
msgctxt "widget"
msgid "Choose a different date"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:44
msgctxt "widget"
msgid "Back"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:45
msgctxt "widget"
msgid "Next month"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:46
msgctxt "widget"
msgid "Previous month"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:48
msgid "Mo"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:49
msgid "Tu"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:50
msgid "We"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:51
msgid "Th"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:52
msgid "Fr"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:53
msgid "Sa"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:54
msgid "Su"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:57
msgid "January"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:58
msgid "February"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:59
msgid "March"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:60
msgid "April"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:61
msgid "May"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:62
msgid "June"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:63
msgid "July"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:64
msgid "August"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:65
msgid "September"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:66
msgid "October"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:67
msgid "November"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:68
msgid "December"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+0000\n"
"PO-Revision-Date: 2019-04-27 21:00+0000\n"
"Last-Translator: Tobias Sundgren <syrgas@gmail.com>\n"
"Language-Team: Swedish <https://translate.pretix.eu/projects/pretix/pretix-"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+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-"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01 12:02+0000\n"
"POT-Creation-Date: 2019-05-09 12:45+0000\n"
"PO-Revision-Date: 2019-03-28 14:00+0000\n"
"Last-Translator: yichengsd <sunzl@jxepub.com>\n"
"Language-Team: Chinese (Simplified) <https://translate.pretix.eu/projects/"

View File

@@ -196,7 +196,7 @@ class PDFCheckinList(ReportlabExportMixin, CheckInListMixin, BaseExporter):
story = [
Paragraph(
'{} {}'.format(cl.name, (cl.subevent or self.event).get_date_from_display()),
cl.name,
headlinestyle
),
Spacer(1, 5 * mm)

View File

@@ -1,7 +1,7 @@
import hashlib
from django.conf import settings
from django.core.files.base import ContentFile
from django.core.files.base import ContentFile, File
from django.core.files.storage import default_storage
from django.core.management.base import BaseCommand
@@ -35,4 +35,7 @@ class Command(BaseCommand):
gs.settings.set('widget_file_{}'.format(lc), 'file://' + newname)
gs.settings.set('widget_checksum_{}'.format(lc), checksum)
if fname:
default_storage.delete(fname)
if isinstance(fname, File):
default_storage.delete(fname.name)
else:
default_storage.delete(fname)

View File

@@ -82,7 +82,8 @@ As with all plugin signals, the ``sender`` keyword argument will contain the eve
checkout_flow_steps = EventPluginSignal()
"""
This signal is sent out to retrieve pages for the checkout flow
This signal is sent out to retrieve pages for the checkout flow. Receivers are expected to return
a subclass of ``pretix.presale.checkoutflow.BaseCheckoutFlowStep``.
As with all plugin signals, the ``sender`` keyword argument will contain the event.
"""

View File

@@ -271,10 +271,11 @@
</div>
<div class="col-md-2 col-xs-6 price">
{% if var.original_price %}
<del>{{ var.original_price|money:event.currency }}</del>
<ins>
{% elif item.original_price %}
<del>{{ item.original_price|money:event.currency }}</del>
{% if event.settings.display_net_prices %}
<del>{{ var.original_price.net|money:event.currency }}</del>
{% else %}
<del>{{ var.original_price.gross|money:event.currency }}</del>
{% endif %}
<ins>
{% endif %}
{% if item.free_price %}
@@ -386,7 +387,11 @@
</div>
<div class="col-md-2 col-xs-6 price">
{% if item.original_price %}
<del>{{ item.original_price|money:event.currency }}</del>
{% if event.settings.display_net_prices %}
<del>{{ item.original_price.net|money:event.currency }}</del>
{% else %}
<del>{{ item.original_price.gross|money:event.currency }}</del>
{% endif %}
<ins>
{% endif %}
{% if item.free_price %}

View File

@@ -94,10 +94,11 @@
</div>
<div class="col-md-2 col-xs-6 price">
{% if var.original_price %}
<del>{{ var.original_price|money:event.currency }}</del>
<ins>
{% elif item.original_price %}
<del>{{ item.original_price|money:event.currency }}</del>
{% if event.settings.display_net_prices %}
<del>{{ var.original_price.net|money:event.currency }}</del>
{% else %}
<del>{{ var.original_price.gross|money:event.currency }}</del>
{% endif %}
<ins>
{% endif %}
{% if item.free_price %}
@@ -200,7 +201,11 @@
</div>
<div class="col-md-2 col-xs-6 price">
{% if item.original_price %}
<del>{{ item.original_price|money:event.currency }}</del>
{% if event.settings.display_net_prices %}
<del>{{ item.original_price.net|money:event.currency }}</del>
{% else %}
<del>{{ item.original_price.gross|money:event.currency }}</del>
{% endif %}
<ins>
{% endif %}
{% if item.free_price %}

View File

@@ -21,6 +21,10 @@ def _detect_event(request, require_live=True, require_plugin=None):
if hasattr(request, '_event_detected'):
return
db = 'default'
if request.method == 'GET':
db = settings.DATABASE_REPLICA
url = resolve(request.path_info)
try:
if hasattr(request, 'organizer_domain'):
@@ -31,24 +35,24 @@ def _detect_event(request, require_live=True, require_plugin=None):
path = "/" + request.get_full_path().split("/", 2)[-1]
return redirect(path)
request.event = request.organizer.events\
.get(
slug=url.kwargs['event'],
organizer=request.organizer,
)
request.event = request.organizer.events.using(db).get(
slug=url.kwargs['event'],
organizer=request.organizer,
)
request.organizer = request.organizer
else:
# We are on our main domain
if 'event' in url.kwargs and 'organizer' in url.kwargs:
request.event = Event.objects\
.select_related('organizer')\
.using(db)\
.get(
slug=url.kwargs['event'],
organizer__slug=url.kwargs['organizer']
)
request.organizer = request.event.organizer
elif 'organizer' in url.kwargs:
request.organizer = Organizer.objects.get(
request.organizer = Organizer.objects.using(db).get(
slug=url.kwargs['organizer']
)
else:

View File

@@ -50,32 +50,34 @@ def item_group_by_category(items):
def get_grouped_items(event, subevent=None, voucher=None, channel='web'):
items = event.items.filter_available(channel=channel, voucher=voucher).select_related(
items = event.items.using(settings.DATABASE_REPLICA).filter_available(channel=channel, voucher=voucher).select_related(
'category', 'tax_rule', # for re-grouping
).prefetch_related(
Prefetch('quotas',
to_attr='_subevent_quotas',
queryset=event.quotas.filter(subevent=subevent)),
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(subevent=subevent)),
Prefetch('bundles',
queryset=ItemBundle.objects.prefetch_related(
queryset=ItemBundle.objects.using(settings.DATABASE_REPLICA).prefetch_related(
Prefetch('bundled_item',
queryset=event.items.select_related('tax_rule').prefetch_related(
queryset=event.items.using(settings.DATABASE_REPLICA).select_related('tax_rule').prefetch_related(
Prefetch('quotas',
to_attr='_subevent_quotas',
queryset=event.quotas.filter(subevent=subevent)),
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(subevent=subevent)),
)),
Prefetch('bundled_variation',
queryset=ItemVariation.objects.select_related('item', 'item__tax_rule').filter(item__event=event).prefetch_related(
queryset=ItemVariation.objects.using(
settings.DATABASE_REPLICA
).select_related('item', 'item__tax_rule').filter(item__event=event).prefetch_related(
Prefetch('quotas',
to_attr='_subevent_quotas',
queryset=event.quotas.filter(subevent=subevent)),
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(subevent=subevent)),
)),
)),
Prefetch('variations', to_attr='available_variations',
queryset=ItemVariation.objects.filter(active=True, quotas__isnull=False).prefetch_related(
queryset=ItemVariation.objects.using(settings.DATABASE_REPLICA).filter(active=True, quotas__isnull=False).prefetch_related(
Prefetch('quotas',
to_attr='_subevent_quotas',
queryset=event.quotas.filter(subevent=subevent))
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(subevent=subevent))
).distinct()),
).annotate(
quotac=Count('quotas'),
@@ -134,10 +136,22 @@ def get_grouped_items(event, subevent=None, voucher=None, channel='web'):
item.display_price = item.tax(price, currency=event.currency, include_bundled=True)
if price != original_price:
item.original_price = original_price
item.original_price = item.tax(original_price, currency=event.currency, include_bundled=True)
else:
item.original_price = (
item.tax(item.original_price, currency=event.currency, include_bundled=True,
base_price_is='net' if event.settings.display_net_prices else 'gross') # backwards-compat
if item.original_price else None
)
display_add_to_cart = display_add_to_cart or item.order_max > 0
else:
item.original_price = (
item.tax(item.original_price, currency=event.currency, include_bundled=True,
base_price_is='net' if event.settings.display_net_prices else 'gross') # backwards-compat
if item.original_price else None
)
for var in item.available_variations:
if voucher and (voucher.allow_ignore_quota or voucher.block_quota):
var.cached_availability = (
@@ -163,7 +177,13 @@ def get_grouped_items(event, subevent=None, voucher=None, channel='web'):
var.display_price = var.tax(price, currency=event.currency, include_bundled=True)
if price != original_price:
var.original_price = original_price
var.original_price = var.tax(original_price, currency=event.currency, include_bundled=True)
else:
var.original_price = (
var.tax(var.original_price or item.original_price, currency=event.currency,
include_bundled=True,
base_price_is='net' if event.settings.display_net_prices else 'gross') # backwards-compat
) if var.original_price or item.original_price else None
display_add_to_cart = display_add_to_cart or var.order_max > 0
@@ -229,7 +249,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
if request.event.has_subevents:
if 'subevent' in kwargs:
self.subevent = request.event.subevents.filter(pk=kwargs['subevent'], active=True).first()
self.subevent = request.event.subevents.using(settings.DATABASE_REPLICA).filter(pk=kwargs['subevent'], active=True).first()
if not self.subevent:
raise Http404()
return super().get(request, *args, **kwargs)
@@ -287,7 +307,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
ebd = defaultdict(list)
add_subevents_for_days(
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel), self.request),
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel).using(settings.DATABASE_REPLICA), self.request),
before, after, ebd, set(), self.request.event,
kwargs.get('cart_namespace')
)
@@ -297,7 +317,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
context['years'] = range(now().year - 2, now().year + 3)
else:
context['subevent_list'] = self.request.event.subevents_sorted(
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel), self.request)
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel).using(settings.DATABASE_REPLICA), self.request)
)
context['show_cart'] = (

View File

@@ -93,7 +93,7 @@ class EventListMixin:
def _get_event_queryset(self):
query = Q(is_public=True) & Q(live=True)
qs = self.request.organizer.events.filter(query)
qs = self.request.organizer.events.using(settings.DATABASE_REPLICA).filter(query)
qs = qs.annotate(
min_from=Min('subevents__date_from'),
min_to=Min('subevents__date_to'),
@@ -126,7 +126,7 @@ class EventListMixin:
def _set_month_to_next_subevent(self):
tz = pytz.timezone(self.request.event.settings.timezone)
next_sev = self.request.event.subevents.filter(
next_sev = self.request.event.subevents.using(settings.DATABASE_REPLICA).filter(
active=True,
is_public=True,
date_from__gte=now()
@@ -141,14 +141,14 @@ class EventListMixin:
self.month = now().month
def _set_month_to_next_event(self):
next_ev = filter_qs_by_attr(Event.objects.filter(
next_ev = filter_qs_by_attr(Event.objects.using(settings.DATABASE_REPLICA).filter(
organizer=self.request.organizer,
live=True,
is_public=True,
date_from__gte=now(),
has_subevents=False
), self.request).order_by('date_from').first()
next_sev = filter_qs_by_attr(SubEvent.objects.filter(
next_sev = filter_qs_by_attr(SubEvent.objects.using(settings.DATABASE_REPLICA).filter(
event__organizer=self.request.organizer,
event__is_public=True,
event__live=True,
@@ -353,14 +353,14 @@ class CalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
def _events_by_day(self, before, after):
ebd = defaultdict(list)
timezones = set()
add_events_for_days(self.request, Event.annotated(self.request.organizer.events, 'web'), before, after, ebd, timezones)
add_events_for_days(self.request, Event.annotated(self.request.organizer.events, 'web').using(settings.DATABASE_REPLICA), before, after, ebd, timezones)
add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter(
event__organizer=self.request.organizer,
event__is_public=True,
event__live=True,
).prefetch_related(
'event___settings_objects', 'event__organizer___settings_objects'
)), self.request), before, after, ebd, timezones)
)), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones)
self._multiple_timezones = len(timezones) > 1
return ebd

View File

@@ -15,7 +15,7 @@ class QuestionsViewMixin(BaseQuestionsViewMixin):
def _positions_for_questions(self):
qqs = self.request.event.questions.all()
if self.only_user_visible:
qqs = qqs.filter(ask_during_checkin=False)
qqs = qqs.filter(ask_during_checkin=False, hidden=False)
cart = get_cart(self.request).select_related(
'addon_to'
).prefetch_related(

View File

@@ -200,7 +200,12 @@ class WidgetAPIProductList(EventListMixin, View):
item.cached_availability[0],
item.cached_availability[1] if self.request.event.settings.show_quota_left else None
] if not item.has_variations else None,
'original_price': item.original_price,
'original_price': (
(item.original_price.net
if self.request.event.settings.display_net_prices
else item.original_price.gross)
if item.original_price else None
),
'variations': [
{
'id': var.id,
@@ -208,7 +213,19 @@ class WidgetAPIProductList(EventListMixin, View):
'order_max': var.order_max,
'description': str(rich_text(var.description, safelinks=False)) if var.description else None,
'price': price_dict(item, var.display_price),
'original_price': getattr(var, 'original_price') or item.original_price,
'original_price': (
(
var.original_price.net
if self.request.event.settings.display_net_prices
else var.original_price.gross
) if var.original_price else None
) or (
(
item.original_price.net
if self.request.event.settings.display_net_prices
else item.original_price.gross
) if item.original_price else None
),
'avail': [
var.cached_availability[0],
var.cached_availability[1] if self.request.event.settings.show_quota_left else None
@@ -434,6 +451,7 @@ class WidgetAPIProductList(EventListMixin, View):
'display_net_prices': request.event.settings.display_net_prices,
'show_variations_expanded': request.event.settings.show_variations_expanded,
'waiting_list_enabled': request.event.settings.waiting_list_enabled,
'voucher_explanation_text': str(request.event.settings.voucher_explanation_text),
'error': None,
'cart_exists': False
}

View File

@@ -109,6 +109,7 @@ if config.has_section('replica'):
'COLLATION': 'utf8mb4_unicode_ci',
} if 'mysql' in db_backend else {}
}
DATABASE_ROUTERS = ['pretix.helpers.database.ReplicaRouter']
STATIC_URL = config.get('urls', 'static', fallback='/static/')

View File

@@ -66,6 +66,7 @@ pre[lang=ck], input[lang=ck], textarea[lang=ck], div[lang=ck] { background-image
pre[lang=cl], input[lang=cl], textarea[lang=cl], div[lang=cl] { background-image: url(static('pretixbase/img/flags/cl.png')); }
pre[lang=cm], input[lang=cm], textarea[lang=cm], div[lang=cm] { background-image: url(static('pretixbase/img/flags/cm.png')); }
pre[lang=cn], input[lang=cn], textarea[lang=cn], div[lang=cn] { background-image: url(static('pretixbase/img/flags/cn.png')); }
pre[lang=zh-hans], input[lang=zh-hans], textarea[lang=zh-hans], div[lang=zh-hans] { background-image: url(static('pretixbase/img/flags/cn.png')); }
pre[lang=co], input[lang=co], textarea[lang=co], div[lang=co] { background-image: url(static('pretixbase/img/flags/co.png')); }
pre[lang=cr], input[lang=cr], textarea[lang=cr], div[lang=cr] { background-image: url(static('pretixbase/img/flags/cr.png')); }
pre[lang=cs], input[lang=cs], textarea[lang=cs], div[lang=cs] { background-image: url(static('pretixbase/img/flags/cs.png')); }

View File

@@ -234,7 +234,7 @@ Vue.component('pricebox', {
+ '<div v-if="free_price">'
+ '{{ $root.currency }} '
+ '<input type="number" class="pretix-widget-pricebox-price-input" placeholder="0" '
+ ' :min="display_price" :value="display_price" :name="field_name"'
+ ' :min="display_price_nonlocalized" :value="display_price_nonlocalized" :name="field_name"'
+ ' step="any">'
+ '</div>'
+ '<small class="pretix-widget-pricebox-tax" v-if="price.rate != \'0.00\' && price.gross != \'0.00\'">'
@@ -255,6 +255,13 @@ Vue.component('pricebox', {
return floatformat(parseFloat(this.price.gross), 2);
}
},
display_price_nonlocalized: function () {
if (this.$root.display_net_prices) {
return parseFloat(this.price.net).toFixed(2);
} else {
return parseFloat(this.price.gross).toFixed(2);
}
},
original_line: function () {
return this.$root.currency + " " + floatformat(parseFloat(this.original_price), 2);
},
@@ -680,6 +687,7 @@ Vue.component('pretix-widget-event-form', {
+ ' v-if="$root.vouchers_exist && !$root.disable_vouchers && !$root.voucher_code">'
+ '<div class="pretix-widget-voucher">'
+ '<h3 class="pretix-widget-voucher-headline">'+ strings['redeem_voucher'] +'</h3>'
+ '<div v-if="$root.voucher_explanation_text" class="pretix-widget-voucher-text">{{ $root.voucher_explanation_text }}</div>'
+ '<div class="pretix-widget-voucher-input-wrap">'
+ '<input class="pretix-widget-voucher-input" type="text" v-model="$parent.voucher" name="voucher" placeholder="'+strings.voucher_code+'">'
+ '</div>'
@@ -1045,6 +1053,7 @@ var shared_root_methods = {
root.categories = data.items_by_category;
root.currency = data.currency;
root.display_net_prices = data.display_net_prices;
root.voucher_explanation_text = data.voucher_explanation_text;
root.error = data.error;
root.display_add_to_cart = data.display_add_to_cart;
root.waiting_list_enabled = data.waiting_list_enabled;
@@ -1200,6 +1209,7 @@ var create_widget = function (element) {
name: null,
voucher_code: voucher,
display_net_prices: false,
voucher_explanation_text: null,
show_variations_expanded: false,
skip_ssl: skip_ssl,
style: style,

View File

@@ -273,6 +273,10 @@
.pretix-widget-voucher-headline {
margin: 10px 0 0 0;
}
.pretix-widget-voucher-text {
margin: 10px 0;
padding: 0 15px;
}
.pretix-widget-voucher-input-wrap {
padding: 0 15px;

View File

@@ -2,6 +2,7 @@
Django==2.2.*
djangorestframework==3.9.*
python-dateutil==2.8.*
requests==2.21.0
pytz
django-bootstrap3==11.0.*
django-formset-js-improved==0.5.0.2
@@ -53,7 +54,6 @@ pyuca # for better sorting of country names in django-countries
defusedcsv>=1.0.1
vat_moss==0.11.0
django-localflavor
idna==2.6 # required by cureent requests
urllib3==1.24.2 # required by cureent requests
urllib3==1.24.*
django-redis==4.10.*
redis==3.2.*

View File

@@ -90,6 +90,7 @@ setup(
'Django==2.2.*',
'djangorestframework==3.9.*',
'python-dateutil==2.8.*',
'requests==2.21.*',
'pytz',
'django-bootstrap3==11.0.*',
'django-formset-js-improved==0.5.0.2',
@@ -140,8 +141,7 @@ setup(
'openpyxl',
'django-oauth-toolkit==1.2.*',
'oauthlib==2.1.*',
'idna==2.6', # required by current requests
'urllib3==1.24.2', # required by current requests
'urllib3==1.24.*', # required by current requests
],
extras_require={
'dev': [

View File

@@ -1591,6 +1591,7 @@ TEST_QUESTION_RES = {
"required": False,
"items": [],
"ask_during_checkin": False,
"hidden": False,
"identifier": "ABC",
"position": 0,
"dependency_question": None,

View File

@@ -197,7 +197,8 @@ class WidgetCartTest(CartTestMixin, TestCase):
],
"itemnum": 2,
"display_add_to_cart": True,
"cart_exists": False
"cart_exists": False,
"voucher_explanation_text": "",
}
def test_product_list_view_with_voucher(self):
@@ -240,6 +241,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
}
],
"itemnum": 1,
"voucher_explanation_text": "",
"display_add_to_cart": True,
"cart_exists": False
}
@@ -303,6 +305,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
],
"itemnum": 1,
"display_add_to_cart": True,
"voucher_explanation_text": "",
"cart_exists": False
}
@@ -322,6 +325,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
"items_by_category": [],
"display_add_to_cart": False,
"cart_exists": False,
"voucher_explanation_text": "",
"itemnum": 0,
}