forked from CGM_Public/pretix_original
Compare commits
43
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59ac56148e | ||
|
|
4d75c568b5 | ||
|
|
abda800953 | ||
|
|
43d26451d1 | ||
|
|
2dd51b6f62 | ||
|
|
9d7d5389dc | ||
|
|
fefa15fedd | ||
|
|
2c905b0b39 | ||
|
|
1c4d64a3b5 | ||
|
|
b920795edf | ||
|
|
270d91080f | ||
|
|
3a26d6173e | ||
|
|
7ee630928b | ||
|
|
215a2f2dbb | ||
|
|
f1a29a7c5b | ||
|
|
e72f6746f2 | ||
|
|
8343b1256b | ||
|
|
d4c541b2bc | ||
|
|
43eff3b65f | ||
|
|
1c1beee5f6 | ||
|
|
dc3fc3cb45 | ||
|
|
f62a200f52 | ||
|
|
49d048fc49 | ||
|
|
fde1c67388 | ||
|
|
c8e818f786 | ||
|
|
5221ce965b | ||
|
|
196eb3482c | ||
|
|
1fe7fa91d5 | ||
|
|
d8ecb43e5d | ||
|
|
0cbb38f3d4 | ||
|
|
ab0e26047e | ||
|
|
079324863b | ||
|
|
ae445c1460 | ||
|
|
6593a64b18 | ||
|
|
b8c448fbd7 | ||
|
|
c1c828d8a2 | ||
|
|
1d0dbae77a | ||
|
|
c6cf8b6f24 | ||
|
|
78fbfc9c80 | ||
|
|
7ead117f2e | ||
|
|
a4417e97fd | ||
|
|
10e9b9e12d | ||
|
|
755c4efba1 |
@@ -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
|
||||
|
||||
@@ -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 |
@@ -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
|
||||
------------------
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -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):
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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
@@ -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'))
|
||||
@@ -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='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> 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><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 %}></div>
|
||||
<pre><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"></div>
|
||||
<noscript>
|
||||
<div class="pretix-widget">
|
||||
<div class="pretix-widget-info-message">
|
||||
@@ -45,7 +45,7 @@
|
||||
</noscript>
|
||||
</pre>
|
||||
{% else %}
|
||||
<pre><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 %}></pretix-widget>
|
||||
<pre><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"></pretix-widget>
|
||||
<noscript>
|
||||
<div class="pretix-widget">
|
||||
<div class="pretix-widget-info-message">
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -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."
|
||||
|
||||
@@ -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 ""
|
||||
|
||||
@@ -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 d’entrées aujourd’hui"
|
||||
|
||||
#: 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 d’entrées aujourd’hui"
|
||||
msgstr "nombre d’entré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 d’entrées aujourd’hui"
|
||||
msgstr "nombre d’entré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 "D’autres 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 l’avez fait, ils doivent aller à la même page dans leur "
|
||||
"compte et accepter votre invitation. Notez que d’autres organisateurs "
|
||||
"pourront également ajouter de l’argent aux cartes-cadeaux que vous devrez "
|
||||
"collecter auprès d’eux. Il est de votre responsabilité de gérer l’échange "
|
||||
"d’argent pour compenser les transactions entre les deux organisateurs."
|
||||
"collecter auprès d’eux. 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 s’est 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
@@ -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)"
|
||||
|
||||
@@ -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
|
||||
),
|
||||
),
|
||||
]
|
||||
-1
File diff suppressed because one or more lines are too long
@@ -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'),
|
||||
]
|
||||
|
||||
@@ -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
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
+310
-326
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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 = [
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user