Compare commits

...

23 Commits

Author SHA1 Message Date
Richard Schreiber
5f6c0c11c0 use universal display_price for items and variation, respect net or gross 2024-03-12 15:41:07 +01:00
Richard Schreiber
b89c076414 add data-price to voucher and addons, unlocalize price 2024-03-12 14:58:19 +01:00
Richard Schreiber
afdc95656f fix price for items (defaul_price)
Co-authored-by: Mira <weller@rami.io>
2024-03-11 13:07:12 +01:00
Richard Schreiber
99b616ebd9 Presale: add data-attribute price to article 2024-03-08 10:49:44 +01:00
Raphael Michel
a3ce3b9af3 Select2: Fix multi-select styling for events 2024-03-08 10:09:11 +01:00
Raphael Michel
b6461e9303 Select2: Set closeOnSelect for event selection 2024-03-08 10:08:44 +01:00
Raphael Michel
f7dfd51c2c Open invoices on new page 2024-03-07 10:57:41 +01:00
dependabot[bot]
3b98d87a26 Update python-dateutil requirement from ==2.8.* to ==2.9.* (#3950)
Updates the requirements on [python-dateutil](https://github.com/dateutil/dateutil) to permit the latest version.
- [Release notes](https://github.com/dateutil/dateutil/releases)
- [Changelog](https://github.com/dateutil/dateutil/blob/master/NEWS)
- [Commits](https://github.com/dateutil/dateutil/compare/2.8.0...2.9.0)

---
updated-dependencies:
- dependency-name: python-dateutil
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-05 12:21:12 +01:00
dependabot[bot]
f045062055 Bump @babel/preset-env from 7.23.9 to 7.24.0 in /src/pretix/static/npm_dir (#3952)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.23.9 to 7.24.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.0/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-05 12:17:23 +01:00
Felix Schäfer
eb501dd1ea Correct config key in docs (#3955) 2024-03-05 12:17:17 +01:00
Dean Wyns
2d8793c355 Translations: Update Dutch
Currently translated at 88.7% (4938 of 5565 strings)

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

powered by weblate
2024-03-05 12:16:58 +01:00
Raphael Michel
d32bd717b7 Fix meta filter being selectable for export 2024-03-04 15:52:45 +01:00
Mira
6e6b75d55e Don't double file extension in clean_filename (#3942)
* Don't double file extension in clean_filename

* Don't use display_name as ClearableBasenameFileInput.FakeFile.name

Reason: it's used as the thumbnail source and therefore needs to be a valid file name and not some display name
2024-03-01 09:58:17 +01:00
Richard Schreiber
50b5f760bb Presale: prefer event’s microdata from settings over generated microdata (#3943) 2024-03-01 09:56:55 +01:00
Richard Schreiber
9ab2e61c31 Widget: fix quantity cut-off on very narrow screens (Z#23141650) (#3944)
* Widget: fix quantity cut-off on very narrow screens

* align price to left

* fix columns on mobile screens (missing clearfix)

* use min-width instead
2024-03-01 09:56:48 +01:00
Mira
4876a0b61f Allow multiple returnurl prefixes (Z#23145768) (#3941)
* Allow multiple returnurl prefixes, improve validation and docs.

* Fix typo

* Allow URL prefixes starting with http://localhost

* Add more explanation
2024-03-01 09:56:22 +01:00
Dean Wyns
56bbcb65c3 Translations: Update Dutch
Currently translated at 85.1% (4741 of 5565 strings)

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

powered by weblate
2024-03-01 09:56:00 +01:00
Dean Wyns
5bb1cb498f Translations: Update Dutch
Currently translated at 83.9% (4672 of 5565 strings)

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

powered by weblate
2024-03-01 09:56:00 +01:00
Raphael Michel
6bf23b0fdd Allow to create blocking vouchers for items with unspecified variation (#3932) 2024-02-29 16:45:41 +01:00
Raphael Michel
5deb1a8c69 Fix organizer being integer in mail_send_task 2024-02-29 08:59:15 +01:00
Raphael Michel
1523137300 Docs: Add extension of exhibitor API 2024-02-28 16:11:50 +01:00
Raphael Michel
04ef097eb1 Fix #65 -- Disallow None value for product default prices (#3847)
* Fix #65 -- Disallow None value for product default prices

* Fix #65 -- Disallow None value for product default prices

* Rebase migration
2024-02-28 16:10:53 +01:00
Raphael Michel
a5d4434a64 Bump to 2024.3.0.dev0 2024-02-28 14:40:30 +01:00
29 changed files with 842 additions and 248 deletions

View File

@@ -345,7 +345,7 @@ to speed up various operations::
The location of redis, as a URL of the form ``redis://[:password]@localhost:6379/0``
or ``unix://[:password]@/path/to/socket.sock?db=0``
``session``
``sessions``
When this is set to ``True``, redis will be used as the session storage.
``sentinels``
@@ -521,4 +521,4 @@ pretix can optionally make use of a GeoIP database for some features. It needs a
.. _GeoAcumen: https://github.com/geoacumen/geoacumen-country
.. _GeoLite2: https://dev.maxmind.com/geoip/geolite2-free-geolocation-data
.. _GeoLite2: https://dev.maxmind.com/geoip/geolite2-free-geolocation-data

View File

@@ -94,7 +94,9 @@ If you want the user to return to your application after the payment is complete
"Plugins". Enable the plugin "Redirection from order page". Then, go to the new page "Settings", then "Redirection".
Enter the base URL of your web application. This will allow you to redirect to pages under this base URL later on.
For example, if you want users to be redirected to ``https://example.org/order/return?tx_id=1234``, you could now
either enter ``https://example.org`` or ``https://example.org/order/``.
either enter ``https://example.org/order/`` or ``https://example.org/``.
Please note that in the latter case the trailing slash is required, ``https://example.org`` is not allowed to prevent.
Only base URLs with a secure (``https://``) or local (``http://localhost``) origin are permitted.
The user will be redirected back to your page instead of pretix' order confirmation page after the payment,
**regardless of whether it was successful or not**. We will append an ``error=…`` query parameter with an error

View File

@@ -34,6 +34,7 @@ internal_id string Can be used for
contact_name string Contact person (or ``null``)
contact_name_parts object of strings Decomposition of contact name (i.e. given name, family name)
contact_email string Contact person email address (or ``null``)
contact_cc_email string Copy email addresses, can be multiple separated by comma (or ``null``)
booth string Booth number (or ``null``). Maximum 100 characters.
locale string Locale for communication with the exhibitor.
access_code string Access code for the exhibitor to access their data or use the lead scanning app (read-only).
@@ -109,6 +110,7 @@ Endpoints
"title": "Dr"
},
"contact_email": "johnson@as.example.org",
"contact_cc_email": "miller@as.example.org,smith@as.example.org",
"booth": "A2",
"locale": "de",
"access_code": "VKHZ2FU84",
@@ -162,6 +164,7 @@ Endpoints
"title": "Dr"
},
"contact_email": "johnson@as.example.org",
"contact_cc_email": "miller@as.example.org,smith@as.example.org",
"booth": "A2",
"locale": "de",
"access_code": "VKHZ2FU84",
@@ -365,6 +368,7 @@ Endpoints
"title": "Dr"
},
"contact_email": "johnson@as.example.org",
"contact_cc_email": "miller@as.example.org,smith@as.example.org",
"booth": "A2",
"locale": "de",
"allow_lead_scanning": true,
@@ -394,6 +398,7 @@ Endpoints
"title": "Dr"
},
"contact_email": "johnson@as.example.org",
"contact_cc_email": "miller@as.example.org,smith@as.example.org",
"booth": "A2",
"locale": "de",
"access_code": "VKHZ2FU84",
@@ -454,6 +459,7 @@ Endpoints
"title": "Dr"
},
"contact_email": "johnson@as.example.org",
"contact_cc_email": "miller@as.example.org,smith@as.example.org",
"booth": "A2",
"locale": "de",
"access_code": "VKHZ2FU84",

View File

@@ -84,7 +84,7 @@ dependencies = [
"pycryptodome==3.20.*",
"pypdf==3.9.*",
"python-bidi==0.4.*", # Support for Arabic in reportlab
"python-dateutil==2.8.*",
"python-dateutil==2.9.*",
"pytz",
"pytz-deprecation-shim==0.1.*",
"pyuca",

View File

@@ -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__ = "2024.2.0"
__version__ = "2024.3.0.dev0"

View File

@@ -86,6 +86,7 @@ class InvoiceExporterMixin:
('', _('All payment providers')),
] + [
(k, v.verbose_name) for k, v in self.event.get_payment_providers().items()
if not v.is_meta
],
required=False,
help_text=_('Only include invoices for orders that have at least one payment attempt '

View File

@@ -0,0 +1,19 @@
# Generated by Django 4.2.9 on 2024-01-30 11:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pretixbase", "0256_itemvariation_unavail_modes"),
]
operations = [
migrations.AlterField(
model_name="item",
name="default_price",
field=models.DecimalField(decimal_places=2, default=0, max_digits=13),
preserve_default=False,
),
]

View File

@@ -265,6 +265,9 @@ class EventMixin:
@property
def event_microdata(self):
if self.settings.event_microdata:
return self.settings.event_microdata
import json
eventdict = {

View File

@@ -430,7 +430,7 @@ class Item(LoggedModel):
help_text=_("If this product has multiple variations, you can set different prices for each of the "
"variations. If a variation does not have a special price or if you do not have variations, "
"this price will be used."),
max_digits=13, decimal_places=2, null=True
max_digits=13, decimal_places=2,
)
free_price = models.BooleanField(
default=False,

View File

@@ -351,9 +351,6 @@ class Voucher(LoggedModel):
'variations.'))
if variation and not item.variations.filter(pk=variation.pk).exists():
raise ValidationError(_('This variation does not belong to this product.'))
if item.has_variations and not variation and data.get('block_quota'):
raise ValidationError(_('You can only block quota if you specify a specific product variation. '
'Otherwise it might be unclear which quotas to block.'))
if item.category and item.category.is_addon:
raise ValidationError(_('It is currently not possible to create vouchers for add-on products.'))
elif block_quota:
@@ -431,7 +428,15 @@ class Voucher(LoggedModel):
elif old_instance.variation:
quotas |= set(old_instance.variation.quotas.filter(subevent=old_instance.subevent))
elif old_instance.item:
quotas |= set(old_instance.item.quotas.filter(subevent=old_instance.subevent))
if old_instance.item.has_variations:
quotas |= set(
Quota.objects.filter(pk__in=Quota.variations.through.objects.filter(
itemvariation__item=old_instance.item,
quota__subevent=old_instance.subevent,
).values('quota_id'))
)
else:
quotas |= set(old_instance.item.quotas.filter(subevent=old_instance.subevent))
return quotas
@staticmethod
@@ -446,13 +451,19 @@ class Voucher(LoggedModel):
if quota:
new_quotas = {quota}
elif item and item.has_variations and not variation:
raise ValidationError(_('You can only block quota if you specify a specific product variation. '
'Otherwise it might be unclear which quotas to block.'))
elif item and variation:
new_quotas = set(variation.quotas.filter(subevent=data.get('subevent')))
elif item and not item.has_variations:
new_quotas = set(item.quotas.filter(subevent=data.get('subevent')))
elif item and item.has_variations:
new_quotas = set(
Quota.objects.filter(
pk__in=Quota.variations.through.objects.filter(
itemvariation__item=old_instance.item,
quota__subevent=data.get('subevent'),
).values('quota_id')
)
)
else:
raise ValidationError(_('You need to select a specific product or quota if this voucher should reserve '
'tickets.'))

View File

@@ -383,6 +383,7 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
if event:
with scopes_disabled():
event = Event.objects.get(id=event)
organizer = event.organizer
backend = event.get_mail_backend()
cm = lambda: scope(organizer=event.organizer) # noqa
elif organizer:

View File

@@ -90,8 +90,8 @@ class QuotaAvailability:
self._count_waitinglist = count_waitinglist
self._ignore_closed = ignore_closed
self._full_results = full_results
self._item_to_quotas = defaultdict(list)
self._var_to_quotas = defaultdict(list)
self._item_to_quotas = defaultdict(set)
self._var_to_quotas = defaultdict(set)
self._early_out = early_out
self._quota_objects = {}
self.results = {}
@@ -243,13 +243,16 @@ class QuotaAvailability:
quota_id__in=[q.pk for q in quotas]
).values('quota_id', 'item_id')
for m in q_items:
self._item_to_quotas[m['item_id']].append(self._quota_objects[m['quota_id']])
self._item_to_quotas[m['item_id']].add(self._quota_objects[m['quota_id']])
q_vars = Quota.variations.through.objects.filter(
quota_id__in=[q.pk for q in quotas]
).values('quota_id', 'itemvariation_id')
).values('quota_id', 'itemvariation_id', 'itemvariation__item_id')
for m in q_vars:
self._var_to_quotas[m['itemvariation_id']].append(self._quota_objects[m['quota_id']])
self._var_to_quotas[m['itemvariation_id']].add(self._quota_objects[m['quota_id']])
# We can't be 100% certain that a quota, when it is connected to a variation, is also always connected to
# the parent item, so we double-check here just to be sure.
self._item_to_quotas[m['itemvariation__item_id']].add(self._quota_objects[m['quota_id']])
self._compute_orders(quotas, q_items, q_vars, size_left)
@@ -378,7 +381,10 @@ class QuotaAvailability:
Q(
Q(
Q(variation_id__isnull=True) &
Q(item_id__in={i['item_id'] for i in q_items if i['quota_id'] in quota_ids})
Q(item_id__in=(
{i['item_id'] for i in q_items if i['quota_id'] in quota_ids} |
{i['itemvariation__item_id'] for i in q_vars if i['quota_id'] in quota_ids}
))
) | Q(
variation_id__in={i['itemvariation_id'] for i in q_vars if i['quota_id'] in quota_ids}
) | Q(

View File

@@ -123,8 +123,6 @@ class ClearableBasenameFileInput(forms.ClearableFileInput):
@property
def name(self):
if hasattr(self.file, 'display_name'):
return self.file.display_name
return self.file.name
@property

View File

@@ -32,9 +32,14 @@ def clean_filename(fname):
"Terms.pdf""Terms.pdf.OybgvyAH.22c0583727d5bc.pdf"
This function reverses this operation:
This function reverses this operation (leaving names without doubled extension as-is):
"Terms.pdf.OybgvyAH.22c0583727d5bc.pdf""Terms.pdf"
"Terms.pdf""Terms.pdf"
"""
ext = '.' + fname.split('.')[-1]
return fname.rsplit(ext + ".", 1)[0] + ext
parts = fname.rsplit(ext + ".", 1)
if len(parts) == 1:
return parts[0]
else:
return parts[0] + ext

File diff suppressed because it is too large Load Diff

View File

@@ -71,11 +71,15 @@ def returnurl_process_request(sender, request, **kwargs):
u = request.GET.get('return_url')
if not sender.settings.returnurl_prefix:
raise PermissionDenied('No return URL prefix set.')
elif not u.startswith(sender.settings.returnurl_prefix):
elif not check_against_prefix_list(u, sender.settings.returnurl_prefix):
raise PermissionDenied('Invalid return URL.')
request.session[key] = u
def check_against_prefix_list(u, allowlist):
return any(u.startswith(allow.strip()) for allow in allowlist.split("\n") if allow.strip() != "")
@receiver(nav_event_settings, dispatch_uid='returnurl_nav')
def navbar_info(sender, request, **kwargs):
url = resolve(request.path_info)

View File

@@ -19,6 +19,8 @@
# 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/>.
#
import re
from django import forms
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
@@ -31,10 +33,14 @@ from pretix.control.views.event import (
class ReturnSettingsForm(SettingsForm):
returnurl_prefix = forms.URLField(
label=_("Base redirection URL"),
help_text=_("Redirection will only be allowed to URLs that start with this prefix."),
returnurl_prefix = forms.RegexField(
label=_("Base redirection URLs"),
help_text=_("Redirection will only be allowed to URLs that start with one of these prefixes. "
"Enter one or more allowed URL prefix per line. "
"URL prefixes must include a slash after the hostname."),
required=False,
widget=forms.Textarea,
regex=re.compile(r'^((https://.*/.*|http://localhost[:/].*)\n*)*$')
)

View File

@@ -106,7 +106,10 @@
</div>
<div class="variations {% if not event.settings.show_variations_expanded %}variations-collapsed{% endif %}">
{% 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">
<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 %}
data-price="{% if event.settings.display_net_prices %}{{ var.display_price.net|unlocalize }}{% else %}{{ var.display_price.gross|unlocalize }}{% endif %}"
{% endif %}>
<div class="col-md-8 col-sm-6 col-xs-12">
<h5 id="cp-{{ form.pos.pk }}-item-{{ item.pk }}-{{ var.pk }}-legend">{{ var }}</h5>
{% if var.description %}
@@ -223,7 +226,10 @@
</div>
</article>
{% else %}
<article aria-labelledby="cp-{{ form.pos.pk }}-item-{{ item.pk }}-legend"{% if item.description %} aria-describedby="cp-{{ form.pos.pk }}-item-{{ item.pk }}-description"{% endif %} class="row-fluid product-row simple">
<article aria-labelledby="cp-{{ form.pos.pk }}-item-{{ item.pk }}-legend"{% if item.description %} aria-describedby="cp-{{ form.pos.pk }}-item-{{ item.pk }}-description"{% endif %} class="row-fluid product-row simple"
{% if not item.free_price %}
data-price="{% if event.settings.display_net_prices %}{{ item.display_price.net|unlocalize }}{% else %}{{ item.display_price.gross|unlocalize }}{% endif %}"
{% endif %}>
<div class="col-md-8 col-sm-6 col-xs-12">
{% if item.picture %}
<a href="{{ item.picture.url }}" class="productpicture"

View File

@@ -98,7 +98,10 @@
</div>
<div class="variations {% if not event.settings.show_variations_expanded %}variations-collapsed{% endif %}">
{% for var in item.available_variations %}
<article aria-labelledby="item-{{ item.pk }}-{{ var.pk }}-legend"{% if var.description %} aria-describedby="item-{{ item.pk }}-{{ var.pk }}-description"{% endif %} class="row product-row variation" id="item-{{ item.pk }}-{{ var.pk }}">
<article aria-labelledby="item-{{ item.pk }}-{{ var.pk }}-legend"{% if var.description %} aria-describedby="item-{{ item.pk }}-{{ var.pk }}-description"{% endif %} class="row product-row variation" id="item-{{ item.pk }}-{{ var.pk }}"
{% if not item.free_price %}
data-price="{% if event.settings.display_net_prices %}{{ var.display_price.net|unlocalize }}{% else %}{{ var.display_price.gross|unlocalize }}{% endif %}"
{% endif %}>
<div class="col-md-8 col-sm-6 col-xs-12">
<h5 id="item-{{ item.pk }}-{{ var.pk }}-legend">{{ var }}</h5>
{% if var.description %}
@@ -228,7 +231,10 @@
</div>
</article>
{% else %}
<article aria-labelledby="item-{{ item.pk }}-legend"{% if item.description %} aria-describedby="item-{{ item.pk }}-description"{% endif %} class="row product-row simple" id="item-{{ item.pk }}">
<article aria-labelledby="item-{{ item.pk }}-legend"{% if item.description %} aria-describedby="item-{{ item.pk }}-description"{% endif %} class="row product-row simple" id="item-{{ item.pk }}"
{% if not item.free_price %}
data-price="{% if event.settings.display_net_prices %}{{ item.display_price.net|unlocalize }}{% else %}{{ item.display_price.gross|unlocalize }}{% endif %}"
{% endif %}>
<div class="col-md-8 col-sm-6 col-xs-12">
{% if item.picture %}
<a href="{{ item.picture.url }}" class="productpicture"

View File

@@ -239,7 +239,7 @@
<ul>
{% for i in invoices %}
<li>
<a href="{% eventurl event "presale:event.invoice.download" invoice=i.pk secret=order.secret order=order.code %}">
<a href="{% eventurl event "presale:event.invoice.download" invoice=i.pk secret=order.secret order=order.code %}" target="_blank">
{% if i.is_cancellation %}{% trans "Cancellation" context "invoice" %}{% else %}{% trans "Invoice" %}{% endif %}
{{ i.number }}</a> ({{ i.date|date:"SHORT_DATE_FORMAT" }})
</li>

View File

@@ -127,7 +127,10 @@
</div>
<div class="variations">
{% for var in item.available_variations %}
<article aria-labelledby="item-{{ item.pk }}-{{ var.pk }}-legend"{% if var.description %} aria-describedby="item-{{ item.pk }}-{{ var.pk }}-description"{% endif %} class="row product-row variation" id="item-{{ item.pk }}-{{ var.pk }}">
<article aria-labelledby="item-{{ item.pk }}-{{ var.pk }}-legend"{% if var.description %} aria-describedby="item-{{ item.pk }}-{{ var.pk }}-description"{% endif %} class="row product-row variation" id="item-{{ item.pk }}-{{ var.pk }}"
{% if not item.free_price %}
data-price="{% if event.settings.display_net_prices %}{{ var.display_price.net|unlocalize }}{% else %}{{ var.display_price.gross|unlocalize }}{% endif %}"
{% endif %}>
<div class="col-md-8 col-sm-6 col-xs-12">
<h5 id="item-{{ item.pk }}-{{ var.pk }}-legend">{{ var }}</h5>
{% if var.description %}
@@ -259,7 +262,10 @@
</div>
</article>
{% else %}
<article aria-labelledby="item-{{ item.pk }}-legend"{% if item.description %} aria-describedby="item-{{ item.pk }}-description"{% endif %} class="row product-row simple" id="item-{{ item.pk }}">
<article aria-labelledby="item-{{ item.pk }}-legend"{% if item.description %} aria-describedby="item-{{ item.pk }}-description"{% endif %} class="row product-row simple" id="item-{{ item.pk }}"
{% if not item.free_price %}
data-price="{% if event.settings.display_net_prices %}{{ item.display_price.net|unlocalize }}{% else %}{{ item.display_price.gross|unlocalize }}{% endif %}"
{% endif %}>
<div class="col-md-8 col-sm-6 col-xs-12">
{% if item.picture %}
<a href="{{ item.picture.url }}" class="productpicture"

View File

@@ -9,7 +9,7 @@
"version": "0.0.0",
"dependencies": {
"@babel/core": "^7.23.9",
"@babel/preset-env": "^7.23.9",
"@babel/preset-env": "^7.24.0",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-node-resolve": "^15.2.3",
"rollup": "^2.79.1",
@@ -335,9 +335,9 @@
}
},
"node_modules/@babel/helper-plugin-utils": {
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz",
"integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==",
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz",
"integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==",
"engines": {
"node": ">=6.9.0"
}
@@ -1196,13 +1196,13 @@
}
},
"node_modules/@babel/plugin-transform-object-rest-spread": {
"version": "7.23.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz",
"integrity": "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==",
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.0.tgz",
"integrity": "sha512-y/yKMm7buHpFFXfxVFS4Vk1ToRJDilIa6fKRioB9Vjichv58TDGXTvqV0dN7plobAmTW5eSEGXDngE+Mm+uO+w==",
"dependencies": {
"@babel/compat-data": "^7.23.3",
"@babel/helper-compilation-targets": "^7.22.15",
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/compat-data": "^7.23.5",
"@babel/helper-compilation-targets": "^7.23.6",
"@babel/helper-plugin-utils": "^7.24.0",
"@babel/plugin-syntax-object-rest-spread": "^7.8.3",
"@babel/plugin-transform-parameters": "^7.23.3"
},
@@ -1479,13 +1479,13 @@
}
},
"node_modules/@babel/preset-env": {
"version": "7.23.9",
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.9.tgz",
"integrity": "sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A==",
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.0.tgz",
"integrity": "sha512-ZxPEzV9IgvGn73iK0E6VB9/95Nd7aMFpbE0l8KQFDG70cOV9IxRP7Y2FUPmlK0v6ImlLqYX50iuZ3ZTVhOF2lA==",
"dependencies": {
"@babel/compat-data": "^7.23.5",
"@babel/helper-compilation-targets": "^7.23.6",
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/helper-plugin-utils": "^7.24.0",
"@babel/helper-validator-option": "^7.23.5",
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3",
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3",
@@ -1538,7 +1538,7 @@
"@babel/plugin-transform-new-target": "^7.23.3",
"@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4",
"@babel/plugin-transform-numeric-separator": "^7.23.4",
"@babel/plugin-transform-object-rest-spread": "^7.23.4",
"@babel/plugin-transform-object-rest-spread": "^7.24.0",
"@babel/plugin-transform-object-super": "^7.23.3",
"@babel/plugin-transform-optional-catch-binding": "^7.23.4",
"@babel/plugin-transform-optional-chaining": "^7.23.4",
@@ -4378,9 +4378,9 @@
}
},
"@babel/helper-plugin-utils": {
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz",
"integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg=="
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz",
"integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w=="
},
"@babel/helper-remap-async-to-generator": {
"version": "7.22.20",
@@ -4922,13 +4922,13 @@
}
},
"@babel/plugin-transform-object-rest-spread": {
"version": "7.23.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz",
"integrity": "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==",
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.0.tgz",
"integrity": "sha512-y/yKMm7buHpFFXfxVFS4Vk1ToRJDilIa6fKRioB9Vjichv58TDGXTvqV0dN7plobAmTW5eSEGXDngE+Mm+uO+w==",
"requires": {
"@babel/compat-data": "^7.23.3",
"@babel/helper-compilation-targets": "^7.22.15",
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/compat-data": "^7.23.5",
"@babel/helper-compilation-targets": "^7.23.6",
"@babel/helper-plugin-utils": "^7.24.0",
"@babel/plugin-syntax-object-rest-spread": "^7.8.3",
"@babel/plugin-transform-parameters": "^7.23.3"
}
@@ -5091,13 +5091,13 @@
}
},
"@babel/preset-env": {
"version": "7.23.9",
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.9.tgz",
"integrity": "sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A==",
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.0.tgz",
"integrity": "sha512-ZxPEzV9IgvGn73iK0E6VB9/95Nd7aMFpbE0l8KQFDG70cOV9IxRP7Y2FUPmlK0v6ImlLqYX50iuZ3ZTVhOF2lA==",
"requires": {
"@babel/compat-data": "^7.23.5",
"@babel/helper-compilation-targets": "^7.23.6",
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/helper-plugin-utils": "^7.24.0",
"@babel/helper-validator-option": "^7.23.5",
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3",
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3",
@@ -5150,7 +5150,7 @@
"@babel/plugin-transform-new-target": "^7.23.3",
"@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4",
"@babel/plugin-transform-numeric-separator": "^7.23.4",
"@babel/plugin-transform-object-rest-spread": "^7.23.4",
"@babel/plugin-transform-object-rest-spread": "^7.24.0",
"@babel/plugin-transform-object-super": "^7.23.3",
"@babel/plugin-transform-optional-catch-binding": "^7.23.4",
"@babel/plugin-transform-optional-chaining": "^7.23.4",

View File

@@ -5,7 +5,7 @@
"scripts": {},
"dependencies": {
"@babel/core": "^7.23.9",
"@babel/preset-env": "^7.23.9",
"@babel/preset-env": "^7.24.0",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-node-resolve": "^15.2.3",
"vue": "^2.7.16",

View File

@@ -625,6 +625,7 @@ var form_handlers = function (el) {
el.find('[data-model-select2=event]').each(function () {
var $s = $(this);
$s.select2({
closeOnSelect: !this.hasAttribute('multiple'),
theme: "bootstrap",
delay: 100,
allowClear: !$s.prop("required"),

View File

@@ -904,8 +904,8 @@ details {
}
}
.select2-container [aria-multiselectable] .select2-results__option span strike::before,
.select2-container [aria-multiselectable] .select2-results__option span span::before {
.select2-container [aria-multiselectable] .select2-results__option > span > strike:first-child::before,
.select2-container [aria-multiselectable] .select2-results__option > span > span:first-child::before {
content: "";
font-family: FontAwesome;
display: inline-block;
@@ -913,8 +913,8 @@ details {
width: 1.28571em;
text-align: center;
}
.select2-container [aria-multiselectable] .select2-results__option[aria-selected=true] span strike::before,
.select2-container [aria-multiselectable] .select2-results__option[aria-selected=true] span span::before {
.select2-container [aria-multiselectable] .select2-results__option[aria-selected=true] > span > strike:first-child::before,
.select2-container [aria-multiselectable] .select2-results__option[aria-selected=true] > span > span:first-child::before {
content: ""
}

View File

@@ -967,8 +967,14 @@
float: none;
margin-bottom: 5px;
}
.pretix-widget-item-info-col:after {
display: block;
content: "";
clear: both;
}
.pretix-widget-item-price-col, .pretix-widget-item-availability-col {
width: 50%;
min-width: 140px;
}
.pretix-widget-action {
width: 100%;

View File

@@ -934,7 +934,7 @@ def test_cartpos_create_with_voucher_unknown(token_client, organizer, event, ite
@pytest.mark.django_db
def test_cartpos_create_with_voucher_invalid_item(token_client, organizer, event, item, quota):
with scopes_disabled():
item2 = event.items.create(name="item2")
item2 = event.items.create(name="item2", default_price=0)
voucher = event.vouchers.create(code="FOOBAR", item=item2)
res = copy.deepcopy(CARTPOS_CREATE_PAYLOAD)
res['item'] = item.pk

View File

@@ -283,6 +283,29 @@ class QuotaTestCase(BaseQuotaTestCase):
v.save()
self.assertEqual(self.var1.check_quotas(), (Quota.AVAILABILITY_ORDERED, 0))
@classscope(attr='o')
def test_voucher_all_variations(self):
self.quota.variations.add(self.var1)
self.quota.size = 1
self.quota.save()
self.quota2 = Quota.objects.create(name="Test", size=2, event=self.event)
self.quota2.variations.add(self.var2)
self.quota3 = Quota.objects.create(name="Test", size=2, event=self.event)
self.quota3.variations.add(self.var3)
v = Voucher.objects.create(item=self.item2, event=self.event)
self.assertEqual(self.var1.check_quotas(), (Quota.AVAILABILITY_OK, 1))
self.assertEqual(self.var2.check_quotas(), (Quota.AVAILABILITY_OK, 2))
self.assertEqual(self.var3.check_quotas(), (Quota.AVAILABILITY_OK, 2))
v.block_quota = True
v.save()
self.assertEqual(self.var1.check_quotas(), (Quota.AVAILABILITY_ORDERED, 0))
self.assertEqual(self.var2.check_quotas(), (Quota.AVAILABILITY_OK, 1))
self.assertEqual(self.var3.check_quotas(), (Quota.AVAILABILITY_OK, 2))
@classscope(attr='o')
def test_voucher_quota(self):
self.quota.variations.add(self.var1)
@@ -979,9 +1002,8 @@ class VoucherTestCase(BaseQuotaTestCase):
@classscope(attr='o')
def test_voucher_specify_variation_for_block_quota(self):
with self.assertRaises(ValidationError):
v = Voucher(item=self.item2, block_quota=True, event=self.event)
v.clean()
v = Voucher(item=self.item2, block_quota=True, event=self.event)
v.clean()
@classscope(attr='o')
def test_voucher_no_item_but_variation(self):

View File

@@ -254,7 +254,7 @@ class VoucherFormTest(SoupTestMixin, TransactionTestCase):
self._create_voucher({
'itemvar': '%d' % self.shirt.pk,
'block_quota': 'on'
}, expected_failure=True)
})
def test_create_blocking_item_voucher_quota_full_invalid(self):
self.quota_shirts.size = 0