Compare commits

...
Author SHA1 Message Date
Richard Schreiber 59ac56148e PDF: fix position of rotated overflown one-line textfields 2023-10-17 09:07:50 +02:00
Raphael Michel 4d75c568b5 Support file upload in AsyncFormView and subevent editor 2023-10-16 17:59:32 +02:00
Raphael Michel abda800953 Do not treat event as free if there are mandatory non-free addons (#3646) 2023-10-16 12:35:20 +02:00
Raphael Michel 43d26451d1 Add tests for locking in API (#3617) 2023-10-16 12:34:21 +02:00
Raphael Michel 2dd51b6f62 Order import: Allow to manually specify character set 2023-10-16 12:22:05 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 9d7d5389dc Bump @babel/preset-env from 7.22.15 to 7.23.2 in /src/pretix/static/npm_dir (#3647)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-16 11:16:33 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> fefa15fedd Bump @babel/core from 7.22.11 to 7.23.0 in /src/pretix/static/npm_dir (#3627)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-16 10:58:32 +02:00
Sanny 2c905b0b39 Translations: Update Italian
Currently translated at 21.4% (1167 of 5443 strings)

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

powered by weblate
2023-10-16 10:58:21 +02:00
Sanny 1c4d64a3b5 Translations: Update Italian
Currently translated at 21.3% (1161 of 5443 strings)

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

powered by weblate
2023-10-16 10:58:21 +02:00
Raphael Michel b920795edf Translations: Update German
Currently translated at 100.0% (5443 of 5443 strings)

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

powered by weblate
2023-10-16 10:58:21 +02:00
Dennis Lichtenthäler 270d91080f Translations: Update German
Currently translated at 100.0% (5443 of 5443 strings)

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

powered by weblate
2023-10-16 10:58:21 +02:00
Richard Schreiber 3a26d6173e Widget: add single-item-select shopping cart (Z#23131246) (#3633) 2023-10-16 10:56:00 +02:00
Richard Schreiber 7ee630928b UI: improve distinction between placeholder and input (#3640) 2023-10-16 10:41:37 +02:00
Raphael Michel 215a2f2dbb API: Fix bulk voucher creation with server-generated codes 2023-10-15 16:16:32 +02:00
Raphael Michel f1a29a7c5b Customer account: Catch IntegrityError during email change 2023-10-11 11:39:45 +02:00
Raphael Michel e72f6746f2 Badges: Add Avery 63.5x96.6 template 2023-10-11 11:28:50 +02:00
lujoga 8343b1256b Docker: Change default number of workers to 2 * $(nproc) (#3634) 2023-10-09 18:05:21 +02:00
Sanny d4c541b2bc Translations: Update Italian
Currently translated at 19.5% (1066 of 5443 strings)

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

powered by weblate
2023-10-09 18:04:03 +02:00
Ronan LE MEILLAT 43eff3b65f Translations: Update French
Currently translated at 100.0% (5443 of 5443 strings)

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

powered by weblate
2023-10-09 18:04:03 +02:00
Ronan LE MEILLAT 1c1beee5f6 Translations: Update French
Currently translated at 100.0% (5443 of 5443 strings)

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

powered by weblate
2023-10-09 18:04:03 +02:00
Takanori Suzuki dc3fc3cb45 Translations: Update Japanese
Currently translated at 3.1% (169 of 5443 strings)

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

powered by weblate
2023-10-09 18:04:03 +02:00
Raphael Michel f62a200f52 Translations: Update French
Currently translated at 99.9% (5439 of 5443 strings)

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

powered by weblate
2023-10-09 18:04:03 +02:00
Mossroy 49d048fc49 Translations: Update French
Currently translated at 99.9% (5439 of 5443 strings)

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

powered by weblate
2023-10-09 18:04:03 +02:00
Mossroy fde1c67388 Translations: Update French
Currently translated at 99.8% (5437 of 5443 strings)

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

powered by weblate
2023-10-09 18:04:03 +02:00
Raphael Michel c8e818f786 Translations: Update German (informal) (de_Informal)
Currently translated at 100.0% (5443 of 5443 strings)

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

powered by weblate
2023-10-09 18:04:03 +02:00
Raphael Michel 5221ce965b Translations: Update German
Currently translated at 100.0% (5443 of 5443 strings)

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

powered by weblate
2023-10-09 18:04:03 +02:00
Igor Támara 196eb3482c Translations: Update Spanish
Currently translated at 60.2% (3282 of 5448 strings)

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

powered by weblate
2023-10-09 18:04:03 +02:00
Igor Támara 1fe7fa91d5 Translations: Update Spanish
Currently translated at 59.4% (3238 of 5448 strings)

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

powered by weblate
2023-10-09 18:04:03 +02:00
Raphael Michel d8ecb43e5d OrderChangeManager: Prevent race conditions (Z#23131769) (#3623) 2023-10-09 16:00:58 +02:00
Richard Schreiber 0cbb38f3d4 Widget: Fix waitinglist-URL for subevents (Z#23132845) (#3635) 2023-10-09 12:17:28 +02:00
Raphael Michel ab0e26047e Docs: Add send_access_code to exhibitors API 2023-10-09 11:58:16 +02:00
Martin Gross 079324863b NoQA extra long default ApplePay Token line 2023-10-05 16:14:40 +02:00
Martin Gross ae445c1460 Move apple-developer-merchantid-domain-association into setting (#3611) 2023-10-05 16:07:11 +02:00
Raphael Michel 6593a64b18 Badges: Add Avery 80x50 template 2023-10-05 15:44:47 +02:00
Raphael MichelandRichard Schreiber b8c448fbd7 Docs: Add new field to exhibitors API (#3624)
Co-authored-by: Richard Schreiber <schreiber@rami.io>
2023-10-04 16:29:38 +02:00
Raphael Michel c1c828d8a2 Order detail page: Don't offer invoice generation for expired orders 2023-10-04 12:42:48 +02:00
Raphael Michel 1d0dbae77a Question detail view: Show total number of answers 2023-10-04 11:44:53 +02:00
Richard Schreiber c6cf8b6f24 Calender: Always add end date (Z#23131739) (#3622) 2023-09-28 16:05:03 +02:00
Raphael Michel 78fbfc9c80 Migrate from AutoField to BigAutoField (#3493) 2023-09-27 08:59:10 +02:00
Raphael Michel 7ead117f2e Docs: Fix errors in exhibitor API documentation 2023-09-26 14:27:21 +02:00
Raphael Michel a4417e97fd Check-in: Handle products without variation in simulator 2023-09-26 13:54:29 +02:00
Raphael Michel 10e9b9e12d Check-in: Handle non-existant IDs 2023-09-26 13:52:10 +02:00
Raphael Michel 755c4efba1 Bump to 2023.9.0.dev0 2023-09-26 11:54:04 +02:00
58 changed files with 2074 additions and 785 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ export DATA_DIR=/data/
export HOME=/pretix
AUTOMIGRATE=${AUTOMIGRATE:-yes}
NUM_WORKERS_DEFAULT=$((2 * $(nproc --all)))
NUM_WORKERS_DEFAULT=$((2 * $(nproc)))
export NUM_WORKERS=${NUM_WORKERS:-$NUM_WORKERS_DEFAULT}
if [ ! -d /data/logs ]; then
+39 -7
View File
@@ -35,7 +35,7 @@ contact_name string Contact person
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``)
booth string Booth number (or ``null``). Maximum 100 characters.
locale string Locale for communication with the exhibitor (or ``null``).
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).
allow_lead_scanning boolean Enables lead scanning app
allow_lead_access boolean Enables access to data gathered by the lead scanning app
@@ -230,7 +230,8 @@ Endpoints
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/exhibitors/(id)/vouchers/
Returns a list of all vouchers connected to an exhibitor. The response contains the same data as described in
:ref:`rest-vouchers`.
:ref:`rest-vouchers` as well as for each voucher an additional field ``exhibitor_comment`` that is shown to the exhibitor. It can only
be modified using the ``attach`` API call below.
**Example request**:
@@ -285,7 +286,7 @@ Endpoints
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/exhibitors/(id)/vouchers/attach/
Attaches an **existing** voucher to an exhibitor. You need to send either the ``id`` **or** the ``code`` field of
the voucher.
the voucher. You can call this method multiple times to update the optional ``exhibitor_comment`` field.
**Example request**:
@@ -296,7 +297,8 @@ Endpoints
Accept: application/json, text/javascript
{
"id": 15
"id": 15,
"exhibitor_comment": "Free ticket"
}
**Example request**:
@@ -308,7 +310,8 @@ Endpoints
Accept: application/json, text/javascript
{
"code": "43K6LKM37FBVR2YG"
"code": "43K6LKM37FBVR2YG",
"exhibitor_comment": "Free ticket"
}
**Example response**:
@@ -356,7 +359,6 @@ Endpoints
"contact_email": "johnson@as.example.org",
"booth": "A2",
"locale": "de",
"access_code": "VKHZ2FU8",
"allow_lead_scanning": true,
"allow_lead_access": true,
"allow_voucher_access": true,
@@ -411,7 +413,7 @@ Endpoints
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/digitalcontents/1/ HTTP/1.1
PATCH /api/v1/organizers/bigevents/events/sampleconf/exhibitors/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
@@ -459,6 +461,36 @@ Endpoints
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event/exhibitor does not exist **or** you have no permission to change it.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/exhibitors/(id)/send_access_code/
Sends an email to the exhibitor with their access code.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/exhibitors/1/send_access_code/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param code: The ``id`` field of the exhibitor to send an email for
:statuscode 200: no error
:statuscode 400: The exhibitor does not have an email address associated
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
:statuscode 404: The requested exhibitor does not exist.
:statuscode 503: The email could not be sent.
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/exhibitors/(id)/
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

+18
View File
@@ -124,6 +124,24 @@ If you want to disable voucher input in the widget, you can pass the ``disable-v
<pretix-widget event="https://pretix.eu/demo/democon/" disable-vouchers></pretix-widget>
Enabling the button-style single item select
--------------------------------------------
By default, the widget uses a checkbox to select items, that can only be bought in quantities of one. If you want to match
the button-style of that checkbox with the one in the pretix shop, you can use the ``single-item-select`` attribute::
<pretix-widget event="https://pretix.eu/demo/democon/" single-item-select="button"></pretix-widget>
.. image:: img/widget_checkbox_button.png
:align: center
:class: screenshot
.. note::
Due to compatibilty with existing widget installations, the default value for ``single-item-select``
is ``checkbox``. This might change in the future, so make sure, to set the attribute to
``single-item-select="checkbox"`` if you need it.
Filtering products
------------------
+1 -1
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__ = "2023.8.0"
__version__ = "2023.9.0.dev0"
+91
View File
@@ -0,0 +1,91 @@
# Generated by Django 4.2.4 on 2023-09-26 12:01
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("pretixapi", "0010_webhook_comment"),
]
operations = [
migrations.AlterField(
model_name="apicall",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="oauthaccesstoken",
name="user",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)s",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="oauthapplication",
name="user",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)s",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="oauthgrant",
name="user",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)s",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="oauthidtoken",
name="user",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)s",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="oauthrefreshtoken",
name="user",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)s",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="webhook",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="webhookcall",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="webhookeventlistener",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
]
+6
View File
@@ -24,10 +24,16 @@ from rest_framework.exceptions import ValidationError
from pretix.api.serializers.i18n import I18nAwareModelSerializer
from pretix.base.models import Seat, Voucher
from pretix.base.models.vouchers import generate_codes
class VoucherListSerializer(serializers.ListSerializer):
def create(self, validated_data):
vouchers_without_codes = [v for v in validated_data if not v.get('code')]
for voucher_data, code in zip(vouchers_without_codes, generate_codes(self.context['event'].organizer, num=len(vouchers_without_codes), prefix=None)):
voucher_data['code'] = code
codes = set()
seats = set()
errs = []
+509
View File
@@ -0,0 +1,509 @@
# Generated by Django 4.2.4 on 2023-09-26 12:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pretixbase", "0245_discount_benefit_products"),
]
operations = [
migrations.RenameIndex(
model_name="logentry",
new_name="pretixbase__datetim_b1fe5a_idx",
old_fields=("datetime", "id"),
),
migrations.RenameIndex(
model_name="order",
new_name="pretixbase__datetim_66aff0_idx",
old_fields=("datetime", "id"),
),
migrations.RenameIndex(
model_name="order",
new_name="pretixbase__last_mo_4ebf8b_idx",
old_fields=("last_modified", "id"),
),
migrations.RenameIndex(
model_name="reusablemedium",
new_name="pretixbase__updated_093277_idx",
old_fields=("updated", "id"),
),
migrations.RenameIndex(
model_name="transaction",
new_name="pretixbase__datetim_b20405_idx",
old_fields=("datetime", "id"),
),
migrations.AlterField(
model_name="attendeeprofile",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="blockedticketsecret",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="cachedcombinedticket",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="cachedticket",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="cancellationrequest",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="cartposition",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="checkin",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="checkinlist",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="customer",
name="locale",
field=models.CharField(default="de", max_length=50),
),
migrations.AlterField(
model_name="device",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="discount",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="event",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="event_settingsstore",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="eventfooterlink",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="eventmetaproperty",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="eventmetavalue",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="exchangerate",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="gate",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="giftcard",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="giftcardacceptance",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="giftcardtransaction",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="globalsettingsobject_settingsstore",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="invoice",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="invoiceaddress",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="invoiceline",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="item",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="itemaddon",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="itembundle",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="itemcategory",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="itemmetaproperty",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="itemmetavalue",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="itemvariation",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="itemvariationmetavalue",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="logentry",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="mediumkeyset",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="notificationsetting",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="order",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="orderfee",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="orderpayment",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="orderposition",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="orderrefund",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="organizer",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="organizer_settingsstore",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="organizerfooterlink",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="question",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="questionanswer",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="questionoption",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="quota",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="revokedticketsecret",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="seat",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="seatcategorymapping",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="seatingplan",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="staffsession",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="staffsessionauditlog",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="subevent",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="subeventitem",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="subeventitemvariation",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="subeventmetavalue",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="taxrule",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="team",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="teamapitoken",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="teaminvite",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="u2fdevice",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="user",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="user",
name="locale",
field=models.CharField(default="de", max_length=50),
),
migrations.AlterField(
model_name="voucher",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="waitinglistentry",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="webauthndevice",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
]
+3 -2
View File
@@ -992,6 +992,7 @@ class Renderer:
canvas.rotate(o.get('rotation', 0) * -1)
p.drawOn(canvas, 0, -h - ad[1] / 2.5)
else:
height_of_one_line = lineheight * float(o['fontsize']) * 1.05
if lineheight != 1.0:
# lineheight adds to ascent/descent offsets, just empirical values again to get
# reportlab to render similarly to browser canvas
@@ -999,9 +1000,9 @@ class Renderer:
ad[0],
ad[1] + (lineheight - 1.0) * float(o['fontsize']) * 1.05
)
canvas.translate(float(o['left']) * mm, float(o['bottom']) * mm + h)
canvas.translate(float(o['left']) * mm, float(o['bottom']) * mm + height_of_one_line)
canvas.rotate(o.get('rotation', 0) * -1)
p.drawOn(canvas, 0, -h - ad[1])
p.drawOn(canvas, 0, -height_of_one_line - ad[1])
canvas.restoreState()
def draw_page(self, canvas: Canvas, order: Order, op: OrderPosition, show_page=True, only_page=None):
+20 -5
View File
@@ -53,8 +53,8 @@ from django.utils.translation import gettext as _
from django_scopes import scope, scopes_disabled
from pretix.base.models import (
Checkin, CheckinList, Device, Event, ItemVariation, Order, OrderPosition,
QuestionOption,
Checkin, CheckinList, Device, Event, Gate, Item, ItemVariation, Order,
OrderPosition, QuestionOption,
)
from pretix.base.signals import checkin_created, order_placed, periodic_task
from pretix.helpers import OF_SELF
@@ -109,11 +109,26 @@ def _logic_annotate_for_graphic_explain(rules, ev, rule_data, now_dt):
var = values[0] if isinstance(values, list) else values
val = rule_data[var]
if var == "product":
val = str(event.items.get(pk=val))
try:
val = str(event.items.get(pk=val))
except Item.DoesNotExist:
val = "?"
elif var == "variation":
val = str(ItemVariation.objects.get(item__event=event, pk=val))
if not val:
val = "-"
else:
try:
val = str(ItemVariation.objects.get(item__event=event, pk=val))
except ItemVariation.DoesNotExist:
val = "?"
elif var == "gate":
val = str(event.organizer.gates.get(pk=val))
if not val:
val = "-"
else:
try:
val = str(event.organizer.gates.get(pk=val))
except Gate.DoesNotExist:
val = "?"
elif isinstance(val, datetime):
val = date_format(val.astimezone(ev.timezone), "SHORT_DATETIME_FORMAT")
return {"var": var, "__result": val}
+9 -8
View File
@@ -54,14 +54,15 @@ class DataImportError(LazyLocaleException):
super().__init__(msg)
def parse_csv(file, length=None, mode="strict"):
def parse_csv(file, length=None, mode="strict", charset=None):
file.seek(0)
data = file.read(length)
try:
import chardet
charset = chardet.detect(data)['encoding']
except ImportError:
charset = file.charset
if not charset:
try:
import chardet
charset = chardet.detect(data)['encoding']
except ImportError:
charset = file.charset
data = data.decode(charset or "utf-8", mode)
# If the file was modified on a Mac, it only contains \r as line breaks
if '\r' in data and '\n' not in data:
@@ -85,12 +86,12 @@ def setif(record, obj, attr, setting):
@app.task(base=ProfiledEventTask, throws=(DataImportError,))
def import_orders(event: Event, fileid: str, settings: dict, locale: str, user) -> None:
def import_orders(event: Event, fileid: str, settings: dict, locale: str, user, charset=None) -> None:
cf = CachedFile.objects.get(id=fileid)
user = User.objects.get(pk=user)
with language(locale, event.settings.region):
cols = get_all_columns(event)
parsed = parse_csv(cf.file)
parsed = parse_csv(cf.file, charset=charset)
orders = []
order = None
data = []
+6
View File
@@ -139,6 +139,8 @@ error_messages = {
'meantime. Please see below for details.'
),
'internal': gettext_lazy("An internal error occurred, please try again."),
'race_condition': gettext_lazy("This order was changed by someone else simultaneously. Please check if your "
"changes are still accurate and try again."),
'empty': gettext_lazy("Your cart is empty."),
'max_items_per_product': ngettext_lazy(
"You cannot select more than %(max)s item of the product %(product)s. We removed the surplus items from your cart.",
@@ -2714,6 +2716,10 @@ class OrderChangeManager:
self._payment_fee_diff()
with transaction.atomic():
locked_instance = Order.objects.select_for_update(of=OF_SELF).get(pk=self.order.pk)
if locked_instance.last_modified != self.order.last_modified:
raise OrderError(error_messages['race_condition'])
if self.order.status in (Order.STATUS_PENDING, Order.STATUS_PAID):
if check_quotas:
self._check_quotas()
File diff suppressed because one or more lines are too long
+53
View File
@@ -0,0 +1,53 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from django.http import Http404, HttpResponse
from pretix.base.settings import GlobalSettingsObject
def association(request, *args, **kwargs):
# This is a crutch to enable event- or organizer-level overrides for the default
# ApplePay MerchantID domain validation/association file.
# We do not provide any FormFields for this on purpose!
#
# Please refer to https://github.com/pretix/pretix/pull/3611 to get updates on
# the upcoming and official way to temporarily override the association-file,
# which will make sure that there are no conflicting requests at the same time.
#
# Should you opt to manually inject a different association-file into an organizer
# or event settings store, we do recommend to remove the setting once you're
# done and the domain has been validated.
#
# If you do not need Stripe's default domain association credential and would
# rather serve a different default credential, you can do so through the
# Global Settings editor.
if hasattr(request, 'event'):
settings = request.event.settings
elif hasattr(request, 'organizer'):
settings = request.organizer.settings
else:
settings = GlobalSettingsObject().settings
if not settings.get('apple_domain_association', None):
raise Http404('')
else:
return HttpResponse(settings.get('apple_domain_association'))
+44 -4
View File
@@ -20,6 +20,8 @@
# <https://www.gnu.org/licenses/>.
#
import logging
from collections import defaultdict
from datetime import timedelta
from importlib import import_module
from zoneinfo import ZoneInfo
@@ -29,18 +31,20 @@ from celery.result import AsyncResult
from django.conf import settings
from django.contrib import messages
from django.core.exceptions import PermissionDenied, ValidationError
from django.core.files.uploadedfile import UploadedFile
from django.db import transaction
from django.http import HttpResponse, JsonResponse, QueryDict
from django.shortcuts import redirect, render
from django.test import RequestFactory
from django.utils import timezone, translation
from django.utils.timezone import get_current_timezone
from django.utils.datastructures import MultiValueDict
from django.utils.timezone import get_current_timezone, now
from django.utils.translation import get_language, gettext as _
from django.views import View
from django.views.generic import FormView
from redis import ResponseError
from pretix.base.models import User
from pretix.base.models import CachedFile, User
from pretix.base.services.tasks import ProfiledEventTask
from pretix.celery_app import app
@@ -228,15 +232,39 @@ class AsyncFormView(AsyncMixin, FormView):
)
def __init_subclass__(cls):
class StoredUploadedFile(UploadedFile):
pass
def async_execute(self, *, request_path, query_string, form_kwargs, locale, tz, url_kwargs=None, url_args=None,
organizer=None, event=None, user=None, session_key=None):
view_instance = cls()
form_kwargs['data'] = QueryDict(form_kwargs['data'])
if form_kwargs['files']:
for k, l in form_kwargs['files'].items():
uploadedfiles = []
for cfid in l:
cf = CachedFile.objects.get(pk=cfid)
uploadedfiles.append(StoredUploadedFile(
file=cf.file,
name=cf.filename,
content_type=cf.type,
size=cf.file.size,
charset=None,
content_type_extra=None,
))
form_kwargs['files'][k] = uploadedfiles
form_kwargs['files'] = MultiValueDict(form_kwargs['files'])
req = RequestFactory().post(
request_path + '?' + query_string,
data=form_kwargs['data'].urlencode(),
content_type='application/x-www-form-urlencoded'
)
if form_kwargs['files']:
req._load_post_and_files()
req._files = form_kwargs['files']
view_instance.request = req
view_instance.kwargs = url_kwargs
view_instance.args = url_args
@@ -287,8 +315,19 @@ class AsyncFormView(AsyncMixin, FormView):
return super().get(request, *args, **kwargs)
def form_valid(self, form):
if form.files:
raise TypeError('File upload currently not supported in AsyncFormView')
files = defaultdict(list)
if self.request.FILES:
for k, v in self.request.FILES.items():
cf = CachedFile.objects.create(
expires=now() + timedelta(hours=2),
date=now(),
web_download=False,
filename=v.name,
type=v.content_type,
)
cf.file.save('uploaded_file.dat', v)
files[k].append(str(cf.pk))
form_kwargs = {
k: v for k, v in self.get_form_kwargs().items()
}
@@ -299,6 +338,7 @@ class AsyncFormView(AsyncMixin, FormView):
form_kwargs['instance'] = None
form_kwargs.setdefault('data', QueryDict())
form_kwargs['data'] = form_kwargs['data'].urlencode()
form_kwargs['files'] = files
form_kwargs['initial'] = {}
form_kwargs.pop('event', None)
kwargs = {
@@ -38,6 +38,7 @@ from django import forms
from django.utils.translation import gettext_lazy as _
from i18nfield.forms import I18nFormField, I18nTextarea, I18nTextInput
from pretix import settings
from pretix.base.forms import SecretKeySettingsField, SettingsForm
from pretix.base.settings import GlobalSettingsObject
from pretix.base.signals import register_global_settings
@@ -95,6 +96,13 @@ class GlobalSettingsForm(SettingsForm):
sample='&copy; &lt;a href=&quot;https://www.openstreetmap.org/copyright&quot;&gt;OpenStreetMap&lt;/a&gt; contributors'
)
)),
('apple_domain_association', forms.CharField(
required=False,
label=_("ApplePay MerchantID Domain Association"),
help_text=_("Will be served at {domain}/.well-known/apple-developer-merchantid-domain-association").format(
domain=settings.SITE_URL
)
))
])
responses = register_global_settings.send(self)
for r, response in sorted(responses, key=lambda r: str(r[0])):
@@ -32,7 +32,7 @@
{% abseventurl request.event "presale:event.index" as indexurl %}
{% endif %}
{% if form.cleaned_data.compatibility_mode %}
<pre>&lt;div class="pretix-widget-compat" event="{% abseventurl request.event "presale:event.index" %}"{% if form.cleaned_data.subevent %} subevent="{{ form.cleaned_data.subevent.pk }}"{% endif %}{% if form.cleaned_data.voucher %} voucher="{{ form.cleaned_data.voucher }}"{% endif %}&gt;&lt;/div&gt;
<pre>&lt;div class="pretix-widget-compat" event="{% abseventurl request.event "presale:event.index" %}"{% if form.cleaned_data.subevent %} subevent="{{ form.cleaned_data.subevent.pk }}"{% endif %}{% if form.cleaned_data.voucher %} voucher="{{ form.cleaned_data.voucher }}"{% endif %} single-item-select="button"&gt;&lt;/div&gt;
&lt;noscript&gt;
&lt;div class="pretix-widget"&gt;
&lt;div class="pretix-widget-info-message"&gt;
@@ -45,7 +45,7 @@
&lt;/noscript&gt;
</pre>
{% else %}
<pre>&lt;pretix-widget event="{% abseventurl request.event "presale:event.index" %}"{% if form.cleaned_data.subevent %} subevent="{{ form.cleaned_data.subevent.pk }}"{% endif %}{% if form.cleaned_data.voucher %} voucher="{{ form.cleaned_data.voucher }}"{% endif %}&gt;&lt;/pretix-widget&gt;
<pre>&lt;pretix-widget event="{% abseventurl request.event "presale:event.index" %}"{% if form.cleaned_data.subevent %} subevent="{{ form.cleaned_data.subevent.pk }}"{% endif %}{% if form.cleaned_data.voucher %} voucher="{{ form.cleaned_data.voucher }}"{% endif %} single-item-select="button"&gt;&lt;/pretix-widget&gt;
&lt;noscript&gt;
&lt;div class="pretix-widget"&gt;
&lt;div class="pretix-widget-info-message"&gt;
@@ -84,6 +84,13 @@
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td><strong>{% trans "Sum" %}</strong></td>
<td class="text-right"><strong>{{ total }}</strong></td>
<td></td>
</tr>
</tfoot>
</table>
</div>
{% endif %}
@@ -10,7 +10,7 @@
<h3 class="panel-title">{% trans "Upload a new file" %}</h3>
</div>
<div class="panel-body">
<form action="" method="post" enctype="multipart/form-data" class="form-inline">
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>
{% blocktrans trimmed %}
@@ -21,6 +21,15 @@
<div class="form-group">
<label for="file">{% trans "Import file" %}: </label> <input id="file" type="file" name="file"/>
</div>
<div class="form-group">
<label for="file">{% trans "Character set" %}: </label>
<select name="charset" class="form-control">
<option>{% trans "Detect automatically" %}</option>
{% for e in encodings %}
<option value="{{ e }}">{{ e }}</option>
{% endfor %}
</select>
</div>
<div class="clearfix"></div>
<button class="btn btn-primary pull-right flip" type="submit">
<span class="icon icon-upload"></span> {% trans "Start import" %}
@@ -8,7 +8,7 @@
{% block title %}{% trans "Date" context "subevent" %}{% endblock %}
{% block content %}
<h1>{% trans "Create multiple dates" context "subevent" %}</h1>
<form action="" method="post" class="form-horizontal" id="subevent-bulk-create-form" data-asynctask>
<form action="" method="post" class="form-horizontal" id="subevent-bulk-create-form" data-asynctask enctype="multipart/form-data">
{% csrf_token %}
{% bootstrap_form_errors form %}
{% for f in itemvar_forms %}
@@ -11,7 +11,7 @@
{% else %}
<h1>{% trans "Date" context "subevent" %}</h1>
{% endif %}
<form action="" method="post" class="form-horizontal">
<form action="" method="post" class="form-horizontal" enctype="multipart/form-datai">
{% csrf_token %}
{% bootstrap_form_errors form %}
{% for f in itemvar_forms %}
+2 -2
View File
@@ -717,13 +717,13 @@ class QuestionView(EventPermissionRequiredMixin, QuestionMixin, ChartContainingV
total = sum(a['count'] for a in r)
for a in r:
a['percentage'] = (a['count'] / total * 100.) if total else 0
return r
return r, total
def get_context_data(self, **kwargs):
ctx = super().get_context_data()
ctx['items'] = self.object.items.all()
stats = self.get_answer_statistics()
ctx['stats'] = stats
ctx['stats'], ctx['total'] = stats
ctx['stats_json'] = json.dumps(stats)
return ctx
+30 -4
View File
@@ -51,6 +51,15 @@ from pretix.control.forms.orderimport import ProcessForm
from pretix.control.permissions import EventPermissionRequiredMixin
logger = logging.getLogger(__name__)
ENCODINGS = (
"utf8", "utf16", "utf32",
"iso-8859-1", "iso-8859-2", "iso-8859-3", "iso-8859-4", "iso-8859-5", "iso-8859-6", "iso-8859-7",
"iso-8859-8", "iso-8859-9", "iso-8859-10", "iso-8859-11", "iso-8859-12", "iso-8859-13", "iso-8859-14",
"iso-8859-15", "iso-8859-16",
"maccyrillic", "macgreek", "maciceland", "maclatin2", "macroman", "macturkish",
"windows-1250", "windows-1251", "windows-1252", "windows-1253", "windows-1254", "windows-1255",
"windows-1256", "windows-1257", "windows-1258"
)
class ImportView(EventPermissionRequiredMixin, TemplateView):
@@ -83,11 +92,20 @@ class ImportView(EventPermissionRequiredMixin, TemplateView):
type='text/csv',
)
cf.file.save('import.csv', request.FILES['file'])
if self.request.POST.get("charset") in ENCODINGS:
charset = self.request.POST.get("charset")
else:
charset = "auto"
return redirect(reverse('control:event.orders.import.process', kwargs={
'event': request.event.slug,
'organizer': request.organizer.slug,
'file': cf.id
}))
}) + "?charset=" + charset)
def get_context_data(self, **kwargs):
return super().get_context_data(encodings=ENCODINGS)
class ProcessView(EventPermissionRequiredMixin, AsyncAction, FormView):
@@ -108,9 +126,13 @@ class ProcessView(EventPermissionRequiredMixin, AsyncAction, FormView):
def form_valid(self, form):
self.request.event.settings.order_import_settings = form.cleaned_data
if self.request.GET.get("charset") in ENCODINGS:
charset = self.request.GET.get("charset")
else:
charset = None
return self.do(
self.request.event.pk, self.file.id, form.cleaned_data, self.request.LANGUAGE_CODE,
self.request.user.pk
self.request.user.pk, charset
)
@cached_property
@@ -119,8 +141,12 @@ class ProcessView(EventPermissionRequiredMixin, AsyncAction, FormView):
@cached_property
def parsed(self):
if self.request.GET.get("charset") in ENCODINGS:
charset = self.request.GET.get("charset")
else:
charset = None
try:
return parse_csv(self.file.file, 1024 * 1024)
return parse_csv(self.file.file, 1024 * 1024, charset=charset)
except UnicodeDecodeError:
messages.warning(
self.request,
@@ -129,7 +155,7 @@ class ProcessView(EventPermissionRequiredMixin, AsyncAction, FormView):
"Some characters were replaced with a placeholder."
)
)
return parse_csv(self.file.file, 1024 * 1024, "replace")
return parse_csv(self.file.file, 1024 * 1024, "replace", charset=charset)
def get(self, request, *args, **kwargs):
if 'async_id' in request.GET and settings.HAS_CELERY:
+3 -6
View File
@@ -441,12 +441,9 @@ class OrderView(EventPermissionRequiredMixin, DetailView):
ctx = super().get_context_data(**kwargs)
ctx['can_generate_invoice'] = invoice_qualified(self.order) and (
self.request.event.settings.invoice_generate in ('admin', 'user', 'paid', 'True')
) and (
) and self.order.status in (Order.STATUS_PAID, Order.STATUS_PENDING) and (
not self.order.invoices.exists()
or (
self.order.status in (Order.STATUS_PAID, Order.STATUS_PENDING)
and self.order.invoices.filter(is_cancellation=True).count() >= self.order.invoices.filter(is_cancellation=False).count()
)
or self.order.invoices.filter(is_cancellation=True).count() >= self.order.invoices.filter(is_cancellation=False).count()
)
return ctx
@@ -1612,7 +1609,7 @@ class OrderInvoiceReissue(OrderView):
messages.error(self.request, _('The invoice has been cleaned of personal data.'))
else:
c = generate_cancellation(inv)
if self.order.status != Order.STATUS_CANCELED:
if self.order.status not in (Order.STATUS_CANCELED, Order.STATUS_EXPIRED):
inv = generate_invoice(self.order)
else:
inv = c
@@ -0,0 +1,19 @@
# Generated by Django 4.2.4 on 2023-09-26 12:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pretixhelpers", "0003_thumbnail_created"),
]
operations = [
migrations.AlterField(
model_name="thumbnail",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
]
+6 -7
View File
@@ -5,7 +5,7 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-09-25 23:11+0000\n"
"PO-Revision-Date: 2023-09-25 23:46+0000\n"
"PO-Revision-Date: 2023-10-16 08:33+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix/de/"
">\n"
@@ -8236,7 +8236,7 @@ msgstr ""
"\n"
"https://pretix.eu/about/de/blog/\n"
"\n"
"Viele Grüße, \n"
"Viele Grüße, \n"
"\n"
"Ihre pretix-Entwickler"
@@ -25066,11 +25066,10 @@ msgid ""
"record."
msgstr ""
"Wir haben einen SPF-Eintrag für die verwendete Domain gefunden, aber er "
"enthält den Mailserver dieses Systems nicht. Sie können trotzdem fortfahren, "
"aber es gibt eine große Chance, dass die E-Mails beim Empfänger in den Spam-"
"Ordner sortiert werden oder nicht ankommen. Sie sollten die DNS-"
"Einstellungen Ihrer Domain ändern, sodass dieses System im SPF-Eintrag "
"vorkommt."
"enthält den Mailserver dieses Systems nicht. Die E-Mails würden daher mit "
"sehr hoher Wahrscheinlichkeit beim Empfänger in den Spam-Ordner sortiert "
"werden oder nicht ankommen. Sie sollten die DNS-Einstellungen Ihrer Domain "
"ändern, sodass dieses System im SPF-Eintrag vorkommt."
#: pretix/control/views/mailsetup.py:211
msgid "The verification code was incorrect, please try again."
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-09-25 23:11+0000\n"
"PO-Revision-Date: 2023-09-25 23:45+0000\n"
"PO-Revision-Date: 2023-09-27 20:00+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
"pretix/pretix/de_Informal/>\n"
@@ -6392,8 +6392,8 @@ msgid ""
msgstr ""
"Wir empfehlen, diese Option zu aktivieren, wenn du die Gebühren Ihres "
"Zahlungsdienstleisters an Ihre Kunden weitergeben willst. Denk daran, oben "
"die richtigen Gebührensätze des Zahlungsanbieters einzustellen. <a "
"href=\"{docs_url}\" target=\"_blank\">Weitere Informationen</a>."
"die richtigen Gebührensätze des Zahlungsanbieters einzustellen. <a href=\""
"{docs_url}\" target=\"_blank\" rel=\"noopener\">Weitere Informationen</a>."
#: pretix/base/payment.py:394
msgid "Text on invoices"
@@ -25025,11 +25025,10 @@ msgid ""
"record."
msgstr ""
"Wir haben einen SPF-Eintrag für die verwendete Domain gefunden, aber er "
"enthält den Mailserver dieses Systems nicht. Du kannst trotzdem fortfahren, "
"aber es gibt eine große Chance, dass die E-Mails beim Empfänger in den Spam-"
"Ordner sortiert werden oder nicht ankommen. Du solltest die DNS-"
"Einstellungen deiner Domain ändern, sodass dieses System im SPF-Eintrag "
"vorkommt."
"enthält den Mailserver dieses Systems nicht. Die E-Mails würden daher mit "
"sehr hoher Wahrscheinlichkeit beim Empfänger in den Spam-Ordner sortiert "
"werden oder nicht ankommen. Du solltest die DNS-Einstellungen deiner Domain "
"ändern, sodass dieses System im SPF-Eintrag vorkommt."
#: pretix/control/views/mailsetup.py:211
msgid "The verification code was incorrect, please try again."
+127 -116
View File
@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-09-25 23:11+0000\n"
"PO-Revision-Date: 2023-09-18 07:47+0000\n"
"Last-Translator: Rubén Vargas <rd_01@rocketmail.com>\n"
"PO-Revision-Date: 2023-09-27 06:59+0000\n"
"Last-Translator: Igor Támara <igor@tamarapatino.org>\n"
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix/"
"es/>\n"
"Language: es\n"
@@ -4191,10 +4191,10 @@ msgid "Owned by ticket holder"
msgstr "Descargue sus entradas aquí:"
#: pretix/base/models/giftcards.py:93
#, fuzzy
msgid "The gift card code may only contain letters, numbers, dots and dashes."
msgstr ""
"La URL semántica solo puede contener letras, números, puntos y guiones."
"El código de la tarjeta de regalo puede contener solamente letras, números, "
"puntos y guiones."
#: pretix/base/models/giftcards.py:105
#: pretix/control/templates/pretixcontrol/organizers/giftcard.html:39
@@ -4203,9 +4203,8 @@ msgid "Special terms and conditions"
msgstr "Términos y condiciones especiales"
#: pretix/base/models/giftcards.py:217 pretix/base/models/giftcards.py:221
#, fuzzy
msgid "Manual transaction"
msgstr "Transliteración"
msgstr "Transacción manual"
#: pretix/base/models/invoices.py:185
#, python-format
@@ -4262,9 +4261,8 @@ msgid "{category} (Add-On products)"
msgstr "{category} (Productos Adicionales)"
#: pretix/base/models/items.py:172 pretix/base/models/items.py:228
#, fuzzy
msgid "Disable product for this date"
msgstr "Opcional. Ningún producto será vendido antes de esta fecha."
msgstr "Inhabilitar el producto para esta fecha"
#: pretix/base/models/items.py:176 pretix/base/models/items.py:232
#: pretix/base/models/items.py:480
@@ -4277,35 +4275,32 @@ msgid "This product will not be sold after the given date."
msgstr "Este producto no será vendido después de la fecha dada."
#: pretix/base/models/items.py:370
#, fuzzy
msgid "Event validity (default)"
msgstr "(Propuesta de evento)"
msgstr "Validez del evento (De forma predeterminada)"
#: pretix/base/models/items.py:371
#, fuzzy
msgid "Fixed time frame"
msgstr "Cliente"
msgstr "Ventana de tiempo fija"
#: pretix/base/models/items.py:372
#, fuzzy
msgid "Dynamic validity"
msgstr "Tarjeta de regalo"
msgstr "Validez dinámica"
#: pretix/base/models/items.py:379 pretix/base/models/items.py:661
msgid "Don't use re-usable media, use regular one-off tickets"
msgstr ""
msgstr "No usar medios reutilizables, usar tiquetes de un solo uso"
#: pretix/base/models/items.py:380
msgid "Require an existing medium to be re-used"
msgstr ""
msgstr "Exigir que se reuse un medio ya existente"
#: pretix/base/models/items.py:381
msgid "Require a previously unknown medium to be newly added"
msgstr ""
msgstr "Exigir que un medio desconocido anteriormente se adicione de nuevo"
#: pretix/base/models/items.py:382
msgid "Require either an existing or a new medium to be used"
msgstr ""
msgstr "Exigir que se use un medio existente o uno nuevo"
#: pretix/base/models/items.py:398 pretix/base/models/items.py:1264
#: pretix/control/templates/pretixcontrol/items/index.html:40
@@ -4322,7 +4317,7 @@ msgstr ""
#: pretix/base/models/items.py:416
msgid "This is shown below the product name in lists."
msgstr "Esto es mostrado debajo del nombre del producto en listas."
msgstr "Esto se muestra debajo del nombre del producto en listas."
#: pretix/base/models/items.py:421
msgid ""
@@ -4356,31 +4351,29 @@ msgstr ""
"evento"
#: pretix/base/models/items.py:448
#, fuzzy
msgid "Is a personalized ticket"
msgstr "Es un ticket de admision"
msgstr "Es un ticket personalizado"
#: pretix/base/models/items.py:450
#, fuzzy
msgid "Whether or not buying this product allows to enter attendee information"
msgstr ""
"Si la compra de este producto permite o no que una persona ingrese a su "
"evento"
"Si la compra de este producto permite o no ingresar la información del "
"asistente"
#: pretix/base/models/items.py:459
msgid "Show a waiting list for this ticket"
msgstr "Mostrar una lista de espera para este ticket"
msgstr "Mostrar una lista de espera para este tiquete"
#: pretix/base/models/items.py:460
#, fuzzy
msgid "This will only work if waiting lists are enabled for this event."
msgstr ""
"Esto sólo funcionará para las listas de espera habilitadas para este evento."
"Esto sólo funcionará si las listas de espera están habilitadas para este "
"evento."
#: pretix/base/models/items.py:464 pretix/base/settings.py:1333
#: pretix/control/forms/event.py:1594 pretix/control/forms/event.py:1598
msgid "Show number of tickets left"
msgstr "Mostrar el número de entradas que quedan"
msgstr "Mostrar el número de tiquetes que quedan"
#: pretix/base/models/items.py:465
msgid "Publicly show how many tickets are still available."
@@ -4402,6 +4395,12 @@ msgid ""
"out. There might be a short period in which both products are visible while "
"all tickets in the referenced quota are reserved, but not yet sold."
msgstr ""
"Si selecciona una quota aquí, este producto solamente se mostrará cuando la "
"quota no está disponible. Si se combina con la opción de esconder productos "
"vendidos, esto permite intercambiar productos por unos más costosos cuando "
"se agoten. Puede haber un período corto en el cual ambos productos son "
"visibles mientras todos los tiquetes en la quota referenciada están "
"reservados, pero todavía no vendidos."
#: pretix/base/models/items.py:501
msgid ""
@@ -4420,7 +4419,7 @@ msgid ""
msgstr ""
"Si este producto es parte de un pedido, el pedido será puesto en un estado "
"de \"aprobación\" y tendrá que ser confirmado por usted antes de que pueda "
"ser pagado y completado. Puede utilizarlo, por ejemplo, para entradas con "
"ser pagado y completado. Puede utilizarlo, por ejemplo, para tiquetes con "
"descuento que sólo están disponibles para grupos específicos."
#: pretix/base/models/items.py:514
@@ -4443,14 +4442,13 @@ msgstr ""
"predefinidos!"
#: pretix/base/models/items.py:527
#, fuzzy
msgid ""
"If this is checked, the usual cancellation and order change settings of this "
"event apply. If this is unchecked, orders containing this product can not be "
"canceled by users but only by you."
msgstr ""
"Si está chequeado, aplica la configuración usual de cancelación para este "
"evento. Si no está chequeado, las ordenes conteniendo este producto no "
"Si está seleccionado, aplica la configuración usual de cancelación para este "
"evento. Si no está seleccionado, las ordenes conteniendo este producto no "
"pueden ser canceladas por los usuarios, solo por usted."
#: pretix/base/models/items.py:534
@@ -4460,8 +4458,8 @@ msgid ""
"limit for this product."
msgstr ""
"Este producto sólo se puede comprar si se agrega al carrito por lo menos "
"esta vez muchas veces. Si mantiene el campo vacío o lo fija en 0, no hay "
"ningún límite especial para este producto."
"esta cantidad de veces. Si deja el campo vacío o lo fija en 0, no hay ningún "
"límite especial para este producto."
#: pretix/base/models/items.py:541
msgid ""
@@ -4470,10 +4468,10 @@ msgid ""
"product. The limit for the maximum number of items in the whole order "
"applies regardless."
msgstr ""
"Este producto sólo puede ser comprado como máximo este muchas veces dentro "
"de una orden. Si mantiene el campo vacío o lo fija en 0, no hay ningún "
"límite especial para este producto. El límite para el número máximo de "
"posiciones en todo el pedido se aplica independientemente."
"Este producto sólo puede ser comprado como máximo este número de veces "
"dentro de una orden. Si mantiene el campo vacío o lo fija en 0, no hay "
"ningún límite especial para este producto. El límite para el número máximo "
"de artículos en todo el pedido se aplica independientemente."
#: pretix/base/models/items.py:548 pretix/base/models/items.py:1072
msgid ""
@@ -4482,11 +4480,11 @@ msgid ""
"tickets to indicate to the person at check-in that the student ID card still "
"needs to be checked."
msgstr ""
"Si lo ajusta, la aplicación de registro mostrará una advertencia visible de "
"que este ticket requiere una atención especial. Puede utilizar esta opción, "
"por ejemplo, para los tickets de estudiante, con el fin de indicar a la "
"persona en el momento de la facturación que todavía es necesario comprobar "
"el carné de estudiante."
"Si lo selecciona, la aplicación de registro mostrará una advertencia visible "
"que indica que este tiquete requiere una atención especial. Puede utilizar "
"esta opción, por ejemplo, para los tiquetes de estudiante, con el fin de "
"indicar a la persona en el momento de la entrada que su carné tendrá que ser "
"validado."
#: pretix/base/models/items.py:556 pretix/base/models/items.py:1021
msgid ""
@@ -4494,8 +4492,8 @@ msgid ""
"current price is a discounted one. This is just a cosmetic setting and will "
"not actually impact pricing."
msgstr ""
"Si está configurado, se mostrará junto al precio actual para mostrar que el "
"precio actual es uno con descuento. Esto es sólo un escenario cosmético y no "
"Si está elegido, se mostrará junto al precio actual para mostrar que el "
"precio actual tiene descuento. Esto es sólo un ajuste cosmético y no "
"afectará realmente a los precios."
#: pretix/base/models/items.py:566
@@ -4507,38 +4505,41 @@ msgstr ""
"valor equivalente al producto."
#: pretix/base/models/items.py:576
#, fuzzy
msgid "Allowed membership types"
msgstr "Membresía del equipo"
msgstr "Tipos de membresía admitidos"
#: pretix/base/models/items.py:581 pretix/base/models/items.py:1042
msgid ""
"Do not show this unless the customer is logged in and has a valid "
"membership. Be aware that this means it will never be visible in the widget."
msgstr ""
"No utilice este a menos que el cliente esté autenticado y tiene una "
"membresía válida. Tenga en cuenta que esto significa que nunca será visible "
"en el componente."
#: pretix/base/models/items.py:590
msgid "This product creates a membership of type"
msgstr ""
msgstr "Este producto crea una membresía de tipo"
#: pretix/base/models/items.py:593
msgid ""
"The duration of the membership is the same as the duration of the event or "
"event series date"
msgstr ""
"La duración de la membresía es la misma que la duración del evento o la "
"serie de eventos"
#: pretix/base/models/items.py:597
msgid "Membership duration in days"
msgstr ""
msgstr "Duración de la membresía en días"
#: pretix/base/models/items.py:601
msgid "Membership duration in months"
msgstr ""
msgstr "Duración de la membresía en meses"
#: pretix/base/models/items.py:608
#, fuzzy
msgid "Validity"
msgstr "Válido hasta"
msgstr "Validez"
#: pretix/base/models/items.py:610
msgid ""
@@ -4551,21 +4552,27 @@ msgid ""
"change the settings here later, existing tickets will not be affected by the "
"change but keep their current validity."
msgstr ""
"Cuando se configura un evento regular o una serie de eventos con ventanas de "
"tiempo, típicamente NO requiere cambiar este valor. La configuración "
"predeterminada significa que el período de validez de los tiquetes no será "
"decidida por el producto, pero si por el evento y la configuración del "
"ingreso. Sólo use las otras opciones si necesita que se den cuenta de una "
"reserva de un tiquete de un año con una fecha dinámica de inicio. Tenga en "
"cuenta que la validez estará almacenada en el tiquete, de forma que si "
"cambia la configuración aquí posteriormente, los tiquetes no se afectarán "
"por el cambio y mantendrán la validez original."
#: pretix/base/models/items.py:618
#, fuzzy
msgid "Start of validity"
msgstr "Tarjeta de regalo"
msgstr "Inicio de validez"
#: pretix/base/models/items.py:619
#, fuzzy
msgid "End of validity"
msgstr "Finalización de preventa"
msgstr "Finalización de validez"
#: pretix/base/models/items.py:622
#, fuzzy
msgid "Minutes"
msgstr "minutos"
msgstr "Minutos"
#: pretix/base/models/items.py:626
msgid "Hours"
@@ -4576,32 +4583,31 @@ msgid "Days"
msgstr "Días"
#: pretix/base/models/items.py:634
#, fuzzy
msgid "Months"
msgstr "mes"
msgstr "Meses"
#: pretix/base/models/items.py:637
#, fuzzy
msgid "Customers can select the validity start date"
msgstr "Clientes pueden cancelar ordenes pagadas"
msgstr "Los clientes pueden seleccionar la fecha de inicio de la validez"
#: pretix/base/models/items.py:638
msgid "If not selected, the validity always starts at the time of purchase."
msgstr ""
"Si no se selecciona, la validez siempre comienza en el momento de la compra."
#: pretix/base/models/items.py:643
#, fuzzy
msgid "Maximum future start"
msgstr "Máximo total de la orden"
msgstr "Fecha máxima de inicio"
#: pretix/base/models/items.py:644
#, fuzzy
msgid "The selected start date may only be this many days in the future."
msgstr "El elemento seleccionado no pertenece a este evento."
msgstr ""
"La fecha de incio seleccionada puede ser máximo esta cantidad de días en el "
"futuro."
#: pretix/base/models/items.py:650
msgid "Reusable media policy"
msgstr "Condiciones de utilización de imágenes"
msgstr "Condiciones de utilización de medios"
#: pretix/base/models/items.py:652
msgid ""
@@ -4612,10 +4618,16 @@ msgid ""
"feature that also requires specific configuration of ticketing and printing "
"settings."
msgstr ""
"Si este producto debiera estar almacenado en un medio físico reusable, puede "
"adjuntar una política de medio físico. Esto no se requiere para tiquetes "
"regulares, que usan un código de barras de única vez, pero solamente para "
"productos como tiquetes renovables de temporada o de tarjetas de regalo de "
"muñeca recargables. Esta es una característica avanzada que también requiere "
"configuración específica de uso de tiquetes y configuración de impresión."
#: pretix/base/models/items.py:662
msgid "Reusable media type"
msgstr ""
msgstr "Tipo de medio reusable"
#: pretix/base/models/items.py:664
msgid ""
@@ -4623,6 +4635,10 @@ msgid ""
"Note that not all media types support all types of products, and not all "
"media types are supported across all sales channels or check-in processes."
msgstr ""
"Elija el tipo de medio físico que debería usarse para este producto. Tenga "
"en cuenta que no todos los tipos de medio soportan todos los tipos de "
"productos, y no todos los tipos de medios se soportan en todos los canales "
"de ventas o el proceso de entrada."
#: pretix/base/models/items.py:675 pretix/base/models/items.py:1505
#: pretix/control/forms/filter.py:409 pretix/control/forms/filter.py:1765
@@ -4644,11 +4660,14 @@ msgid ""
"If you select a reusable media policy, you also need to select a reusable "
"media type."
msgstr ""
"Si selecciona una política de medios reusables también sera necesario que "
"seleccione un tipo de medio reusable."
#: pretix/base/models/items.py:849
#, fuzzy
msgid "The selected media type is not enabled in your organizer settings."
msgstr "El producto seleccionado no está activo o no tiene precio fijo."
msgstr ""
"El tipo de medio seleccionado no está activo en sus configuraciones de "
"organizador."
#: pretix/base/models/items.py:851
#, fuzzy
@@ -4656,10 +4675,11 @@ msgid "The selected media type does not support usage for tickets currently."
msgstr "El elemento seleccionado no pertenece a este evento."
#: pretix/base/models/items.py:853
#, fuzzy
msgid ""
"The selected media type does not support usage for gift cards currently."
msgstr "El elemento seleccionado no pertenece a este evento."
msgstr ""
"El tipo de medio seleccionado no soporta el uso de tarjetas de regalo en "
"este momento."
#: pretix/base/models/items.py:855
msgid ""
@@ -4667,6 +4687,9 @@ msgid ""
"Instead, gift cards for some reusable media types can be created or re-"
"charged directly at the POS."
msgstr ""
"En este momento no puede crear tarjetas de regalo con un tipo de medio "
"reusable. En cambio, las tarjetas de regalo para algunos tipos de medio "
"reusables pueden crearse o recargarse directamente en el POS."
#: pretix/base/models/items.py:863
msgid ""
@@ -4696,9 +4719,8 @@ msgid "This is shown below the variation name in lists."
msgstr "Esto se muestra debajo del nombre de la variación en las listas."
#: pretix/base/models/items.py:1025
#, fuzzy
msgid "Require approval"
msgstr "La nueva orden requiere aprobación"
msgstr "Se requiere aprobación"
#: pretix/base/models/items.py:1027
#, fuzzy
@@ -4716,9 +4738,8 @@ msgstr ""
#: pretix/base/models/items.py:1037 pretix/control/navigation.py:565
#: pretix/control/templates/pretixcontrol/organizers/membershiptypes.html:4
#: pretix/control/templates/pretixcontrol/organizers/membershiptypes.html:6
#, fuzzy
msgid "Membership types"
msgstr "Membresía del equipo"
msgstr "Tipos de membresía"
#: pretix/base/models/items.py:1049
#, fuzzy
@@ -4736,22 +4757,21 @@ msgid ""
"if a sales channel is selected here but not on product level, the variation "
"will not be available."
msgstr ""
"La selección de canales de venta para el producto como un todo toma "
"precedencia, de forma que si se selecciona un canal de ventas aquí pero no "
"un nivel de producto, la variación no estará disponible."
#: pretix/base/models/items.py:1064
#, fuzzy
msgid "Show only if a matching voucher is redeemed."
msgstr ""
"Este producto sólo se mostrará si se canjea un recibo que coincida con el "
"producto."
msgstr "Mostrar solamente si un vale coincidente se redime."
#: pretix/base/models/items.py:1066
#, fuzzy
msgid ""
"This variation will be hidden from the event page until the user enters a "
"voucher that unlocks this variation."
msgstr ""
"Este producto no se mostrará en la página del evento hasta que el usuario "
"introduzca un código de recibo que desbloquee el producto."
"introduzca un vale que desbloquee el producto."
#: pretix/base/models/items.py:1080 pretix/base/models/vouchers.py:262
#: pretix/base/models/waitinglist.py:99 pretix/base/orderimport.py:285
@@ -4780,23 +4800,22 @@ msgid ""
"If selected, adding add-ons to this ticket is free, even if the add-ons "
"would normally cost money individually."
msgstr ""
"Si se selecciona esta opción, la adición de complementos a este Ticket es "
"Si se selecciona esta opción, la adición de complementos a este tiquete es "
"gratuita, incluso si los complementos normalmente cuestan dinero "
"individualmente."
#: pretix/base/models/items.py:1283
#, fuzzy
msgid "Allow the same product to be selected multiple times"
msgstr "No se pueden seleccionar más de %s posiciones por pedido."
msgstr "Permitir que el mismo producto se seleccione múltiples veces"
#: pretix/base/models/items.py:1302
msgid "The add-on's category must belong to the same event as the item."
msgstr ""
"La categoría del add-on debe pertenecer al mismo evento que el artículo."
"La categoría del complemento debe pertenecer al mismo evento que el artículo."
#: pretix/base/models/items.py:1307
msgid "The item already has an add-on of this category."
msgstr "El artículo ya tiene un add-on de esta categoría."
msgstr "El artículo ya tiene un complemento de esta categoría."
#: pretix/base/models/items.py:1312
msgid "The minimum count needs to be equal to or greater than zero."
@@ -4811,14 +4830,12 @@ msgid "The maximum count needs to be greater than the minimum count."
msgstr "El conteo máximo debe ser mayor que el conteo mínimo."
#: pretix/base/models/items.py:1349
#, fuzzy
msgid "Bundled item"
msgstr "artículo inválido"
msgstr "artículo incluído"
#: pretix/base/models/items.py:1355
#, fuzzy
msgid "Bundled variation"
msgstr "Variación de la muestra"
msgstr "Variación de la agrupación"
#: pretix/base/models/items.py:1366
msgid "Designated price part"
@@ -4833,27 +4850,25 @@ msgid ""
msgstr ""
"Si se establece, se mostrará que este artículo agrupado es responsable de "
"cierto valor del precio bruto total. Esto puede ser importante en casos de "
"impuestos mixtos, pero de lo contrario puede mantenerse en blanco. Este "
"valor NO se agregará al precio del artículo base."
"impuestos mixtos, pero puede mantenerse en blanco. Este valor NO se agregará "
"al precio del artículo base."
#: pretix/base/models/items.py:1390
#, fuzzy
msgid "The bundled item must belong to the same event as the item."
msgstr ""
"La categoría del add-on debe pertenecer al mismo evento que el artículo."
"La agrupación de artículos debe pertenecer al mismo evento que el artículo."
#: pretix/base/models/items.py:1392
#, fuzzy
msgid "A variation needs to be set for this item."
msgstr "Esta variación no pertenece a este producto."
msgstr "Se requiere establecer una variación para este artículo."
#: pretix/base/models/items.py:1394
msgid "The chosen variation does not belong to this item."
msgstr "La variación escogida no pertenece a este item."
msgstr "La variación escogida no pertenece a este artículo."
#: pretix/base/models/items.py:1399
msgid "The count needs to be equal to or greater than zero."
msgstr "El conteo necesita ser igual o mayor que cero."
msgstr "Se requiere que la cantidad sea igual o mayor que cero."
#: pretix/base/models/items.py:1452
msgid "Number"
@@ -4873,11 +4888,11 @@ msgstr "Sí/No"
#: pretix/base/models/items.py:1456
msgid "Choose one from a list"
msgstr "Seleccione uno de la lista"
msgstr "Seleccione uno de una lista"
#: pretix/base/models/items.py:1457
msgid "Choose multiple from a list"
msgstr "Seleccione varios de la lista"
msgstr "Seleccione varios de una lista"
#: pretix/base/models/items.py:1458
msgid "File upload"
@@ -4907,12 +4922,12 @@ msgid "Question"
msgstr "Pregunta"
#: pretix/base/models/items.py:1484 pretix/base/models/items.py:1727
#, fuzzy
msgid ""
"The identifier may only contain letters, numbers, dots, dashes, and "
"underscores."
msgstr ""
"El nombre de la propiedad puede solo contener letras, números y guión bajo."
"El nombre de la propiedad puede contener sólo letras, números, puntos, "
"líneas y guión bajo."
#: pretix/base/models/items.py:1489
msgid "Help text"
@@ -4944,6 +4959,8 @@ msgstr ""
#: pretix/base/models/items.py:1515
msgid "Not supported by all check-in apps for all question types."
msgstr ""
"No se soporta por todas las aplicaciones de registro para todos los tipos de "
"preguntas."
#: pretix/base/models/items.py:1519
#: pretix/control/templates/pretixcontrol/items/questions.html:66
@@ -4951,43 +4968,37 @@ msgid "Hidden question"
msgstr "Pregunta oculta"
#: pretix/base/models/items.py:1520
#, fuzzy
msgid "This question will only show up in the backend."
msgstr "Esta pregunta se hará durante el registro."
msgstr "Esta pregunta sólo se mostrará en el backend."
#: pretix/base/models/items.py:1524
#, fuzzy
msgid "Print answer on invoices"
msgstr "Texto en las facturas"
msgstr "Imprimir respuesta en las facturas"
#: pretix/base/models/items.py:1532 pretix/base/models/items.py:1538
#: pretix/base/models/items.py:1544
#, fuzzy
msgid "Minimum value"
msgstr "Número mínimo"
msgstr "Valor mínimo"
#: pretix/base/models/items.py:1533 pretix/base/models/items.py:1536
#: pretix/base/models/items.py:1539 pretix/base/models/items.py:1542
#: pretix/base/models/items.py:1545 pretix/base/models/items.py:1548
#: pretix/base/models/items.py:1552
#, fuzzy
msgid "Currently not supported in our apps and during check-in"
msgstr "La pregunta no debe depender de otra realizada durante el registro."
msgstr "No se soporta en nuestras aplicaciones ni durante el registro"
#: pretix/base/models/items.py:1535 pretix/base/models/items.py:1541
#: pretix/base/models/items.py:1547
#, fuzzy
msgid "Maximum value"
msgstr "Número máximo"
msgstr "Valor máximo"
#: pretix/base/models/items.py:1550
#, fuzzy
msgid "Maximum length"
msgstr "Número máximo"
msgstr "Longitud máxima"
#: pretix/base/models/items.py:1556
msgid "Validate file to be a portrait"
msgstr ""
msgstr "Validar que el archivo sea vertical"
#: pretix/base/models/items.py:1557
msgid ""
+26 -50
View File
@@ -4,7 +4,7 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-09-25 23:11+0000\n"
"PO-Revision-Date: 2023-09-11 10:00+0000\n"
"PO-Revision-Date: 2023-10-06 03:00+0000\n"
"Last-Translator: Ronan LE MEILLAT <ronan.le_meillat@highcanfly.club>\n"
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix/fr/"
">\n"
@@ -13,7 +13,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 4.18.2\n"
"X-Generator: Weblate 5.0.2\n"
#: htmlcov/pretix_control_views_dashboards_py.html:963
#: pretix/control/templates/pretixcontrol/events/index.html:140
@@ -104,7 +104,7 @@ msgstr "Grec"
#: pretix/_base_settings.py:92
msgid "Indonesian"
msgstr ""
msgstr "Indonésien"
#: pretix/_base_settings.py:93
msgid "Italian"
@@ -3040,10 +3040,9 @@ msgid "Invalid placeholder(s): %(value)s"
msgstr "Caractère générique invalide : %(value)s"
#: pretix/base/forms/widgets.py:67
#, fuzzy, python-format
#| msgid "Sample city"
#, python-format
msgid "Sample: %s"
msgstr "Ville exemple"
msgstr "Exemple : %s"
#: pretix/base/forms/widgets.py:70
#, python-brace-format
@@ -6375,14 +6374,10 @@ msgstr ""
"la date indiquée."
#: pretix/base/payment.py:342
#, fuzzy
#| msgid ""
#| "Users will not be able to choose this payment provider after the given "
#| "date."
msgid ""
"Users will not be able to choose this payment provider before the given date."
msgstr ""
"Les utilisateurs ne pourront plus choisir ce fournisseur de paiement après "
"Les utilisateurs ne pourront plus choisir ce fournisseur de paiement avant "
"la date indiquée."
#: pretix/base/payment.py:347
@@ -7519,10 +7514,8 @@ msgid "Ticket type not allowed"
msgstr "Type de ticket interdit"
#: pretix/base/services/checkin.py:263
#, fuzzy
#| msgid "Create a new gate"
msgid "Wrong entrance gate"
msgstr "Créer une nouvelle porte"
msgstr "Mauvaise porte d'entrée"
#: pretix/base/services/checkin.py:285
msgid "time since last entry"
@@ -7545,16 +7538,14 @@ msgid "number of entries today"
msgstr "nombre dentrées aujourdhui"
#: pretix/base/services/checkin.py:290
#, fuzzy, python-brace-format
#| msgid "number of entries today"
#, python-brace-format
msgid "number of entries since {datetime}"
msgstr "nombre dentrées aujourdhui"
msgstr "nombre dentrées depuis {datetime}"
#: pretix/base/services/checkin.py:291
#, fuzzy, python-brace-format
#| msgid "number of entries today"
#, python-brace-format
msgid "number of entries before {datetime}"
msgstr "nombre dentrées aujourdhui"
msgstr "nombre dentrées avant {datetime}"
#: pretix/base/services/checkin.py:292
msgid "week day"
@@ -12125,18 +12116,11 @@ msgid "Filetype not allowed!"
msgstr "Type de fichier interdit !"
#: pretix/control/forms/__init__.py:321
#, fuzzy
#| msgid "Unofficial translation"
msgid "Community translations"
msgstr "Traduction non officielle"
msgstr "Traductions communautaires"
#: pretix/control/forms/__init__.py:323
#, fuzzy, python-brace-format
#| msgid ""
#| "This translation is not maintained by the pretix team. We cannot vouch "
#| "for its correctness and new or recently changed features might not be "
#| "translated and will show in English instead. You can help translating at "
#| "translate.pretix.eu."
#, python-brace-format
msgid ""
"These translations are not maintained by the pretix team. We cannot vouch "
"for their correctness and new or recently changed features might not be "
@@ -12146,24 +12130,20 @@ msgstr ""
"Cette traduction n'est pas maintenue par l'équipe de pretix. Nous ne pouvons "
"pas garantir son exactitude et les fonctionnalités nouvelles ou récemment "
"modifiées peuvent ne pas être traduites et s'afficheront en anglais à la "
"place. Vous pouvez nous aider à traduire sur translate.pretix.eu."
"place. Vous pouvez nous <a href=\"{translate_url}\" target=\"_blank\">aider "
"à traduire</a>."
#: pretix/control/forms/__init__.py:334
msgid "Development only"
msgstr ""
msgstr "Développement seulement"
#: pretix/control/forms/__init__.py:335
#, fuzzy
#| msgid ""
#| "The translation for this language is still in progress. This language can "
#| "currently only be selected on development installations of pretix, not in "
#| "production."
msgid ""
"These translations are still in progress. These languages can currently only "
"be selected on development installations of pretix, not in production."
msgstr ""
"La traduction de cette langue est toujours en cours. Ce langage ne peut "
"actuellement être sélectionné que sur des installations de développement de "
"La traduction de ces langues est toujours en cours. Ces langage ne peuvent "
"actuellement être sélectionnés que sur des installations de développement de "
"pretix, pas en production."
#: pretix/control/forms/checkin.py:93 pretix/control/forms/event.py:1524
@@ -20183,6 +20163,10 @@ msgid ""
"including most plugins. Even a low value might be enough if you only use "
"specific features. Untranslated texts will show up in English."
msgstr ""
"Ce pourcentage de textes est traduit dans toutes les parties du système, y "
"compris la plupart des plugins. Même une valeur faible peut suffire si vous "
"n'utilisez que des fonctionnalités spécifiques. Les textes non traduits "
"s'afficheront en anglais."
#: pretix/control/templates/pretixcontrol/oauth/app_delete.html:4
#: pretix/control/templates/pretixcontrol/oauth/app_delete.html:6
@@ -22494,14 +22478,6 @@ msgid "Other organizers accepting gift cards from you"
msgstr "Dautres organisateurs acceptent les cartes-cadeaux de votre part"
#: pretix/control/templates/pretixcontrol/organizers/giftcard_acceptance_list.html:87
#, fuzzy
#| msgid ""
#| "You can invite other organizers to accept your gift cards. After you have "
#| "done so, they need to go to the same page in their account and accept "
#| "your invitation. Note that other organizers will be able to add money to "
#| "gift cards as well that you will need to collect form them. It is your "
#| "responsibility to handle the exchange of money to offset the transactions "
#| "between the two organizers."
msgid ""
"You can invite other organizers to accept your gift cards. After you have "
"done so, they need to go to the same page in their account and accept your "
@@ -22514,8 +22490,8 @@ msgstr ""
"Une fois que vous lavez fait, ils doivent aller à la même page dans leur "
"compte et accepter votre invitation. Notez que dautres organisateurs "
"pourront également ajouter de largent aux cartes-cadeaux que vous devrez "
"collecter auprès deux. Il est de votre responsabilité de gérer l’échange "
"dargent pour compenser les transactions entre les deux organisateurs."
"collecter auprès deux. Il est de votre responsabilité de gérer l’échange d"
"argent pour compenser les transactions entre les deux organisateurs."
#: pretix/control/templates/pretixcontrol/organizers/giftcard_acceptance_list.html:95
msgid ""
@@ -29725,8 +29701,8 @@ msgid ""
"Sorry, there was an error in the payment process. Please check the link in "
"your emails to continue."
msgstr ""
"Désolé, une erreur sest produite dans le processus de paiement. Veuillez "
"vérifier le lien dans vos e-mails pour continuer."
"Désolé, une erreur s'est produite lors du traitement du paiement. SVP "
"vérifiez le lien dans vos courriels pour continuer."
#: pretix/plugins/stripe/views.py:567
msgid ""
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-09-25 23:11+0000\n"
"PO-Revision-Date: 2022-05-31 10:01+0000\n"
"Last-Translator: Aya Yabuki <a.yabuki@enobyte.com>\n"
"PO-Revision-Date: 2023-09-28 14:05+0000\n"
"Last-Translator: Takanori Suzuki <takanori@takanory.net>\n"
"Language-Team: Japanese <https://translate.pretix.eu/projects/pretix/pretix/"
"ja/>\n"
"Language: ja\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 4.12\n"
"X-Generator: Weblate 5.0.2\n"
#: htmlcov/pretix_control_views_dashboards_py.html:963
#: pretix/control/templates/pretixcontrol/events/index.html:140
@@ -52,11 +52,11 @@ msgstr "発売中"
#: pretix/_base_settings.py:78
msgid "English"
msgstr ""
msgstr "英語"
#: pretix/_base_settings.py:79
msgid "German"
msgstr ""
msgstr "ドイツ語"
#: pretix/_base_settings.py:80
msgid "German (informal)"
+16
View File
@@ -125,6 +125,22 @@ OPTIONS = OrderedDict([
'offsets': [pagesizes.landscape(pagesizes.A4)[0] / 4, pagesizes.landscape(pagesizes.A4)[1] / 2],
'pagesize': pagesizes.landscape(pagesizes.A4),
}),
('avery_63x29', {
'name': 'Avery Zweckform 63.5 x 29.6 mm (L4784)',
'cols': 3,
'rows': 9,
'margins': [15.3 * mm, 7.15 * mm, 15.3 * mm, 7.15 * mm],
'offsets': [66.1 * mm, 29.6 * mm],
'pagesize': pagesizes.A4,
}),
('avery_80x50', {
'name': 'Avery Zweckform 80 x 50 mm (L4785)',
'cols': 2,
'rows': 5,
'margins': [13.5 * mm, 17.5 * mm, 13.5 * mm, 17.5 * mm],
'offsets': [95 * mm, 55 * mm],
'pagesize': pagesizes.A4,
}),
('durable_54x90', {
'name': 'DURABLE BADGEMAKER® 54 x 90 mm (1445-02)',
'cols': 2,
@@ -0,0 +1,26 @@
# Generated by Django 4.2.4 on 2023-09-26 12:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("badges", "0002_auto_20190201_1424"),
]
operations = [
migrations.AlterField(
model_name="badgeitem",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="badgelayout",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
]
@@ -0,0 +1,33 @@
# Generated by Django 4.2.4 on 2023-09-26 12:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("banktransfer", "0009_banktransaction_currency"),
]
operations = [
migrations.AlterField(
model_name="bankimportjob",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="banktransaction",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="refundexport",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.4 on 2023-09-26 12:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("paypal", "0003_migrate_to_v2"),
]
operations = [
migrations.AlterField(
model_name="referencedpaypalobject",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
]
@@ -0,0 +1,23 @@
# Generated by Django 4.2.4 on 2023-09-26 12:01
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pretixbase", "0246_bigint"),
("sendmail", "0006_rule_subevent_alter_rule_checked_in_status"),
]
operations = [
migrations.AlterField(
model_name="rule",
name="subevent",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.PROTECT,
to="pretixbase.subevent",
),
),
]
@@ -0,0 +1,26 @@
# Generated by Django 4.2.4 on 2023-09-26 12:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("stripe", "0004_auto_20211208_1407"),
]
operations = [
migrations.AlterField(
model_name="referencedstripeobject",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="registeredapplepaydomain",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
]
File diff suppressed because one or more lines are too long
+1 -11
View File
@@ -25,8 +25,7 @@ from pretix.multidomain import event_url
from .views import (
OrganizerSettingsFormView, ReturnView, ScaReturnView, ScaView,
applepay_association, oauth_disconnect, oauth_return, redirect_view,
webhook,
oauth_disconnect, oauth_return, redirect_view, webhook,
)
event_patterns = [
@@ -38,13 +37,6 @@ event_patterns = [
re_path(r'^sca/(?P<order>[^/]+)/(?P<hash>[^/]+)/(?P<payment>[0-9]+)/return/$',
ScaReturnView.as_view(), name='sca.return'),
])),
re_path(r'^.well-known/apple-developer-merchantid-domain-association$',
applepay_association, name='applepay.association'),
]
organizer_patterns = [
re_path(r'^.well-known/apple-developer-merchantid-domain-association$',
applepay_association, name='applepay.association'),
]
urlpatterns = [
@@ -54,6 +46,4 @@ urlpatterns = [
OrganizerSettingsFormView.as_view(), name='settings.connect'),
re_path(r'^_stripe/webhook/$', webhook, name='webhook'),
re_path(r'^_stripe/oauth_return/$', oauth_return, name='oauth.return'),
re_path(r'^.well-known/apple-developer-merchantid-domain-association$',
applepay_association, name='applepay.association'),
]
-7
View File
@@ -476,13 +476,6 @@ def oauth_disconnect(request, **kwargs):
}))
@xframe_options_exempt
def applepay_association(request, *args, **kwargs):
r = render(request, 'pretixplugins/stripe/apple-developer-merchantid-domain-association')
r._csp_ignore = True
return r
class StripeOrderView:
def dispatch(self, request, *args, **kwargs):
try:
@@ -0,0 +1,33 @@
# Generated by Django 4.2.4 on 2023-09-26 12:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("ticketoutputpdf", "0007_auto_20181123_1059"),
]
operations = [
migrations.AlterField(
model_name="ticketlayout",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="ticketlayout",
name="layout",
field=models.TextField(
default='[{\n "type":"textarea",\n "left":"17.50",\n "bottom":"274.60",\n "fontsize":"16.0",\n "color":[\n 0,\n 0,\n 0,\n 1\n ],\n "fontfamily":"Open Sans",\n "bold":false,\n "italic":false,\n "width":"175.00",\n "content":"event_name",\n "text":"Sample event name",\n "align":"left"\n},\n{\n "type":"textarea",\n "left":"17.50",\n "bottom":"262.90",\n "fontsize":"13.0",\n "color":[\n 0,\n 0,\n 0,\n 1\n ],\n "fontfamily":"Open Sans",\n "bold":false,\n "italic":false,\n "width":"110.00",\n "content":"itemvar",\n "text":"Sample product sample variation",\n "align":"left"\n},\n{\n "type":"textarea",\n "left":"17.50",\n "bottom":"252.50",\n "fontsize":"13.0",\n "color":[\n 0,\n 0,\n 0,\n 1\n ],\n "fontfamily":"Open Sans",\n "bold":false,\n "italic":false,\n "width":"110.00",\n "content":"attendee_name",\n "text":"John Doe",\n "align":"left"\n},\n{\n "type":"textarea",\n "left":"17.50",\n "bottom":"242.10",\n "fontsize":"13.0",\n "color":[\n 0,\n 0,\n 0,\n 1\n ],\n "fontfamily":"Open Sans",\n "bold":false,\n "italic":false,\n "width":"110.00",\n "content":"event_begin",\n "text":"2016-05-31 20:00",\n "align":"left"\n},\n{\n "type":"textarea",\n "left":"17.50",\n "bottom":"231.70",\n "fontsize":"13.0",\n "color":[\n 0,\n 0,\n 0,\n 1\n ],\n "fontfamily":"Open Sans",\n "bold":false,\n "italic":false,\n "width":"110.00",\n "content":"seat",\n "text":"Ground floor, Row 3, Seat 4",\n "align":"left"\n},\n{\n "type":"textarea",\n "left":"17.50",\n "bottom":"204.80",\n "fontsize":"13.0",\n "color":[\n 0,\n 0,\n 0,\n 1\n ],\n "fontfamily":"Open Sans",\n "bold":false,\n "italic":false,\n "width":"110.00",\n "content":"event_location",\n "text":"Random City",\n "align":"left"\n},\n{\n "type":"textarea",\n "left":"17.50",\n "bottom":"194.50",\n "fontsize":"13.0",\n "color":[\n 0,\n 0,\n 0,\n 1\n ],\n "fontfamily":"Open Sans",\n "bold":false,\n "italic":false,\n "width":"30.00",\n "content":"order",\n "text":"A1B2C",\n "align":"left"\n},\n{\n "type":"textarea",\n "left":"52.50",\n "bottom":"194.50",\n "fontsize":"13.0",\n "color":[\n 0,\n 0,\n 0,\n 1\n ],\n "fontfamily":"Open Sans",\n "bold":false,\n "italic":false,\n "width":"45.00",\n "content":"price",\n "text":"123.45 EUR",\n "align":"right"\n},\n{\n "type":"textarea",\n "left":"102.50",\n "bottom":"194.50",\n "fontsize":"13.0",\n "color":[\n 0,\n 0,\n 0,\n 1\n ],\n "fontfamily":"Open Sans",\n "bold":false,\n "italic":false,\n "width":"90.00",\n "content":"secret",\n "text":"tdmruoekvkpbv1o2mv8xccvqcikvr58u",\n "align":"left"\n},\n{\n "type":"barcodearea",\n "left":"130.40",\n "bottom":"204.50",\n "size":"64.00",\n "content":"secret"\n},\n{\n "type":"poweredby",\n "left":"88.72",\n "bottom":"10.00",\n "size":"20.00",\n "content":"dark"\n}]'
),
),
migrations.AlterField(
model_name="ticketlayoutitem",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
]
+12 -7
View File
@@ -68,13 +68,18 @@ def get_public_ical(events):
else:
vevent.add('dtstart').value = ev.date_from.astimezone(tz).date()
if event.settings.show_date_to and ev.date_to:
if event.settings.show_times:
vevent.add('dtend').value = ev.date_to.astimezone(tz)
else:
# with full-day events date_to in pretix is included (e.g. last day)
# whereas dtend in vcalendar is non-inclusive => add one day for export
vevent.add('dtend').value = ev.date_to.astimezone(tz).date() + datetime.timedelta(days=1)
# always add dtend as calendar apps otherwise have display issues
use_date_to = event.settings.show_date_to and ev.date_to
dtend = (ev.date_to if use_date_to else ev.date_from).astimezone(tz)
if not event.settings.show_times:
# with full-day events date_to in pretix is included (e.g. last day)
# whereas dtend in vcalendar is non-inclusive => add one day for export
dtend = dtend.date() + datetime.timedelta(days=1)
elif not use_date_to:
# date_from used as end-date => add 1h as a default duration
dtend = dtend + datetime.timedelta(hours=1)
vevent.add('dtend').value = dtend
descr = []
descr.append(_('Tickets: {url}').format(url=url))
+3 -2
View File
@@ -571,12 +571,13 @@ class RedeemView(NoSearchIndexViewMixin, EventViewMixin, CartMixin, TemplateView
for item in items])
context['allfree'] = all(
item.display_price.gross == Decimal('0.00') for item in items if not item.has_variations
item.display_price.gross == Decimal('0.00') and not item.mandatory_priced_addons
for item in items if not item.has_variations
) and all(
all(
var.display_price.gross == Decimal('0.00')
for var in item.available_variations
)
) and not item.mandatory_priced_addons
for item in items if item.has_variations
)
+14 -10
View File
@@ -562,17 +562,21 @@ class ConfirmChangeView(View):
messages.error(request, _('You clicked an invalid link.'))
return HttpResponseRedirect(self.get_success_url())
with transaction.atomic():
customer.email = data['email']
customer.save()
customer.log_action('pretix.customer.changed', {
'email': data['email']
})
try:
with transaction.atomic():
customer.email = data['email']
customer.save()
customer.log_action('pretix.customer.changed', {
'email': data['email']
})
except IntegrityError:
messages.success(request, _('Your email address has not been updated since the address is already in use '
'for another customer account.'))
else:
messages.success(request, _('Your email address has been updated.'))
messages.success(request, _('Your email address has been updated.'))
if customer == request.customer:
update_customer_session_auth_hash(self.request, customer)
if customer == request.customer:
update_customer_session_auth_hash(self.request, customer)
return HttpResponseRedirect(self.get_success_url())
+3 -2
View File
@@ -516,12 +516,13 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
items = [i for i in items if not i.requires_seat]
context['itemnum'] = len(items)
context['allfree'] = all(
item.display_price.gross == Decimal('0.00') for item in items if not item.has_variations
item.display_price.gross == Decimal('0.00') and not item.mandatory_priced_addons
for item in items if not item.has_variations
) and all(
all(
var.display_price.gross == Decimal('0.00')
for var in item.available_variations
)
) and not item.mandatory_priced_addons
for item in items if item.has_variations
)
+1 -2
View File
@@ -735,5 +735,4 @@ FILE_UPLOAD_MAX_SIZE_EMAIL_ATTACHMENT = 1024 * 1024 * config.getint("pretix_file
FILE_UPLOAD_MAX_SIZE_EMAIL_AUTO_ATTACHMENT = 1024 * 1024 * config.getint("pretix_file_upload", "max_size_email_auto_attachment", fallback=1)
FILE_UPLOAD_MAX_SIZE_OTHER = 1024 * 1024 * config.getint("pretix_file_upload", "max_size_other", fallback=10)
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' # sadly. we would prefer BigInt, and should use it for all new models but the migration will be hard
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
@@ -208,7 +208,7 @@ $input-border-radius-small: $border-radius-small !default;
$input-border-focus: #66afe9 !default;
//** Placeholder text color
$input-color-placeholder: #767676 !default;
$input-color-placeholder: #999 !default;
//** Default `.form-control` height
$input-height-base: ($line-height-computed + ($padding-base-vertical * 2) + 2) !default;
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -4,8 +4,8 @@
"private": true,
"scripts": {},
"dependencies": {
"@babel/core": "^7.22.11",
"@babel/preset-env": "^7.22.15",
"@babel/core": "^7.23.0",
"@babel/preset-env": "^7.23.2",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-node-resolve": "^15.2.1",
"vue": "^2.7.14",
@@ -13,6 +13,10 @@ input[type=number]::-webkit-outer-spin-button {
padding: 8px 4px;
}
.form-control::placeholder {
font-style: italic;
}
.sidebar-nav li > a > .fa {
color: $navbar-inverse-bg;
}
@@ -20,6 +20,7 @@ $gray-lightest: lighten(#000, 97.25%);
$font-family-sans-serif: "Open Sans", "OpenSans", "Helvetica Neue", Helvetica, Arial, sans-serif !default;
$text-color: #222222 !default;
$text-muted: #767676 !default;
$input-color-placeholder: lighten(#000, 70%) !default;
$brand-primary: #7f5a91 !default;
$brand-success: #50a167 !default;
@@ -17,6 +17,7 @@ var strings = {
'quantity_dec': django.pgettext('widget', 'Decrease quantity'),
'quantity_inc': django.pgettext('widget', 'Increase quantity'),
'price': django.pgettext('widget', 'Price'),
'select': django.pgettext('widget', 'Select'),
'select_item': django.pgettext('widget', 'Select %s'),
'select_variant': django.pgettext('widget', 'Select variant %s'),
'sold_out': django.pgettext('widget', 'Sold out'),
@@ -214,7 +215,13 @@ Vue.component('availbox', {
+ '<a :href="waiting_list_url" target="_blank" @click="$root.open_link_in_frame">' + strings.waiting_list + '</a>'
+ '</div>'
+ '<div class="pretix-widget-availability-available" v-if="!require_voucher && avail[0] === 100">'
+ '<label class="pretix-widget-item-count-single-label" v-if="order_max === 1">'
+ '<label class="pretix-widget-item-count-single-label pretix-widget-btn-checkbox" v-if="order_max === 1 && $root.single_item_select == \'button\'">'
+ '<input type="checkbox" value="1" :checked="!!amount_selected" @change="amount_selected = $event.target.checked" :name="input_name"'
+ ' v-bind:aria-label="label_select_item"'
+ '>'
+ '<span class="pretix-widget-icon-cart" aria-hidden="true"></span> ' + strings.select
+ '</label>'
+ '<label class="pretix-widget-item-count-single-label" v-else-if="order_max === 1">'
+ '<input type="checkbox" value="1" :checked="!!amount_selected" @change="amount_selected = $event.target.checked" :name="input_name"'
+ ' v-bind:aria-label="label_select_item"'
+ '>'
@@ -1013,7 +1020,7 @@ Vue.component('pretix-widget-event-form', {
break;
}
}
if (item.variations.length === 0 && item.price.gross !== "0.00") {
if ((item.variations.length === 0 && item.price.gross !== "0.00") || item.mandatory_priced_addons) {
all_free = false;
break;
}
@@ -1717,11 +1724,10 @@ var shared_root_methods = {
});
},
startwaiting: function () {
var redirect_url = this.$root.target_url + 'w/' + widget_id;
var redirect_url = this.$root.target_url + 'w/' + widget_id + '/waitinglist/?iframe=1&locale=' + lang;
if (this.$root.subevent){
redirect_url += '/' + this.$root.subevent;
redirect_url += '&subevent=' + this.$root.subevent;
}
redirect_url += '/waitinglist/?iframe=1&locale=' + lang;
if (this.$root.useIframe) {
var iframe = this.$root.overlay.$children[0].$refs['frame-container'].children[0];
this.$root.overlay.frame_loading = true;
@@ -1904,6 +1910,7 @@ var create_widget = function (element) {
var items = element.attributes.items ? element.attributes.items.value : null;
var variations = element.attributes.variations ? element.attributes.variations.value : null;
var categories = element.attributes.categories ? element.attributes.categories.value : null;
var single_item_select = element.getAttribute("single-item-select") || "checkbox";
for (var i = 0; i < element.attributes.length; i++) {
var attrib = element.attributes[i];
if (attrib.name.match(/^data-.*$/)) {
@@ -1950,6 +1957,7 @@ var create_widget = function (element) {
voucher_code: voucher,
display_net_prices: false,
use_native_spinners: false,
single_item_select: single_item_select,
voucher_explanation_text: null,
show_variations_expanded: !!variations,
skip_ssl: skip_ssl,
@@ -69,6 +69,36 @@
@include button-variant($btn-default-color, $btn-default-bg, $btn-default-border);
}
}
label.pretix-widget-btn-checkbox {
@include button-variant($btn-default-color, $btn-default-bg, $btn-default-border);
border-width: 1px;
border-style: solid;
position: relative;
cursor: pointer;
padding: 6px 24px;
min-height: 32px;
box-sizing: border-box;
color: #333;
input {
position: absolute;
left: 10px;
}
&:has(input:checked) {
background-color: #e6e6e6;
border-color: #adadad;
}
}
.pretix-widget-icon-cart {
display: inline-block;
width: 1em;
height: 1em;
vertical-align: text-bottom;
fill: #333;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2.267 6.756c0-.312-.202-.563-.453-.563-.252 0-.454.251-.454.563 0 .312.202.563.454.563.251 0 .453-.251.453-.563Zm3.174 0c0-.312-.202-.563-.454-.563-.251 0-.453.251-.453.563 0 .312.202.563.453.563.252 0 .454-.251.454-.563Zm.453-4.785c0-.154-.103-.282-.227-.282H1.413c-.035-.211-.039-.563-.28-.563H.227c-.124 0-.227.128-.227.282 0 .153.103.281.227.281h.722l.627 3.62c-.049.127-.216.466-.216.603 0 .153.103.281.227.281h3.627c.124 0 .227-.128.227-.281 0-.154-.103-.282-.227-.282H1.955c.036-.088.085-.18.085-.281 0-.102-.032-.212-.046-.308l3.698-.537c.117-.018.202-.141.202-.281V1.971Z' transform='matrix(2.52069 0 0 2.02994 -.035 -.523)'/%3E%3C/svg%3E%0A");
}
input:checked + .pretix-widget-icon-cart {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.534 3.097a.317.317 0 0 1-.067.197L3.56 4.42a.207.207 0 0 1-.16.084.207.207 0 0 1-.159-.084l-.907-1.126a.317.317 0 0 1-.067-.197c0-.154.103-.282.227-.282.06 0 .117.031.159.084l.521.642V2.252c0-.154.102-.281.226-.281.124 0 .227.127.227.281v1.289l.521-.642a.205.205 0 0 1 .159-.084c.124 0 .227.128.227.282ZM2.267 6.756c0-.312-.202-.563-.453-.563-.252 0-.454.251-.454.563 0 .312.202.563.454.563.251 0 .453-.251.453-.563Zm3.174 0c0-.312-.202-.563-.454-.563-.251 0-.453.251-.453.563 0 .312.202.563.453.563.252 0 .454-.251.454-.563Zm.453-4.785c0-.154-.103-.282-.227-.282H1.413c-.035-.211-.039-.563-.28-.563H.227c-.124 0-.227.128-.227.282 0 .153.103.281.227.281h.722l.627 3.62c-.049.127-.216.466-.216.603 0 .153.103.281.227.281h3.627c.124 0 .227-.128.227-.281 0-.154-.103-.282-.227-.282H1.955c.036-.088.085-.18.085-.281 0-.102-.032-.212-.046-.308l3.698-.537c.117-.018.202-.141.202-.281V1.971Z' transform='matrix(2.52069 0 0 2.02994 -.035 -.523)'/%3E%3C/svg%3E%0A");
}
input[type="text"], input[type="number"] {
line-height: normal;
border: 1px solid $input-border;
+4 -2
View File
@@ -38,7 +38,7 @@ from django.views.generic import RedirectView
import pretix.control.urls
import pretix.presale.urls
from pretix.base.views import js_helpers
from pretix.base.views import applepay, js_helpers
from .base.views import (
cachedfiles, csp, health, js_catalog, metrics, redirect, source,
@@ -57,7 +57,9 @@ base_patterns = [
re_path(r'^agpl_source$', source.get_source, name='source'),
re_path(r'^js_helpers/states/$', js_helpers.states, name='js_helpers.states'),
re_path(r'^api/v1/', include(('pretix.api.urls', 'pretixapi'), namespace='api-v1')),
re_path(r'^api/$', RedirectView.as_view(url='/api/v1/'), name='redirect-api-version')
re_path(r'^api/$', RedirectView.as_view(url='/api/v1/'), name='redirect-api-version'),
re_path(r'^.well-known/apple-developer-merchantid-domain-association$',
applepay.association, name='applepay.association'),
]
control_patterns = [
+44
View File
@@ -1092,6 +1092,50 @@ def test_create_multiple_vouchers_duplicate_code(token_client, organizer, event,
assert Voucher.objects.count() == 0
@pytest.mark.django_db
def test_create_multiple_vouchers_autogenerate_codes(token_client, organizer, event, item):
resp = token_client.post(
'/api/v1/organizers/{}/events/{}/vouchers/batch_create/'.format(organizer.slug, event.slug),
data=[
{
'max_usages': 1,
'valid_until': None,
'block_quota': False,
'allow_ignore_quota': False,
'price_mode': 'set',
'value': '12.00',
'item': item.pk,
'variation': None,
'quota': None,
'tag': 'Foo',
'comment': '',
'subevent': None
},
{
'max_usages': 1,
'valid_until': None,
'block_quota': True,
'allow_ignore_quota': False,
'price_mode': 'set',
'value': '12.00',
'item': item.pk,
'variation': None,
'quota': None,
'tag': 'Foo',
'comment': '',
'subevent': None
}
], format='json'
)
assert resp.status_code == 201
with scopes_disabled():
assert Voucher.objects.count() == 2
v1 = Voucher.objects.get(code=resp.data[0]['code'])
assert not v1.block_quota
v2 = Voucher.objects.get(code=resp.data[1]['code'])
assert v2.block_quota
@pytest.fixture
def seatingplan(organizer, event):
plan = SeatingPlan.objects.create(
+1
View File
@@ -3733,6 +3733,7 @@ def test_issue_when_paid_and_changed(event):
op.refresh_from_db()
assert op.secret == gc1.secret
order.refresh_from_db()
ocm = OrderChangeManager(order)
ocm.add_position(ticket, None, Decimal('12.00'))
ocm.commit()
@@ -0,0 +1,189 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import asyncio
from datetime import timedelta
from importlib import import_module
import pytest
from asgiref.sync import sync_to_async
from django.conf import settings
from django.utils.timezone import now
from django_scopes import scopes_disabled
from tests.concurrency_tests.utils import post
from pretix.base.models import CartPosition, OrderPosition
SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
@pytest.fixture
@scopes_disabled()
def cart1_expired(event, organizer, item, customer):
cp = CartPosition.objects.create(
event=event,
item=item,
datetime=now(),
expires=now() - timedelta(days=1),
price=item.default_price,
cart_id="cart1"
)
session = SessionStore("cart1")
session['current_cart_event_{}'.format(event.pk)] = "cart1"
session['carts'] = {
'cart1': {
'payment': 'banktransfer',
'email': 'admin@localhost',
'customer': customer.pk,
}
}
session[f'customer_auth_id:{event.organizer.pk}'] = customer.pk
session[f'customer_auth_hash:{event.organizer.pk}'] = customer.get_session_auth_hash()
session.save()
return cp, session
@pytest.fixture
@scopes_disabled()
def cart2_expired(event, organizer, item, customer):
cp = CartPosition.objects.create(
event=event,
item=item,
datetime=now(),
expires=now() - timedelta(days=1),
price=item.default_price,
cart_id="cart2"
)
session = SessionStore("cart2")
session['current_cart_event_{}'.format(event.pk)] = "cart2"
session['carts'] = {
'cart2': {
'payment': 'banktransfer',
'email': 'admin@localhost',
'customer': customer.pk,
}
}
session[f'customer_auth_id:{event.organizer.pk}'] = customer.pk
session[f'customer_auth_hash:{event.organizer.pk}'] = customer.get_session_auth_hash()
session.save()
return cp, session
@pytest.mark.asyncio
async def test_quota_race_condition_happens_if_we_disable_locks(live_server, session, event, item, quota,
cart1_expired, cart2_expired):
# This test exists to ensure that our test setup makes sense. If it fails, all tests down below
# might be useless.
quota.size = 1
await sync_to_async(quota.save)()
url = f"/{event.organizer.slug}/{event.slug}/checkout/confirm/?_debug_flag=skip-csrf&_debug_flag=skip-locking&_debug_flag=sleep-after-quota-check"
payload = {}
r1, r2 = await asyncio.gather(
post(session, f"{live_server}{url}", data=payload, cookies={settings.SESSION_COOKIE_NAME: cart1_expired[1].session_key}),
post(session, f"{live_server}{url}", data=payload, cookies={settings.SESSION_COOKIE_NAME: cart2_expired[1].session_key}),
)
assert ['thank-you' in r1, 'thank-you' in r2].count(True) == 2
with scopes_disabled():
assert await sync_to_async(CartPosition.objects.filter(item=item).count)() == 0
assert await sync_to_async(OrderPosition.objects.filter(item=item).count)() == 2
@pytest.mark.asyncio
async def test_quota_race_condition_prevented_by_locks(live_server, session, event, item, quota, cart1_expired, cart2_expired):
quota.size = 1
await sync_to_async(quota.save)()
url = f"/{event.organizer.slug}/{event.slug}/checkout/confirm/?_debug_flag=skip-csrf&_debug_flag=sleep-after-quota-check"
payload = {}
r1, r2 = await asyncio.gather(
post(session, f"{live_server}{url}", data=payload, cookies={settings.SESSION_COOKIE_NAME: cart1_expired[1].session_key}),
post(session, f"{live_server}{url}", data=payload, cookies={settings.SESSION_COOKIE_NAME: cart2_expired[1].session_key}),
)
assert ['thank-you' in r1, 'thank-you' in r2].count(True) == 1
with scopes_disabled():
assert await sync_to_async(OrderPosition.objects.filter(item=item).count)() == 1
assert await sync_to_async(CartPosition.objects.filter(item=item).count)() == 0
@pytest.mark.asyncio
async def test_voucher_race_condition_prevented_by_locks(live_server, session, event, item, quota, cart1_expired, cart2_expired, voucher):
cart1_expired[0].voucher = voucher
await sync_to_async(cart1_expired[0].save)()
cart2_expired[0].voucher = voucher
await sync_to_async(cart2_expired[0].save)()
url = f"/{event.organizer.slug}/{event.slug}/checkout/confirm/?_debug_flag=skip-csrf&_debug_flag=sleep-after-quota-check"
payload = {}
r1, r2 = await asyncio.gather(
post(session, f"{live_server}{url}", data=payload, cookies={settings.SESSION_COOKIE_NAME: cart1_expired[1].session_key}),
post(session, f"{live_server}{url}", data=payload, cookies={settings.SESSION_COOKIE_NAME: cart2_expired[1].session_key}),
)
assert ['thank-you' in r1, 'thank-you' in r2].count(True) == 1
with scopes_disabled():
assert await sync_to_async(OrderPosition.objects.filter(item=item, voucher=voucher).count)() == 1
assert await sync_to_async(CartPosition.objects.filter(item=item, voucher=voucher).count)() == 0
@pytest.mark.asyncio
async def test_seat_race_condition_prevented_by_locks(live_server, session, event, item, quota, cart1_expired, cart2_expired, seat):
cart1_expired[0].seat = seat
await sync_to_async(cart1_expired[0].save)()
cart2_expired[0].seat = seat
await sync_to_async(cart2_expired[0].save)()
url = f"/{event.organizer.slug}/{event.slug}/checkout/confirm/?_debug_flag=skip-csrf&_debug_flag=sleep-after-quota-check"
payload = {}
r1, r2 = await asyncio.gather(
post(session, f"{live_server}{url}", data=payload, cookies={settings.SESSION_COOKIE_NAME: cart1_expired[1].session_key}),
post(session, f"{live_server}{url}", data=payload, cookies={settings.SESSION_COOKIE_NAME: cart2_expired[1].session_key}),
)
assert ['thank-you' in r1, 'thank-you' in r2].count(True) == 1
with scopes_disabled():
assert await sync_to_async(OrderPosition.objects.filter(item=item, seat=seat).count)() == 1
assert await sync_to_async(CartPosition.objects.filter(item=item, seat=seat).count)() == 0
@pytest.mark.asyncio
async def test_membership_race_condition_prevented_by_locks(live_server, session, event, item, quota, cart1_expired, cart2_expired, membership):
cart1_expired[0].used_membership = membership
await sync_to_async(cart1_expired[0].save)()
cart2_expired[0].used_membership = membership
await sync_to_async(cart2_expired[0].save)()
item.require_membership = True
await sync_to_async(item.save)()
await sync_to_async(item.require_membership_types.set)([membership.membership_type])
url = f"/{event.organizer.slug}/{event.slug}/checkout/confirm/?_debug_flag=skip-csrf&_debug_flag=sleep-after-quota-check"
payload = {}
r1, r2 = await asyncio.gather(
post(session, f"{live_server}{url}", data=payload, cookies={settings.SESSION_COOKIE_NAME: cart1_expired[1].session_key}),
post(session, f"{live_server}{url}", data=payload, cookies={settings.SESSION_COOKIE_NAME: cart2_expired[1].session_key}),
)
assert ['thank-you' in r1, 'thank-you' in r2].count(True) == 1
with scopes_disabled():
assert await sync_to_async(OrderPosition.objects.filter(item=item).count)() == 1
assert await sync_to_async(CartPosition.objects.filter(item=item).count)() == 1
+5 -2
View File
@@ -1418,7 +1418,10 @@ class EventIcalDownloadTest(EventTestMixin, SoupTest):
(self.event.settings.timezone,
self.event.date_from.astimezone(ZoneInfo(self.event.settings.timezone)).strftime(fmt)),
ical, 'incorrect start time')
self.assertNotIn('DTEND', ical, 'unexpected end time attribute')
self.assertIn('DTEND;TZID=%s:%s' %
(self.event.settings.timezone,
(self.event.date_from.astimezone(ZoneInfo(self.event.settings.timezone)) + datetime.timedelta(hours=1)).strftime(fmt)),
ical, 'incorrect end time')
def test_no_date_to_and_time(self):
self.event.settings.show_date_to = False
@@ -1426,7 +1429,7 @@ class EventIcalDownloadTest(EventTestMixin, SoupTest):
self.event.save()
ical = self.client.get('/%s/%s/ical/' % (self.orga.slug, self.event.slug)).content.decode()
self.assertIn('DTSTART;VALUE=DATE:%s' % self.event.date_from.strftime('%Y%m%d'), ical, 'incorrect start date')
self.assertNotIn('DTEND', ical, 'unexpected end time attribute')
self.assertIn('DTEND;VALUE=DATE:%s' % (self.event.date_from + datetime.timedelta(days=1)).strftime('%Y%m%d'), ical, 'incorrect start date')
def test_local_date_diff_from_utc(self):
self.event.date_from = datetime.datetime(2013, 12, 26, 21, 57, 58, tzinfo=datetime.timezone.utc)