mirror of
https://github.com/pretix/pretix.git
synced 2026-05-14 16:44:06 +00:00
Compare commits
12 Commits
quota-webh
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26d6ed7072 | ||
|
|
bda27d72e7 | ||
|
|
f67690bc56 | ||
|
|
75c8f97080 | ||
|
|
10789f097d | ||
|
|
1adec102e6 | ||
|
|
921fd801e5 | ||
|
|
448d2e70d5 | ||
|
|
49f692c666 | ||
|
|
2d31c62812 | ||
|
|
08df3d828d | ||
|
|
96e10bcd71 |
@@ -27,7 +27,7 @@ classifiers = [
|
||||
]
|
||||
|
||||
dependencies = [
|
||||
"arabic-reshaper==3.0.0", # Support for Arabic in reportlab
|
||||
"arabic-reshaper==3.0.1", # Support for Arabic in reportlab
|
||||
"babel",
|
||||
"BeautifulSoup4==4.14.*",
|
||||
"bleach==6.3.*",
|
||||
|
||||
@@ -19,4 +19,4 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
__version__ = "2026.4.0.dev0"
|
||||
__version__ = "2026.5.0.dev0"
|
||||
|
||||
@@ -923,7 +923,7 @@ class Renderer:
|
||||
|
||||
# We do not use str.format like in emails so we (a) can evaluate lazily and (b) can re-implement this
|
||||
# 1:1 on other platforms that render PDFs through our API (libpretixprint)
|
||||
return re.sub(r'\{([a-zA-Z0-9:_]+)\}', replace, text)
|
||||
return re.sub(r'\{([-a-zA-Z0-9:_]+)\}', replace, text)
|
||||
|
||||
elif o['content'].startswith('itemmeta:'):
|
||||
if op.variation_id:
|
||||
|
||||
@@ -173,6 +173,7 @@ def create_thumbnail(source, size, formats=None):
|
||||
# filesystem path, this only works because _open() uses safe_join, which accepts absolute paths if they match the
|
||||
# expected base dir. For NanoCDN Files, this works because source.name is set to the storage path.
|
||||
source_rb = default_storage.open(source_name, mode='rb')
|
||||
source_ext = os.path.splitext(source_name)[1].lower()
|
||||
|
||||
image = Image.open(BytesIO(source_rb.read()), formats=formats or settings.PILLOW_FORMATS_QUESTIONS_IMAGE)
|
||||
try:
|
||||
@@ -183,11 +184,14 @@ def create_thumbnail(source, size, formats=None):
|
||||
frames = []
|
||||
durations = []
|
||||
for f in ImageSequence.Iterator(image):
|
||||
if f.mode in ("P", "PA") and source_ext == '.png':
|
||||
f = f.convert('RGBA')
|
||||
if f.mode not in ("1", "L", "RGB", "RGBA"):
|
||||
f = f.convert('RGB')
|
||||
durations.append(f.info.get("duration", 1000))
|
||||
frames.append(resize_image(f, size))
|
||||
image_out = frames[0]
|
||||
save_kwargs = {}
|
||||
source_ext = os.path.splitext(source_name)[1].lower()
|
||||
|
||||
if source_ext == '.jpg' or source_ext == '.jpeg':
|
||||
# Yields better file sizes for photos
|
||||
@@ -211,10 +215,6 @@ def create_thumbnail(source, size, formats=None):
|
||||
checksum = hashlib.md5(image.tobytes()).hexdigest()
|
||||
name = checksum + '.' + size.replace('^', 'c') + '.' + target_ext
|
||||
buffer = BytesIO()
|
||||
if image_out.mode == "P" and source_ext == '.png':
|
||||
image_out = image_out.convert('RGBA')
|
||||
if image_out.mode not in ("1", "L", "RGB", "RGBA"):
|
||||
image_out = image_out.convert('RGB')
|
||||
image_out.save(fp=buffer, format=target_ext.upper(), quality=quality, **save_kwargs)
|
||||
imgfile = ContentFile(buffer.getvalue())
|
||||
|
||||
|
||||
@@ -5,16 +5,16 @@ msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-04-28 09:03+0000\n"
|
||||
"PO-Revision-Date: 2026-03-30 11:50+0000\n"
|
||||
"PO-Revision-Date: 2026-04-28 09:22+0000\n"
|
||||
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
||||
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix/de/"
|
||||
">\n"
|
||||
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix/"
|
||||
"de/>\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.16.2\n"
|
||||
"X-Generator: Weblate 5.17\n"
|
||||
"X-Poedit-Bookmarks: -1,-1,904,-1,-1,-1,-1,-1,-1,-1\n"
|
||||
|
||||
#: pretix/_base_settings.py:87
|
||||
@@ -8226,10 +8226,8 @@ msgstr ""
|
||||
"2x Workshop 2"
|
||||
|
||||
#: pretix/base/pdf.py:383
|
||||
#, fuzzy
|
||||
#| msgid "List of Add-Ons"
|
||||
msgid "List of Checked-In Add-Ons"
|
||||
msgstr "Liste der Zusatz-Produkte"
|
||||
msgstr "Liste der eingecheckten Zusatzprodukte"
|
||||
|
||||
#: pretix/base/pdf.py:390 pretix/control/forms/filter.py:1537
|
||||
#: pretix/control/forms/filter.py:1539
|
||||
@@ -9236,10 +9234,8 @@ msgid "Czech National Bank"
|
||||
msgstr "Tschechische Nationalbank"
|
||||
|
||||
#: pretix/base/services/currencies.py:41
|
||||
#, fuzzy
|
||||
#| msgid "Czech National Bank"
|
||||
msgid "National Bank of Poland"
|
||||
msgstr "Tschechische Nationalbank"
|
||||
msgstr "Nationalbank von Polen"
|
||||
|
||||
#: pretix/base/services/export.py:95 pretix/base/services/export.py:155
|
||||
msgid ""
|
||||
@@ -10330,16 +10326,12 @@ msgstr ""
|
||||
"Rechnungsbetrag nicht in CZK ist."
|
||||
|
||||
#: pretix/base/settings.py:577 pretix/base/settings.py:586
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Based on Czech National Bank daily rates, whenever the invoice amount is "
|
||||
#| "not in CZK."
|
||||
msgid ""
|
||||
"Based on National Bank of Poland daily rates, whenever the invoice amount is "
|
||||
"not in PLN."
|
||||
msgstr ""
|
||||
"Basierend auf den Kursen der Tschechischen Nationalbank, immer wenn der "
|
||||
"Rechnungsbetrag nicht in CZK ist."
|
||||
"Basierend auf den Kursen der Nationalbank von Polen, immer wenn der "
|
||||
"Rechnungsbetrag nicht in PLN ist."
|
||||
|
||||
#: pretix/base/settings.py:597
|
||||
msgid "Require invoice address"
|
||||
@@ -16440,10 +16432,8 @@ msgid "Allow to overbook quotas when performing this operation"
|
||||
msgstr "Überbuchen von Kontingenten bei dieser Aktion erlauben"
|
||||
|
||||
#: pretix/control/forms/orders.py:335
|
||||
#, fuzzy
|
||||
#| msgid "Number of orders"
|
||||
msgid "Number of products to add"
|
||||
msgstr "Anzahl Bestellungen"
|
||||
msgstr "Anzahl hinzuzufügender Produkt"
|
||||
|
||||
#: pretix/control/forms/orders.py:344
|
||||
msgid "Add-on to"
|
||||
@@ -16475,10 +16465,10 @@ msgstr ""
|
||||
"Produktes."
|
||||
|
||||
#: pretix/control/forms/orders.py:441
|
||||
#, fuzzy
|
||||
#| msgid "You can not select the same seat multiple times."
|
||||
msgid "You can not choose a seat when adding multiple products at once."
|
||||
msgstr "Sie können den gleichen Sitzplatz nicht mehrfach auswählen."
|
||||
msgstr ""
|
||||
"Sie können keinen Sitzplatz auswählen, wenn mehrere Produkte auf einmal "
|
||||
"hinzugefügt werden."
|
||||
|
||||
#: pretix/control/forms/orders.py:478 pretix/control/forms/orders.py:482
|
||||
#: pretix/control/forms/orders.py:510 pretix/control/forms/orders.py:552
|
||||
@@ -17097,7 +17087,7 @@ msgstr "Wochenendtag"
|
||||
#: pretix/control/forms/subevents.py:106
|
||||
msgctxt "subevent"
|
||||
msgid "Skip dates that overlap with any existing date"
|
||||
msgstr ""
|
||||
msgstr "Überspringe Termine, die mit einem bestehenden Termin überlappen"
|
||||
|
||||
#: pretix/control/forms/subevents.py:109
|
||||
msgctxt "subevent"
|
||||
@@ -17107,6 +17097,10 @@ msgid ""
|
||||
"This respects even inactive dates and works best if all dates have both a "
|
||||
"start and end time."
|
||||
msgstr ""
|
||||
"Dies kann nützlich sein, wenn alle Termine am selben Ort stattfinden und "
|
||||
"wiederholte Termine nicht in Konflikt mit existierenden Sonderterminen "
|
||||
"geraten sollen. Dies berücksichtigt auch inaktive Termine und funktioniert "
|
||||
"am besten, wenn alle Termine eine Start- und Endzeit haben."
|
||||
|
||||
#: pretix/control/forms/subevents.py:128
|
||||
msgid "Keep the current values"
|
||||
@@ -30276,6 +30270,8 @@ msgstr "Bitte erstelle maximal 100.000 Termine auf einmal."
|
||||
#: pretix/control/views/subevents.py:966
|
||||
msgid "All dates would be skipped because they conflict with existing dates."
|
||||
msgstr ""
|
||||
"Alle Termine würden übersprungen werden, weil sie mit existierenden Terminen "
|
||||
"überlappen."
|
||||
|
||||
#: pretix/control/views/subevents.py:1102
|
||||
#, python-brace-format
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-04-28 09:03+0000\n"
|
||||
"PO-Revision-Date: 2026-03-30 11:50+0000\n"
|
||||
"PO-Revision-Date: 2026-04-28 09:22+0000\n"
|
||||
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
||||
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
|
||||
"pretix/pretix/de_Informal/>\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 5.16.2\n"
|
||||
"X-Generator: Weblate 5.17\n"
|
||||
|
||||
#: pretix/_base_settings.py:87
|
||||
msgid "English"
|
||||
@@ -8219,10 +8219,8 @@ msgstr ""
|
||||
"2x Workshop 2"
|
||||
|
||||
#: pretix/base/pdf.py:383
|
||||
#, fuzzy
|
||||
#| msgid "List of Add-Ons"
|
||||
msgid "List of Checked-In Add-Ons"
|
||||
msgstr "Liste der Zusatz-Produkte"
|
||||
msgstr "Liste der eingecheckten Zusatzprodukte"
|
||||
|
||||
#: pretix/base/pdf.py:390 pretix/control/forms/filter.py:1537
|
||||
#: pretix/control/forms/filter.py:1539
|
||||
@@ -9226,10 +9224,8 @@ msgid "Czech National Bank"
|
||||
msgstr "Tschechische Nationalbank"
|
||||
|
||||
#: pretix/base/services/currencies.py:41
|
||||
#, fuzzy
|
||||
#| msgid "Czech National Bank"
|
||||
msgid "National Bank of Poland"
|
||||
msgstr "Tschechische Nationalbank"
|
||||
msgstr "Nationalbank von Polen"
|
||||
|
||||
#: pretix/base/services/export.py:95 pretix/base/services/export.py:155
|
||||
msgid ""
|
||||
@@ -10318,16 +10314,12 @@ msgstr ""
|
||||
"Rechnungsbetrag nicht in CZK ist."
|
||||
|
||||
#: pretix/base/settings.py:577 pretix/base/settings.py:586
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Based on Czech National Bank daily rates, whenever the invoice amount is "
|
||||
#| "not in CZK."
|
||||
msgid ""
|
||||
"Based on National Bank of Poland daily rates, whenever the invoice amount is "
|
||||
"not in PLN."
|
||||
msgstr ""
|
||||
"Basierend auf den Kursen der Tschechischen Nationalbank, immer wenn der "
|
||||
"Rechnungsbetrag nicht in CZK ist."
|
||||
"Basierend auf den Kursen der Nationalbank von Polen, immer wenn der "
|
||||
"Rechnungsbetrag nicht in PLN ist."
|
||||
|
||||
#: pretix/base/settings.py:597
|
||||
msgid "Require invoice address"
|
||||
@@ -16417,10 +16409,8 @@ msgid "Allow to overbook quotas when performing this operation"
|
||||
msgstr "Überbuchen von Kontingenten bei dieser Aktion erlauben"
|
||||
|
||||
#: pretix/control/forms/orders.py:335
|
||||
#, fuzzy
|
||||
#| msgid "Number of orders"
|
||||
msgid "Number of products to add"
|
||||
msgstr "Anzahl Bestellungen"
|
||||
msgstr "Anzahl hinzuzufügender Produkt"
|
||||
|
||||
#: pretix/control/forms/orders.py:344
|
||||
msgid "Add-on to"
|
||||
@@ -16452,10 +16442,10 @@ msgstr ""
|
||||
"Produktes."
|
||||
|
||||
#: pretix/control/forms/orders.py:441
|
||||
#, fuzzy
|
||||
#| msgid "You can not select the same seat multiple times."
|
||||
msgid "You can not choose a seat when adding multiple products at once."
|
||||
msgstr "Du kannst den gleichen Sitzplatz nicht mehrfach auswählen."
|
||||
msgstr ""
|
||||
"Du kannst keinen Sitzplatz auswählen, wenn mehrere Produkte auf einmal "
|
||||
"hinzugefügt werden."
|
||||
|
||||
#: pretix/control/forms/orders.py:478 pretix/control/forms/orders.py:482
|
||||
#: pretix/control/forms/orders.py:510 pretix/control/forms/orders.py:552
|
||||
@@ -17075,7 +17065,7 @@ msgstr "Wochenendtag"
|
||||
#: pretix/control/forms/subevents.py:106
|
||||
msgctxt "subevent"
|
||||
msgid "Skip dates that overlap with any existing date"
|
||||
msgstr ""
|
||||
msgstr "Überspringe Termine, die mit einem bestehenden Termin überlappen"
|
||||
|
||||
#: pretix/control/forms/subevents.py:109
|
||||
msgctxt "subevent"
|
||||
@@ -17085,6 +17075,10 @@ msgid ""
|
||||
"This respects even inactive dates and works best if all dates have both a "
|
||||
"start and end time."
|
||||
msgstr ""
|
||||
"Dies kann nützlich sein, wenn alle Termine am selben Ort stattfinden und "
|
||||
"wiederholte Termine nicht in Konflikt mit existierenden Sonderterminen "
|
||||
"geraten sollen. Dies berücksichtigt auch inaktive Termine und funktioniert "
|
||||
"am besten, wenn alle Termine eine Start- und Endzeit haben."
|
||||
|
||||
#: pretix/control/forms/subevents.py:128
|
||||
msgid "Keep the current values"
|
||||
@@ -30232,6 +30226,8 @@ msgstr "Bitte erstelle maximal 100.000 Termine auf einmal."
|
||||
#: pretix/control/views/subevents.py:966
|
||||
msgid "All dates would be skipped because they conflict with existing dates."
|
||||
msgstr ""
|
||||
"Alle Termine würden übersprungen werden, weil sie mit existierenden Terminen "
|
||||
"überlappen."
|
||||
|
||||
#: pretix/control/views/subevents.py:1102
|
||||
#, python-brace-format
|
||||
|
||||
@@ -37,6 +37,7 @@ import uuid
|
||||
from collections import defaultdict
|
||||
from decimal import Decimal
|
||||
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.core.cache import caches
|
||||
@@ -69,6 +70,7 @@ from pretix.base.services.cart import (
|
||||
from pretix.base.services.cross_selling import CrossSellingService
|
||||
from pretix.base.services.memberships import validate_memberships_in_order
|
||||
from pretix.base.services.orders import perform_order
|
||||
from pretix.base.services.pricing import get_price
|
||||
from pretix.base.services.tasks import EventTask
|
||||
from pretix.base.settings import PERSON_NAME_SCHEMES
|
||||
from pretix.base.signals import validate_cart_addons
|
||||
@@ -529,6 +531,48 @@ class AddOnsStep(CartMixin, AsyncAction, TemplateFlowStep):
|
||||
self._completed = True
|
||||
return True
|
||||
|
||||
def _get_initial_val_price(self, current_addon_products, cartpos, item, variation):
|
||||
val = None
|
||||
price = None
|
||||
|
||||
if self.request.POST:
|
||||
if variation:
|
||||
field = f'cp_{cartpos.pk}_variation_{item.pk}_{variation.pk}'
|
||||
else:
|
||||
field = f'cp_{cartpos.pk}_item_{item.pk}'
|
||||
|
||||
try:
|
||||
val = int(self.request.POST.get(field) or '0')
|
||||
except ValueError:
|
||||
pass
|
||||
if val and item.free_price:
|
||||
custom_price = forms.DecimalField(localize=True).to_python(self.request.POST.get(f'{field}_price') or '0')
|
||||
price = get_price(
|
||||
item, variation, voucher=cartpos.voucher, custom_price=custom_price, subevent=cartpos.subevent,
|
||||
custom_price_is_net=self.event.settings.display_net_prices,
|
||||
invoice_address=self.invoice_address,
|
||||
)
|
||||
else:
|
||||
price = variation.suggested_price if variation else item.suggested_price
|
||||
|
||||
else:
|
||||
current_products = current_addon_products[item.pk, variation.pk if variation else None]
|
||||
val = len(current_products)
|
||||
if current_products and item.free_price:
|
||||
a = current_products[0]
|
||||
price = TaxedPrice(
|
||||
net=a.price - a.tax_value,
|
||||
gross=a.price,
|
||||
tax=a.tax_value,
|
||||
name=a.item.tax_rule.name if a.item.tax_rule else "",
|
||||
rate=a.tax_rate,
|
||||
code=a.item.tax_rule.code if a.item.tax_rule else None,
|
||||
)
|
||||
else:
|
||||
price = variation.suggested_price if variation else item.suggested_price
|
||||
|
||||
return val, price
|
||||
|
||||
@cached_property
|
||||
def forms(self):
|
||||
"""
|
||||
@@ -587,34 +631,10 @@ class AddOnsStep(CartMixin, AsyncAction, TemplateFlowStep):
|
||||
|
||||
if i.has_variations:
|
||||
for v in i.available_variations:
|
||||
v.initial = len(current_addon_products[i.pk, v.pk])
|
||||
if v.initial and i.free_price:
|
||||
a = current_addon_products[i.pk, v.pk][0]
|
||||
v.initial_price = TaxedPrice(
|
||||
net=a.price - a.tax_value,
|
||||
gross=a.price,
|
||||
tax=a.tax_value,
|
||||
name=a.item.tax_rule.name if a.item.tax_rule else "",
|
||||
rate=a.tax_rate,
|
||||
code=a.item.tax_rule.code if a.item.tax_rule else None,
|
||||
)
|
||||
else:
|
||||
v.initial_price = v.suggested_price
|
||||
v.initial, v.initial_price = self._get_initial_val_price(current_addon_products, cartpos, i, v)
|
||||
i.expand = any(v.initial for v in i.available_variations)
|
||||
else:
|
||||
i.initial = len(current_addon_products[i.pk, None])
|
||||
if i.initial and i.free_price:
|
||||
a = current_addon_products[i.pk, None][0]
|
||||
i.initial_price = TaxedPrice(
|
||||
net=a.price - a.tax_value,
|
||||
gross=a.price,
|
||||
tax=a.tax_value,
|
||||
name=a.item.tax_rule.name if a.item.tax_rule else "",
|
||||
rate=a.tax_rate,
|
||||
code=a.item.tax_rule.code if a.item.tax_rule else None,
|
||||
)
|
||||
else:
|
||||
i.initial_price = i.suggested_price
|
||||
i.initial, i.initial_price = self._get_initial_val_price(current_addon_products, cartpos, i, None)
|
||||
|
||||
if items:
|
||||
formsetentry['categories'].append({
|
||||
|
||||
@@ -95,17 +95,17 @@
|
||||
<div class="col-md-2 col-sm-3 col-xs-6 availability-box">
|
||||
{% if not event.settings.show_variations_expanded %}
|
||||
<button type="button" data-toggle="variations" class="btn btn-default btn-block js-only"
|
||||
data-label-alt="{% trans "Hide variants" %}"
|
||||
aria-expanded="false" aria-controls="cp-{{ form.pos.pk }}-item-{{ item.pk }}-variations"
|
||||
data-label-alt="{% if item.expand %}{% trans "Show variants" %}{% else %}{% trans "Hide variants" %}{% endif %}"
|
||||
aria-expanded="{{ item.expand|yesno:"true,false" }}" aria-controls="cp-{{ form.pos.pk }}-item-{{ item.pk }}-variations"
|
||||
aria-describedby="cp-{{ form.pos.pk }}-item-{{ item.pk }}-legend">
|
||||
<i class="fa fa-angle-down collapse-indicator" aria-hidden="true"></i>
|
||||
<span>{% trans "Show variants" %}</span>
|
||||
<span>{% if item.expand %}{% trans "Hide variants" %}{% else %}{% trans "Show variants" %}{% endif %}</span>
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="variations {% if not event.settings.show_variations_expanded %}variations-collapsed{% endif %}" id="cp-{{ form.pos.pk }}-item-{{ item.pk }}-variations">
|
||||
<div class="variations {% if not event.settings.show_variations_expanded and not item.expand %}variations-collapsed{% endif %}" id="cp-{{ form.pos.pk }}-item-{{ item.pk }}-variations">
|
||||
{% for var in item.available_variations %}
|
||||
<article aria-labelledby="cp-{{ form.pos.pk }}-item-{{ item.pk }}-{{ var.pk }}-legend"{% if var.description %} aria-describedby="cp-{{ form.pos.pk }}-item-{{ item.pk }}-{{ var.pk }}-description"{% endif %} class="row-fluid product-row variation"
|
||||
{% if not item.free_price %}
|
||||
|
||||
@@ -99,6 +99,15 @@ class BaseCheckoutTestCase:
|
||||
self.workshopquota.variations.add(self.workshop2a)
|
||||
self.workshopquota.variations.add(self.workshop2b)
|
||||
|
||||
self.parkingcat = ItemCategory.objects.create(name="Parking", is_addon=True, event=self.event)
|
||||
self.parkingquota = Quota.objects.create(event=self.event, name='Parking', size=5)
|
||||
self.parking1 = Item.objects.create(event=self.event, name='Premium Parking',
|
||||
category=self.parkingcat, default_price=Decimal('15.00'))
|
||||
self.parking2 = Item.objects.create(event=self.event, name='Standard Parking',
|
||||
category=self.parkingcat, default_price=Decimal('5.00'))
|
||||
self.parkingquota.items.add(self.parking1)
|
||||
self.parkingquota.items.add(self.parking2)
|
||||
|
||||
def _set_session(self, key, value):
|
||||
session = self.client.session
|
||||
session['carts'][get_cart_session_key(self.client, self.event)][key] = value
|
||||
@@ -4202,6 +4211,58 @@ class CheckoutTestCase(BaseCheckoutTestCase, TimemachineTestMixin, TestCase):
|
||||
assert '35.29' in response.content.decode()
|
||||
assert '10.08' in response.content.decode()
|
||||
|
||||
def test_set_addons_invalid_initial(self):
|
||||
self.event.settings.locales = ['de', 'en']
|
||||
self.event.settings.locale = 'de'
|
||||
with scopes_disabled():
|
||||
ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat, min_count=1)
|
||||
ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.parkingcat, min_count=1)
|
||||
cp1 = CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
price=23, expires=now() - timedelta(minutes=10)
|
||||
)
|
||||
self.workshop1.free_price = True
|
||||
self.workshop1.save()
|
||||
self.workshop2.free_price = True
|
||||
self.workshop2.save()
|
||||
|
||||
ws1_val = 'cp_{}_item_{}'.format(cp1.pk, self.workshop1.pk)
|
||||
ws1_price = 'cp_{}_item_{}_price'.format(cp1.pk, self.workshop1.pk)
|
||||
ws2a_val = 'cp_{}_variation_{}_{}'.format(cp1.pk, self.workshop2.pk, self.workshop2a.pk)
|
||||
ws2a_price = 'cp_{}_variation_{}_{}_price'.format(cp1.pk, self.workshop2.pk, self.workshop2a.pk)
|
||||
p1_val = 'cp_{}_item_{}'.format(cp1.pk, self.parking1.pk)
|
||||
p2_val = 'cp_{}_item_{}'.format(cp1.pk, self.parking2.pk)
|
||||
|
||||
response = self.client.post('/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug), {
|
||||
ws1_val: '1',
|
||||
ws2a_val: '1',
|
||||
})
|
||||
assert response.status_code == 200
|
||||
with scopes_disabled():
|
||||
assert cp1.addons.count() == 0
|
||||
doc = BeautifulSoup(response.text, 'lxml')
|
||||
assert doc.find('input', {'name': ws1_val}).attrs.get('checked')
|
||||
assert doc.find('input', {'name': ws2a_val}).attrs.get('checked')
|
||||
assert not doc.find('input', {'name': p1_val}).attrs.get('checked')
|
||||
assert not doc.find('input', {'name': p2_val}).attrs.get('checked')
|
||||
|
||||
response = self.client.post('/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug), {
|
||||
ws1_val: '1',
|
||||
ws1_price: '222,22',
|
||||
ws2a_val: '1',
|
||||
ws2a_price: '333.33',
|
||||
})
|
||||
assert response.status_code == 200
|
||||
with scopes_disabled():
|
||||
assert cp1.addons.count() == 0
|
||||
doc = BeautifulSoup(response.text, 'lxml')
|
||||
assert doc.find('input', {'name': ws1_val}).attrs.get('checked')
|
||||
assert doc.find('input', {'name': ws1_price}).attrs.get('value') in ['222.22', '222,22']
|
||||
assert doc.find('input', {'name': ws2a_val}).attrs.get('checked')
|
||||
assert doc.find('input', {'name': ws2a_price}).attrs.get('value') in ['333.33', '333,33']
|
||||
assert not doc.find('input', {'name': p1_val}).attrs.get('checked')
|
||||
assert not doc.find('input', {'name': p2_val}).attrs.get('checked')
|
||||
|
||||
def test_confirm_subevent_presale_not_yet(self):
|
||||
with scopes_disabled():
|
||||
self.event.has_subevents = True
|
||||
|
||||
Reference in New Issue
Block a user