Compare commits

..

2 Commits

Author SHA1 Message Date
Raphael Michel
5d87f9a26f Bump to 2025.10.1 2025-12-19 13:06:58 +01:00
Raphael Michel
4b5651862c [SECURITY] Prevent access to arbitrary cached files by UUID (CVE-2025-14881) 2025-12-19 13:06:48 +01:00
133 changed files with 2467 additions and 5099 deletions

View File

@@ -211,7 +211,7 @@ The line-based computation has a few significant advantages:
The main disadvantage is that the tax looks "wrong" when computed from the sum. Taking the sum of net prices (420.15)
and multiplying it with the tax rate (19%) yields a tax amount of 79.83 (instead of 79.85) and a gross sum of 499.98
(instead of 500.00). This becomes a problem when juristictions, data formats, or external systems expect this calculation
(instead of 499.98). This becomes a problem when juristictions, data formats, or external systems expect this calculation
to work on the level of the entire order. A prominent example is the EN 16931 standard for e-invoicing that
does not allow the computation as created by pretix.

View File

@@ -30,12 +30,11 @@ dependencies = [
"babel",
"BeautifulSoup4==4.14.*",
"bleach==6.2.*",
"celery==5.6.*",
"celery==5.5.*",
"chardet==5.2.*",
"cryptography>=44.0.0",
"css-inline==0.18.*",
"defusedcsv>=1.1.0",
"dnspython==2.*",
"Django[argon2]==4.2.*,>=4.2.26",
"django-bootstrap3==25.2",
"django-compressor==4.5.1",
@@ -62,7 +61,7 @@ dependencies = [
"importlib_metadata==8.*", # Polyfill, we can probably drop this once we require Python 3.10+
"isoweek",
"jsonschema",
"kombu==5.6.*",
"kombu==5.5.*",
"libsass==0.23.*",
"lxml",
"markdown==3.9", # 3.3.5 requires importlib-metadata>=4.4, but django-bootstrap3 requires importlib-metadata<3.
@@ -82,7 +81,7 @@ dependencies = [
"pycountry",
"pycparser==2.23",
"pycryptodome==3.23.*",
"pypdf==6.4.*",
"pypdf==6.3.*",
"python-bidi==0.6.*", # Support for Arabic in reportlab
"python-dateutil==2.9.*",
"pytz",
@@ -92,13 +91,14 @@ dependencies = [
"redis==6.4.*",
"reportlab==4.4.*",
"requests==2.32.*",
"sentry-sdk==2.47.*",
"sentry-sdk==2.45.*",
"sepaxml==2.7.*",
"stripe==7.9.*",
"text-unidecode==1.*",
"tlds>=2020041600",
"tqdm==4.*",
"ua-parser==1.0.*",
"vat_moss_forked==2020.3.20.0.11.0",
"vobject==0.9.*",
"webauthn==2.7.*",
"zeep==4.3.*"

View File

@@ -19,4 +19,4 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
__version__ = "2025.11.0.dev0"
__version__ = "2025.10.1"

View File

@@ -795,7 +795,6 @@ class EventSettingsSerializer(SettingsSerializer):
'invoice_address_asked',
'invoice_address_required',
'invoice_address_vatid',
'invoice_address_vatid_required_countries',
'invoice_address_company_required',
'invoice_address_beneficiary',
'invoice_address_custom_field',
@@ -944,7 +943,6 @@ class DeviceEventSettingsSerializer(EventSettingsSerializer):
'invoice_address_asked',
'invoice_address_required',
'invoice_address_vatid',
'invoice_address_vatid_required_countries',
'invoice_address_company_required',
'invoice_address_beneficiary',
'invoice_address_custom_field',

View File

@@ -74,6 +74,11 @@ class ExportersMixin:
@action(detail=True, methods=['GET'], url_name='download', url_path='download/(?P<asyncid>[^/]+)/(?P<cfid>[^/]+)')
def download(self, *args, **kwargs):
cf = get_object_or_404(CachedFile, id=kwargs['cfid'])
if not cf.allowed_for_session(self.request, "exporters-api"):
return Response(
{'status': 'failed', 'message': 'Unknown file ID or export failed'},
status=status.HTTP_410_GONE
)
if cf.file:
resp = ChunkBasedFileResponse(cf.file.file, content_type=cf.type)
resp['Content-Disposition'] = 'attachment; filename="{}"'.format(cf.filename).encode("ascii", "ignore")
@@ -109,7 +114,8 @@ class ExportersMixin:
serializer = JobRunSerializer(exporter=instance, data=self.request.data, **self.get_serializer_kwargs())
serializer.is_valid(raise_exception=True)
cf = CachedFile(web_download=False)
cf = CachedFile(web_download=True)
cf.bind_to_session(self.request, "exporters-api")
cf.date = now()
cf.expires = now() + timedelta(hours=24)
cf.save()

View File

@@ -567,7 +567,7 @@ class QuotaViewSet(ConditionalListView, viewsets.ModelViewSet):
write_permission = 'can_change_items'
def get_queryset(self):
return self.request.event.quotas.select_related('subevent').prefetch_related('items', 'variations').all()
return self.request.event.quotas.all()
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset()).distinct()

View File

@@ -721,7 +721,7 @@ class MembershipViewSet(viewsets.ModelViewSet):
def get_queryset(self):
return Membership.objects.filter(
customer__organizer=self.request.organizer
).select_related('customer')
)
def get_serializer_context(self):
ctx = super().get_serializer_context()

View File

@@ -19,7 +19,6 @@
# 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.db import transaction
from django.db.models import F, Q
from django.utils.timezone import now
@@ -65,13 +64,8 @@ class VoucherViewSet(viewsets.ModelViewSet):
permission = 'can_view_vouchers'
write_permission = 'can_change_vouchers'
@scopes_disabled() # we have an event check here, and we can save some performance on subqueries
def get_queryset(self):
return Voucher.annotate_budget_used(
self.request.event.vouchers
).select_related(
'item', 'quota', 'seat', 'variation'
)
return self.request.event.vouchers.select_related('seat').all()
@transaction.atomic()
def create(self, request, *args, **kwargs):

View File

@@ -43,7 +43,6 @@ from pretix.base.services.tasks import ProfiledTask, TransactionAwareTask
from pretix.base.signals import periodic_task
from pretix.celery_app import app
from pretix.helpers import OF_SELF
from pretix.helpers.celery import get_task_priority
logger = logging.getLogger(__name__)
_ALL_EVENTS = None
@@ -475,10 +474,7 @@ def notify_webhooks(logentry_ids: list):
)
for wh in webhooks:
send_webhook.apply_async(
args=(logentry.id, notification_type.action_type, wh.pk),
priority=get_task_priority("notifications", logentry.organizer_id),
)
send_webhook.apply_async(args=(logentry.id, notification_type.action_type, wh.pk))
@app.task(base=ProfiledTask, bind=True, max_retries=5, default_retry_delay=60, acks_late=True, autoretry_for=(DatabaseError,),)

View File

@@ -90,7 +90,6 @@ StaticMapping = namedtuple('StaticMapping', ('id', 'pretix_model', 'external_obj
class OutboundSyncProvider:
max_attempts = 5
list_field_joiner = "," # set to None to keep native lists in properties
def __init__(self, event):
self.event = event
@@ -282,8 +281,7 @@ class OutboundSyncProvider:
'Please update value mapping for field "{field_name}" - option "{val}" not assigned'
).format(field_name=key, val=val)])
if self.list_field_joiner:
val = self.list_field_joiner.join(val)
val = ",".join(val)
return val
def get_properties(self, inputs: dict, property_mappings: List[dict]):

View File

@@ -71,20 +71,15 @@ def assign_properties(
return out
def _add_to_list(out, field_name, current_value, new_item_input, list_sep):
def _add_to_list(out, field_name, current_value, new_item, list_sep):
new_item = str(new_item)
if list_sep is not None:
new_items = str(new_item_input).split(list_sep)
new_item = new_item.replace(list_sep, "")
current_value = current_value.split(list_sep) if current_value else []
else:
new_items = [str(new_item_input)]
if not isinstance(current_value, (list, tuple)):
current_value = [str(current_value)]
new_list = list(current_value)
for new_item in new_items:
if new_item not in current_value:
new_list.append(new_item)
if new_list != current_value:
elif not isinstance(current_value, (list, tuple)):
current_value = [str(current_value)]
if new_item not in current_value:
new_list = current_value + [new_item]
if list_sep is not None:
new_list = list_sep.join(new_list)
out[field_name] = new_list

View File

@@ -83,7 +83,7 @@ from pretix.base.invoicing.transmission import (
from pretix.base.models import InvoiceAddress, Item, Question, QuestionOption
from pretix.base.models.tax import ask_for_vat_id
from pretix.base.services.tax import (
VATIDFinalError, VATIDTemporaryError, normalize_vat_id, validate_vat_id,
VATIDFinalError, VATIDTemporaryError, validate_vat_id,
)
from pretix.base.settings import (
COUNTRIES_WITH_STATE_IN_ADDRESS, COUNTRY_STATE_LABEL,
@@ -1165,11 +1165,13 @@ class BaseInvoiceAddressForm(forms.ModelForm):
self.fields['vat_id'].help_text = '<br/>'.join([
str(_('Optional, but depending on the country you reside in we might need to charge you '
'additional taxes if you do not enter it.')),
str(_('If you are registered in Switzerland, you can enter your UID instead.')),
])
else:
self.fields['vat_id'].help_text = '<br/>'.join([
str(_('Optional, but it might be required for you to claim tax benefits on your invoice '
'depending on your and the sellers country of residence.')),
str(_('If you are registered in Switzerland, you can enter your UID instead.')),
])
transmission_type_choices = [
@@ -1356,24 +1358,13 @@ class BaseInvoiceAddressForm(forms.ModelForm):
"transmission method.")}
)
vat_id_applicable = (
'vat_id' in self.fields and
data.get('is_business') and
ask_for_vat_id(data.get('country'))
)
vat_id_required = vat_id_applicable and str(data.get('country')) in self.event.settings.invoice_address_vatid_required_countries
if vat_id_required and not data.get('vat_id'):
raise ValidationError({
"vat_id": _("This field is required.")
})
if self.validate_vat_id and self.instance.vat_id_validated and 'vat_id' not in self.changed_data:
pass # Skip re-validation if it is validated
elif self.validate_vat_id and vat_id_applicable:
pass
elif self.validate_vat_id and data.get('is_business') and ask_for_vat_id(data.get('country')) and data.get('vat_id'):
try:
normalized_id = validate_vat_id(data.get('vat_id'), str(data.get('country')))
self.instance.vat_id_validated = True
self.instance.vat_id = data['vat_id'] = normalized_id
self.instance.vat_id = normalized_id
except VATIDFinalError as e:
if self.all_optional:
self.instance.vat_id_validated = False
@@ -1381,9 +1372,6 @@ class BaseInvoiceAddressForm(forms.ModelForm):
else:
raise ValidationError({"vat_id": e.message})
except VATIDTemporaryError as e:
# We couldn't check it online, but we can still normalize it
normalized_id = normalize_vat_id(data.get('vat_id'), str(data.get('country')))
self.instance.vat_id = data['vat_id'] = normalized_id
self.instance.vat_id_validated = False
if self.request and self.vat_warning:
messages.warning(self.request, e.message)

View File

@@ -32,6 +32,7 @@ from itertools import groupby
from typing import Tuple
import bleach
import vat_moss.exchange_rates
from bidi import get_display
from django.contrib.staticfiles import finders
from django.db.models import Sum
@@ -1058,7 +1059,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
def fmt(val):
try:
return money_filter(val, self.invoice.foreign_currency_display)
return vat_moss.exchange_rates.format(val, self.invoice.foreign_currency_display)
except ValueError:
return localize(val) + ' ' + self.invoice.foreign_currency_display

View File

@@ -19,11 +19,8 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import base64
import hashlib
import re
import dns.resolver
from django import forms
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _, pgettext
@@ -126,9 +123,6 @@ class PeppolIdValidator:
"9959": ".*",
}
def __init__(self, validate_online=False):
self.validate_online = validate_online
def __call__(self, value):
if ":" not in value:
raise ValidationError(_("A Peppol participant ID always starts with a prefix, followed by a colon (:)."))
@@ -142,28 +136,6 @@ class PeppolIdValidator:
raise ValidationError(_("The Peppol participant ID does not match the validation rules for the prefix "
"%(number)s. Please reach out to us if you are sure this ID is correct."),
params={"number": prefix})
if self.validate_online:
base_hostnames = ['edelivery.tech.ec.europa.eu', 'acc.edelivery.tech.ec.europa.eu']
smp_id = base64.b32encode(hashlib.sha256(value.lower().encode()).digest()).decode().rstrip("=")
for base_hostname in base_hostnames:
smp_domain = f'{smp_id}.iso6523-actorid-upis.{base_hostname}'
resolver = dns.resolver.Resolver()
try:
answers = resolver.resolve(smp_domain, 'NAPTR', lifetime=1.0)
if answers:
return value
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
# ID not registered, do not set found=True
pass
except Exception: # noqa
# Error likely on our end or infrastructure is down, allow user to proceed
return value
raise ValidationError(
_("The Peppol participant ID is not registered on the Peppol network."),
)
return value
@@ -183,9 +155,7 @@ class PeppolTransmissionType(TransmissionType):
"transmission_peppol_participant_id": forms.CharField(
label=_("Peppol participant ID"),
validators=[
PeppolIdValidator(
validate_online=True,
),
PeppolIdValidator(),
]
),
}

View File

@@ -47,19 +47,6 @@ class DataImportError(LazyLocaleException):
super().__init__(msg)
def rename_duplicates(values):
used = set()
had_duplicates = False
for i, value in enumerate(values):
c = 0
while values[i] in used:
c += 1
values[i] = f'{value}__{c}'
had_duplicates = True
used.add(values[i])
return had_duplicates
def parse_csv(file, length=None, mode="strict", charset=None):
file.seek(0)
data = file.read(length)
@@ -83,7 +70,6 @@ def parse_csv(file, length=None, mode="strict", charset=None):
return None
reader = csv.DictReader(io.StringIO(data), dialect=dialect)
reader._had_duplicates = rename_duplicates(reader.fieldnames)
return reader

View File

@@ -53,6 +53,7 @@ from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from django_otp.models import Device
from django_scopes import scopes_disabled
from webauthn.helpers.structs import PublicKeyCredentialDescriptor
from pretix.base.i18n import language
from pretix.helpers.urls import build_absolute_uri
@@ -707,8 +708,6 @@ class U2FDevice(Device):
@property
def webauthndevice(self):
from webauthn.helpers.structs import PublicKeyCredentialDescriptor
d = json.loads(self.json_data)
return PublicKeyCredentialDescriptor(websafe_decode(d['keyHandle']))
@@ -738,8 +737,6 @@ class WebAuthnDevice(Device):
@property
def webauthndevice(self):
from webauthn.helpers.structs import PublicKeyCredentialDescriptor
return PublicKeyCredentialDescriptor(websafe_decode(self.credential_id))
@property

View File

@@ -31,7 +31,6 @@ from django.urls import reverse
from django.utils.crypto import get_random_string
from django.utils.functional import cached_property
from pretix.helpers.celery import get_task_priority
from pretix.helpers.json import CustomJSONEncoder
@@ -59,6 +58,37 @@ class CachedFile(models.Model):
web_download = models.BooleanField(default=True) # allow web download, True for backwards compatibility in plugins
session_key = models.TextField(null=True, blank=True) # only allow download in this session
def session_key_for_request(self, request, salt=None):
from ...api.models import OAuthAccessToken, OAuthApplication
from .devices import Device
from .organizer import TeamAPIToken
if hasattr(request, "auth") and isinstance(request.auth, OAuthAccessToken):
k = f'app:{request.auth.application.pk}'
elif hasattr(request, "auth") and isinstance(request.auth, OAuthApplication):
k = f'app:{request.auth.pk}'
elif hasattr(request, "auth") and isinstance(request.auth, TeamAPIToken):
k = f'token:{request.auth.pk}'
elif hasattr(request, "auth") and isinstance(request.auth, Device):
k = f'device:{request.auth.pk}'
elif request.session.session_key:
k = request.session.session_key
else:
raise ValueError("No auth method found to bind to")
if salt:
k = f"{k}!{salt}"
return k
def allowed_for_session(self, request, salt=None):
return (
not self.session_key or
self.session_key_for_request(request, salt) == self.session_key
)
def bind_to_session(self, request, salt=None):
self.session_key = self.session_key_for_request(request, salt)
@receiver(post_delete, sender=CachedFile)
def cached_file_delete(sender, instance, **kwargs):
@@ -132,15 +162,9 @@ class LoggingMixin:
logentry.save()
if logentry.notification_type:
notify.apply_async(
args=(logentry.pk,),
priority=get_task_priority("notifications", logentry.organizer_id),
)
notify.apply_async(args=(logentry.pk,))
if logentry.webhook_type:
notify_webhooks.apply_async(
args=(logentry.pk,),
priority=get_task_priority("notifications", logentry.organizer_id),
)
notify_webhooks.apply_async(args=(logentry.pk,))
return logentry

View File

@@ -35,14 +35,11 @@
import json
import logging
from django.conf import settings
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import connections, models
from django.utils.functional import cached_property
from pretix.helpers.celery import get_task_priority
class VisibleOnlyManager(models.Manager):
def get_queryset(self):
@@ -189,19 +186,7 @@ class LogEntry(models.Model):
to_notify = [o.id for o in objects if o.notification_type]
if to_notify:
organizer_ids = set(o.organizer_id for o in objects if o.notification_type)
notify.apply_async(
args=(to_notify,),
priority=settings.PRIORITY_CELERY_HIGHEST_FUNC(
get_task_priority("notifications", oid) for oid in organizer_ids
),
)
notify.apply_async(args=(to_notify,))
to_wh = [o.id for o in objects if o.webhook_type]
if to_wh:
organizer_ids = set(o.organizer_id for o in objects if o.webhook_type)
notify_webhooks.apply_async(
args=(to_wh,),
priority=settings.PRIORITY_CELERY_HIGHEST_FUNC(
get_task_priority("notifications", oid) for oid in organizer_ids
),
)
notify_webhooks.apply_async(args=(to_wh,))

View File

@@ -3526,30 +3526,11 @@ class InvoiceAddress(models.Model):
})
return d
def describe_transmission(self, event=None):
# we only need an explicit event if the order is not yet created and we do not want to show unnecessary data to customers
def describe_transmission(self):
from pretix.base.invoicing.transmission import transmission_types
data = []
t, m = transmission_types.get(identifier=self.transmission_type)
d_event = self.order.event if self.order else event
# reusing hack from questions.py for default transmission -- this should at least be fast
if d_event:
if (
t.identifier == "email" and
m in ("transmission_email_other", "transmission_email_address") and
(
d_event.settings.invoice_generate == "False" or
not d_event.settings.invoice_email_attachment
)
):
return data
# we also don't show transmission data if we never showed the corresponding form fields
if not t.invoice_address_form_fields_visible(country=self.country, is_business=self.is_business):
return data
t, __ = transmission_types.get(identifier=self.transmission_type)
data.append((_("Transmission type"), t.public_name))
form_data = t.transmission_info_to_form_data(self.transmission_info or {})
for k, f in t.invoice_address_form_fields.items():

View File

@@ -22,6 +22,7 @@
import json
from collections import namedtuple
import jsonschema
from django.contrib.staticfiles import finders
from django.core.exceptions import ValidationError
from django.db import models
@@ -37,8 +38,6 @@ from pretix.base.models import Event, Item, LoggedModel, Organizer, SubEvent
@deconstructible
class SeatingPlanLayoutValidator:
def __call__(self, value):
import jsonschema
if not isinstance(value, dict):
try:
val = json.loads(value)

View File

@@ -23,6 +23,7 @@ import json
from decimal import Decimal
from typing import Optional
import jsonschema
from django.contrib.staticfiles import finders
from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator
@@ -297,8 +298,6 @@ def cc_to_vat_prefix(country_code):
@deconstructible
class CustomRulesValidator:
def __call__(self, value):
import jsonschema
if not isinstance(value, dict):
try:
val = json.loads(value)

View File

@@ -623,7 +623,7 @@ class Voucher(LoggedModel):
return max(1, self.min_usages - self.redeemed)
@classmethod
def annotate_budget_used(cls, qs):
def annotate_budget_used_orders(cls, qs):
opq = OrderPosition.objects.filter(
voucher_id=OuterRef('pk'),
voucher_budget_use__isnull=False,
@@ -632,7 +632,7 @@ class Voucher(LoggedModel):
Order.STATUS_PENDING
]
).order_by().values('voucher_id').annotate(s=Sum('voucher_budget_use')).values('s')
return qs.annotate(budget_used=Coalesce(Subquery(opq, output_field=models.DecimalField(max_digits=13, decimal_places=2)), Decimal('0.00')))
return qs.annotate(budget_used_orders=Coalesce(Subquery(opq, output_field=models.DecimalField(max_digits=13, decimal_places=2)), Decimal('0.00')))
def budget_used(self):
ops = OrderPosition.objects.filter(

View File

@@ -47,6 +47,7 @@ from collections import OrderedDict, defaultdict
from functools import partial
from io import BytesIO
import jsonschema
import pypdf
import pypdf.generic
import reportlab.rl_config
@@ -1310,8 +1311,6 @@ def _correct_page_media_box(page: pypdf.PageObject):
@deconstructible
class PdfLayoutValidator:
def __call__(self, value):
import jsonschema
if not isinstance(value, dict):
try:
val = json.loads(value)

View File

@@ -1361,11 +1361,6 @@ class CartManager:
deleted_positions.add(op.position.pk)
addons.delete()
op.position.delete()
if op.position.is_bundled:
deleted_positions |= {a.pk for a in op.position.addon_to.addons.all()}
deleted_positions.add(op.position.addon_to.pk)
op.position.addon_to.addons.all().delete()
op.position.addon_to.delete()
else:
raise AssertionError("ExtendOperation cannot affect more than one item")
elif isinstance(op, self.VoucherOperation):

View File

@@ -47,6 +47,7 @@ from urllib.parse import urljoin, urlparse
from zoneinfo import ZoneInfo
import requests
from bs4 import BeautifulSoup
from celery import chain
from celery.exceptions import MaxRetriesExceededError
from django.conf import settings
@@ -763,8 +764,6 @@ def render_mail(template, context, placeholder_mode=SafeFormatter.MODE_RICH_TO_P
def replace_images_with_cid_paths(body_html):
from bs4 import BeautifulSoup
if body_html:
email = BeautifulSoup(body_html, "lxml")
cid_images = []

View File

@@ -32,7 +32,6 @@ from pretix.base.services.mail import mail_send_task
from pretix.base.services.tasks import ProfiledTask, TransactionAwareTask
from pretix.base.signals import notification
from pretix.celery_app import app
from pretix.helpers.celery import get_task_priority
from pretix.helpers.urls import build_absolute_uri
@@ -89,18 +88,12 @@ def notify(logentry_ids: list):
for um, enabled in notify_specific.items():
user, method = um
if enabled:
send_notification.apply_async(
args=(logentry.id, notification_type.action_type, user.pk, method),
priority=get_task_priority("notifications", logentry.organizer_id),
)
send_notification.apply_async(args=(logentry.id, notification_type.action_type, user.pk, method))
for um, enabled in notify_global.items():
user, method = um
if enabled and um not in notify_specific:
send_notification.apply_async(
args=(logentry.id, notification_type.action_type, user.pk, method),
priority=get_task_priority("notifications", logentry.organizer_id),
)
send_notification.apply_async(args=(logentry.id, notification_type.action_type, user.pk, method))
notification.send(logentry.event, logentry_id=logentry.id, notification_type=notification_type.action_type)

View File

@@ -27,6 +27,7 @@ from decimal import Decimal
from xml.etree import ElementTree
import requests
import vat_moss.id
from django.conf import settings
from django.utils.translation import gettext_lazy as _
from zeep import Client, Transport
@@ -41,142 +42,14 @@ logger = logging.getLogger(__name__)
error_messages = {
'unavailable': _(
'Your VAT ID could not be checked, as the VAT checking service of '
'your country is currently not available. We will therefore need to '
'charge you the same tax rate as if you did not enter a VAT ID.'
'your country is currently not available. We will therefore '
'need to charge VAT on your invoice. You can get the tax amount '
'back via the VAT reimbursement process.'
),
'invalid': _('This VAT ID is not valid. Please re-check your input.'),
'country_mismatch': _('Your VAT ID does not match the selected country.'),
}
VAT_ID_PATTERNS = {
# Patterns generated by consulting the following URLs:
#
# - http://en.wikipedia.org/wiki/VAT_identification_number
# - http://ec.europa.eu/taxation_customs/vies/faq.html
# - https://euipo.europa.eu/tunnel-web/secure/webdav/guest/document_library/Documents/COSME/VAT%20numbers%20EU.pdf
# - http://www.skatteetaten.no/en/International-pages/Felles-innhold-benyttes-i-flere-malgrupper/Brochure/Guide-to-value-added-tax-in-Norway/?chapter=7159
'AT': { # Austria
'regex': '^U\\d{8}$',
'country_code': 'AT'
},
'BE': { # Belgium
'regex': '^(1|0?)\\d{9}$',
'country_code': 'BE'
},
'BG': { # Bulgaria
'regex': '^\\d{9,10}$',
'country_code': 'BG'
},
'CH': { # Switzerland
'regex': '^\\dE{9}$',
'country_code': 'CH'
},
'CY': { # Cyprus
'regex': '^\\d{8}[A-Z]$',
'country_code': 'CY'
},
'CZ': { # Czech Republic
'regex': '^\\d{8,10}$',
'country_code': 'CZ'
},
'DE': { # Germany
'regex': '^\\d{9}$',
'country_code': 'DE'
},
'DK': { # Denmark
'regex': '^\\d{8}$',
'country_code': 'DK'
},
'EE': { # Estonia
'regex': '^\\d{9}$',
'country_code': 'EE'
},
'EL': { # Greece
'regex': '^\\d{9}$',
'country_code': 'GR'
},
'ES': { # Spain
'regex': '^[A-Z0-9]\\d{7}[A-Z0-9]$',
'country_code': 'ES'
},
'FI': { # Finland
'regex': '^\\d{8}$',
'country_code': 'FI'
},
'FR': { # France
'regex': '^[A-Z0-9]{2}\\d{9}$',
'country_code': 'FR'
},
'GB': { # United Kingdom
'regex': '^(GD\\d{3}|HA\\d{3}|\\d{9}|\\d{12})$',
'country_code': 'GB'
},
'HR': { # Croatia
'regex': '^\\d{11}$',
'country_code': 'HR'
},
'HU': { # Hungary
'regex': '^\\d{8}$',
'country_code': 'HU'
},
'IE': { # Ireland
'regex': '^(\\d{7}[A-Z]{1,2}|\\d[A-Z+*]\\d{5}[A-Z])$',
'country_code': 'IE'
},
'IT': { # Italy
'regex': '^\\d{11}$',
'country_code': 'IT'
},
'LT': { # Lithuania
'regex': '^(\\d{9}|\\d{12})$',
'country_code': 'LT'
},
'LU': { # Luxembourg
'regex': '^\\d{8}$',
'country_code': 'LU'
},
'LV': { # Latvia
'regex': '^\\d{11}$',
'country_code': 'LV'
},
'MT': { # Malta
'regex': '^\\d{8}$',
'country_code': 'MT'
},
'NL': { # Netherlands
'regex': '^\\d{9}B\\d{2}$',
'country_code': 'NL'
},
'NO': { # Norway
'regex': '^\\d{9}MVA$',
'country_code': 'NO'
},
'PL': { # Poland
'regex': '^\\d{10}$',
'country_code': 'PL'
},
'PT': { # Portugal
'regex': '^\\d{9}$',
'country_code': 'PT'
},
'RO': { # Romania
'regex': '^\\d{2,10}$',
'country_code': 'RO'
},
'SE': { # Sweden
'regex': '^\\d{12}$',
'country_code': 'SE'
},
'SI': { # Slovenia
'regex': '^\\d{8}$',
'country_code': 'SI'
},
'SK': { # Slovakia
'regex': '^\\d{10}$',
'country_code': 'SK'
},
}
class VATIDError(Exception):
def __init__(self, message):
@@ -191,57 +64,13 @@ class VATIDTemporaryError(VATIDError):
pass
def normalize_vat_id(vat_id, country_code):
"""
Accepts a VAT ID and normaizes it, getting rid of spaces, periods, dashes
etc and converting it to upper case.
Original function from https://github.com/wbond/vat_moss-python
Copyright (c) 2015 Will Bond <will@wbond.net>
MIT License
"""
if not vat_id:
return None
if not isinstance(vat_id, str):
raise TypeError('VAT ID is not a string')
if len(vat_id) < 3:
raise ValueError('VAT ID must be at least three character long')
# Normalize the ID for simpler regexes
vat_id = re.sub('\\s+', '', vat_id)
vat_id = vat_id.replace('-', '')
vat_id = vat_id.replace('.', '')
vat_id = vat_id.upper()
# Clean the different shapes a number can take in Switzerland depending on purpse
if country_code == "CH":
vat_id = re.sub('[^A-Z0-9]', '', vat_id.replace('HR', '').replace('MWST', ''))
# Fix people using GR prefix for Greece
if vat_id[0:2] == "GR" and country_code == "GR":
vat_id = "EL" + vat_id[2:]
# Check if we already have a valid country prefix. If not, we try to figure out if we can
# add one, since in some countries (e.g. Italy) it's very custom to enter it without the prefix
if vat_id[:2] in VAT_ID_PATTERNS and re.match(VAT_ID_PATTERNS[vat_id[0:2]]['regex'], vat_id[2:]):
# Prefix set and prefix matches pattern, nothing to do
pass
elif re.match(VAT_ID_PATTERNS[cc_to_vat_prefix(country_code)]['regex'], vat_id):
# Prefix not set but adding it fixes pattern
vat_id = cc_to_vat_prefix(country_code) + vat_id
else:
# We have no idea what this is
pass
return vat_id
def _validate_vat_id_NO(vat_id, country_code):
# Inspired by vat_moss library
if not vat_id.startswith("NO"):
# prefix is not usually used in Norway, but expected by vat_moss library
vat_id = "NO" + vat_id
try:
vat_id = normalize_vat_id(vat_id, country_code)
vat_id = vat_moss.id.normalize(vat_id)
except ValueError:
raise VATIDFinalError(error_messages['invalid'])
@@ -275,7 +104,7 @@ def _validate_vat_id_NO(vat_id, country_code):
def _validate_vat_id_EU(vat_id, country_code):
# Inspired by vat_moss library
try:
vat_id = normalize_vat_id(vat_id, country_code)
vat_id = vat_moss.id.normalize(vat_id)
except ValueError:
raise VATIDFinalError(error_messages['invalid'])
@@ -283,10 +112,11 @@ def _validate_vat_id_EU(vat_id, country_code):
raise VATIDFinalError(error_messages['invalid'])
number = vat_id[2:]
if vat_id[:2] != cc_to_vat_prefix(country_code):
raise VATIDFinalError(error_messages['country_mismatch'])
if not re.match(VAT_ID_PATTERNS[cc_to_vat_prefix(country_code)]['regex'], number):
if not re.match(vat_moss.id.ID_PATTERNS[cc_to_vat_prefix(country_code)]['regex'], number):
raise VATIDFinalError(error_messages['invalid'])
# We are relying on the country code of the normalized VAT-ID and not the user/InvoiceAddress-provided
@@ -345,12 +175,9 @@ def _validate_vat_id_EU(vat_id, country_code):
def _validate_vat_id_CH(vat_id, country_code):
if vat_id[:3] != 'CHE':
raise VATIDFinalError(error_messages['country_mismatch'])
raise VATIDFinalError(_('Your VAT ID does not match the selected country.'))
try:
vat_id = normalize_vat_id(vat_id, country_code)
except ValueError:
raise VATIDFinalError(error_messages['invalid'])
vat_id = re.sub('[^A-Z0-9]', '', vat_id.replace('HR', '').replace('MWST', ''))
try:
transport = Transport(
cache=SqliteCache(os.path.join(settings.CACHE_DIR, "validate_vat_id_ch_zeep_cache.db")),

View File

@@ -113,11 +113,6 @@ def assign_automatically(event: Event, user_id: int=None, subevent_id: int=None)
lock_objects(quotas, shared_lock_objects=[event])
for wle in qs:
# add this event to wle.item as it is not yet cached and is needed in check_quotas
wle.item.event = event
if wle.variation:
wle.variation.item = wle.item
if (wle.item_id, wle.variation_id, wle.subevent_id) in gone:
continue
ev = (wle.subevent or event)

View File

@@ -629,40 +629,13 @@ DEFAULTS = {
'form_kwargs': dict(
label=_("Ask for VAT ID"),
help_text=format_lazy(
_("Only works if an invoice address is asked for. VAT ID is only requested from business customers "
"in the following countries: {countries}."),
_("Only works if an invoice address is asked for. VAT ID is never required and only requested from "
"business customers in the following countries: {countries}"),
countries=lazy(lambda *args: ', '.join(sorted(gettext(Country(cc).name) for cc in VAT_ID_COUNTRIES)), str)()
),
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_invoice_address_asked'}),
)
},
'invoice_address_vatid_required_countries': {
'default': ['IT', 'GR'],
'type': list,
'form_class': forms.MultipleChoiceField,
'serializer_class': serializers.MultipleChoiceField,
'serializer_kwargs': dict(
choices=lazy(
lambda *args: sorted([(cc, gettext(Country(cc).name)) for cc in VAT_ID_COUNTRIES], key=lambda c: c[1]),
list
)(),
),
'form_kwargs': dict(
label=_("Require VAT ID in"),
choices=lazy(
lambda *args: sorted([(cc, gettext(Country(cc).name)) for cc in VAT_ID_COUNTRIES], key=lambda c: c[1]),
list
)(),
help_text=format_lazy(
_("VAT ID is optional by default, because not all businesses are assigned a VAT ID in all countries. "
"VAT ID will be required for all business addresses in the selected countries."),
),
widget=forms.CheckboxSelectMultiple(attrs={
"class": "scrolling-multiple-choice",
'data-display-dependency': '#id_invoice_address_vatid'
}),
)
},
'invoice_address_explanation_text': {
'default': '',
'type': LazyI18nString,

View File

@@ -1,65 +0,0 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-today pretix 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 datetime import datetime
from django import template
from django.utils.html import format_html
from django.utils.timezone import get_current_timezone
from pretix.base.i18n import LazyExpiresDate
from pretix.helpers.templatetags.date_fast import date_fast
register = template.Library()
@register.simple_tag
def html_time(value: datetime, dt_format: str = "SHORT_DATE_FORMAT", **kwargs):
"""
Building a <time datetime='{html-datetime}'>{human-readable datetime}</time> html string,
where the html-datetime as well as the human-readable datetime can be set
to a value from django's FORMAT_SETTINGS or "format_expires".
If attr_fmt isnt provided, it will be set to isoformat.
Usage example:
{% html_time event_start "SHORT_DATETIME_FORMAT" %}
or
{% html_time event_start "TIME_FORMAT" attr_fmt="H:i" %}
"""
if value in (None, ''):
return ''
value = value.astimezone(get_current_timezone())
attr_fmt = kwargs["attr_fmt"] if kwargs else None
try:
if not attr_fmt:
date_html = value.isoformat()
else:
date_html = date_fast(value, attr_fmt)
if dt_format == "format_expires":
date_human = LazyExpiresDate(value)
else:
date_human = date_fast(value, dt_format)
return format_html("<time datetime='{}'>{}</time>", date_html, date_human)
except AttributeError:
return ''

View File

@@ -93,9 +93,7 @@ def timeline_for_event(event, subevent=None):
description=format_lazy(
'{} ({})',
pgettext_lazy('timeline', 'End of ticket sales'),
pgettext_lazy('timeline', 'automatically because the event is over and no end of presale has been configured')
) if not ev.presale_end else (
pgettext_lazy('timeline', 'End of ticket sales')
pgettext_lazy('timeline', 'automatically because the event is over and no end of presale has been configured') if not ev.presale_end else ""
),
edit_url=ev_edit_url + '#id_presale_end_0'
))

View File

@@ -36,9 +36,8 @@ class DownloadView(TemplateView):
def object(self) -> CachedFile:
try:
o = get_object_or_404(CachedFile, id=self.kwargs['id'], web_download=True)
if o.session_key:
if o.session_key != self.request.session.session_key:
raise Http404()
if not o.allowed_for_session(self.request):
raise Http404()
return o
except (ValueError, ValidationError): # Invalid URLs
raise Http404()

View File

@@ -22,7 +22,7 @@
import pycountry
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext, pgettext, pgettext_lazy
from django.utils.translation import pgettext
from django_countries.fields import Country
from django_scopes import scope
@@ -36,22 +36,6 @@ from pretix.base.settings import (
COUNTRIES_WITH_STATE_IN_ADDRESS, COUNTRY_STATE_LABEL,
)
VAT_ID_LABELS = {
# VAT ID is a EU concept and Switzerland has a distinct, but differently-named concept
"CH": pgettext_lazy("tax_id_swiss", "UID"), # Translators: Only translate to French (IDE) and Italien (IDI), otherwise keep the same
# Awareness around VAT IDs differes by EU country. For example, in Germany the VAT ID is assigned
# separately to each company and only used in cross-country transactions. Therefore, it makes sense
# to call it just "VAT ID" on the form, and people will either know their VAT ID or they don't.
# In contrast, in Italy the EU-compatible VAT ID is not separately assigned, but is just "IT" + the national tax
# number (Partita IVA) and also used on domestic transactions. So someone who never purchased something international
# for their company, might still know the value, if we call it the right way and not just "VAT ID".
"IT": pgettext_lazy("tax_id_italy", "VAT ID / P.IVA"), # Translators: Translate to only "P.IVA" in Italian, keep second part as-is in other languages
"GR": pgettext_lazy("tax_id_greece", "VAT ID / TIN"), # Translators: Translate to only "ΑΦΜ" in Greek
"ES": pgettext_lazy("tax_id_spain", "VAT ID / NIF"), # Translators: Translate to only "NIF" in Spanish
"PT": pgettext_lazy("tax_id_portugal", "VAT ID / NIF"), # Translators: Translate to only "NIF" in Portuguese
}
def _info(cc):
info = {
@@ -63,12 +47,7 @@ def _info(cc):
'required': 'if_any' if cc in COUNTRIES_WITH_STATE_IN_ADDRESS else False,
'label': COUNTRY_STATE_LABEL.get(cc, pgettext('address', 'State')),
},
'vat_id': {
'visible': cc in VAT_ID_COUNTRIES,
'required': False,
'label': VAT_ID_LABELS.get(cc, gettext("VAT ID")),
'helptext_visible': True,
},
'vat_id': {'visible': cc in VAT_ID_COUNTRIES, 'required': False},
}
if cc not in COUNTRIES_WITH_STATE_IN_ADDRESS:
return {'data': [], **info}
@@ -145,10 +124,4 @@ def address_form(request):
"required": transmission_type.identifier == selected_transmission_type and k in required
}
if is_business and country in event.settings.invoice_address_vatid_required_countries and info["vat_id"]["visible"]:
info["vat_id"]["required"] = True
if info["vat_id"]["required"]:
# The help text explains that it is optional, so we want to hide that if it is required
info["vat_id"]["helptext_visible"] = False
return JsonResponse(info)

View File

@@ -207,7 +207,6 @@ class EventWizardBasicsForm(I18nModelForm):
'Sample Conference Center\nHeidelberg, Germany'
)
self.fields['slug'].widget.prefix = build_absolute_uri(self.organizer, 'presale:organizer.index')
self.fields['tax_rate']._required = True # Do not render as optional because it is conditionally required
if self.has_subevents:
del self.fields['presale_start']
del self.fields['presale_end']
@@ -928,7 +927,6 @@ class InvoiceSettingsForm(EventSettingsValidationMixin, SettingsForm):
'invoice_address_asked',
'invoice_address_required',
'invoice_address_vatid',
'invoice_address_vatid_required_countries',
'invoice_address_company_required',
'invoice_address_beneficiary',
'invoice_address_custom_field',
@@ -1959,13 +1957,6 @@ class EventFooterLinkForm(I18nModelForm):
class Meta:
model = EventFooterLink
fields = ('label', 'url')
widgets = {
"url": forms.URLInput(
attrs={
"placeholder": "https://..."
}
)
}
class BaseEventFooterLinkFormSet(I18nFormSetMixin, forms.BaseInlineFormSet):

View File

@@ -61,10 +61,6 @@ from pretix.base.models import (
SubEvent, SubEventMetaValue, Team, TeamAPIToken, TeamInvite, Voucher,
)
from pretix.base.signals import register_payment_providers
from pretix.base.timeframes import (
DateFrameField,
resolve_timeframe_to_datetime_start_inclusive_end_exclusive,
)
from pretix.control.forms import SplitDateTimeField
from pretix.control.forms.widgets import Select2, Select2ItemVarQuota
from pretix.control.signals import order_search_filter_q
@@ -1223,129 +1219,6 @@ class OrderPaymentSearchFilterForm(forms.Form):
return qs
class QuestionAnswerFilterForm(forms.Form):
STATUS_VARIANTS = [
("", _("All orders")),
(Order.STATUS_PAID, _("Paid")),
(Order.STATUS_PAID + 'v', _("Paid or confirmed")),
(Order.STATUS_PENDING, _("Pending")),
(Order.STATUS_PENDING + Order.STATUS_PAID, _("Pending or paid")),
("o", _("Pending (overdue)")),
(Order.STATUS_EXPIRED, _("Expired")),
(Order.STATUS_PENDING + Order.STATUS_EXPIRED, _("Pending or expired")),
(Order.STATUS_CANCELED, _("Canceled"))
]
status = forms.ChoiceField(
choices=STATUS_VARIANTS,
required=False,
label=_("Order status"),
)
item = forms.ChoiceField(
choices=[],
required=False,
label=_("Products"),
)
subevent = forms.ModelChoiceField(
queryset=SubEvent.objects.none(),
required=False,
empty_label=pgettext_lazy('subevent', 'All dates'),
label=pgettext_lazy("subevent", "Date"),
)
date_range = DateFrameField(
required=False,
include_future_frames=True,
label=_('Event date'),
)
def __init__(self, *args, **kwargs):
self.event = kwargs.pop('event')
super().__init__(*args, **kwargs)
self.initial['status'] = Order.STATUS_PENDING + Order.STATUS_PAID
choices = [('', _('All products'))]
for i in self.event.items.prefetch_related('variations').all():
variations = list(i.variations.all())
if variations:
choices.append((str(i.pk), _('{product} Any variation').format(product=str(i))))
for v in variations:
choices.append(('%d-%d' % (i.pk, v.pk), '%s %s' % (str(i), v.value)))
else:
choices.append((str(i.pk), str(i)))
self.fields['item'].choices = choices
if self.event.has_subevents:
self.fields["subevent"].queryset = self.event.subevents.all()
self.fields['subevent'].widget = Select2(
attrs={
'data-model-select2': 'event',
'data-select2-url': reverse('control:event.subevents.select2', kwargs={
'event': self.event.slug,
'organizer': self.event.organizer.slug,
}),
'data-placeholder': pgettext_lazy('subevent', 'All dates')
}
)
self.fields['subevent'].widget.choices = self.fields['subevent'].choices
else:
del self.fields['subevent']
def clean(self):
cleaned_data = super().clean()
subevent = cleaned_data.get('subevent')
date_range = cleaned_data.get('date_range')
if subevent is not None and date_range is not None:
d_start, d_end = resolve_timeframe_to_datetime_start_inclusive_end_exclusive(now(), date_range, self.event.timezone)
if (
(d_start and not (d_start <= subevent.date_from)) or
(d_end and not (subevent.date_from < d_end))
):
self.add_error('subevent', pgettext_lazy('subevent', "Date doesn't start in selected date range."))
return cleaned_data
def filter_qs(self, opqs):
fdata = self.cleaned_data
subevent = fdata.get('subevent', None)
date_range = fdata.get('date_range', None)
if subevent is not None:
opqs = opqs.filter(subevent=subevent)
if date_range is not None:
d_start, d_end = resolve_timeframe_to_datetime_start_inclusive_end_exclusive(now(), date_range, self.event.timezone)
opqs = opqs.filter(
subevent__date_from__gte=d_start,
subevent__date_from__lt=d_end
)
s = fdata.get("status", Order.STATUS_PENDING + Order.STATUS_PAID)
if s != "":
if s == Order.STATUS_PENDING:
opqs = opqs.filter(order__status=Order.STATUS_PENDING,
order__expires__lt=now().replace(hour=0, minute=0, second=0))
elif s == Order.STATUS_PENDING + Order.STATUS_PAID:
opqs = opqs.filter(order__status__in=[Order.STATUS_PENDING, Order.STATUS_PAID])
elif s == Order.STATUS_PAID + 'v':
opqs = opqs.filter(
Q(order__status=Order.STATUS_PAID) |
Q(order__status=Order.STATUS_PENDING, order__valid_if_pending=True)
)
elif s == Order.STATUS_PENDING + Order.STATUS_EXPIRED:
opqs = opqs.filter(order__status__in=[Order.STATUS_PENDING, Order.STATUS_EXPIRED])
else:
opqs = opqs.filter(order__status=s)
if s not in (Order.STATUS_CANCELED, ""):
opqs = opqs.filter(canceled=False)
if fdata.get("item", "") != "":
i = fdata.get("item", "")
opqs = opqs.filter(item_id__in=(i,))
return opqs
class SubEventFilterForm(FilterForm):
orders = {
'date_from': 'date_from',

View File

@@ -974,7 +974,7 @@ class EventCancelForm(FormPlaceholderMixin, forms.Form):
self._set_field_placeholders('send_subject', ['event_or_subevent', 'refund_amount', 'position_or_address',
'order', 'event'])
self._set_field_placeholders('send_message', ['event_or_subevent', 'refund_amount', 'position_or_address',
'order', 'event'], rich=True)
'order', 'event'])
self.fields['send_waitinglist_subject'] = I18nFormField(
label=_("Subject"),
required=True,
@@ -998,7 +998,7 @@ class EventCancelForm(FormPlaceholderMixin, forms.Form):
))
)
self._set_field_placeholders('send_waitinglist_subject', ['event_or_subevent', 'event'])
self._set_field_placeholders('send_waitinglist_message', ['event_or_subevent', 'event'], rich=True)
self._set_field_placeholders('send_waitinglist_message', ['event_or_subevent', 'event'])
if self.event.has_subevents:
self.fields['subevent'].queryset = self.event.subevents.all()

View File

@@ -1024,13 +1024,6 @@ class OrganizerFooterLinkForm(I18nModelForm):
class Meta:
model = OrganizerFooterLink
fields = ('label', 'url')
widgets = {
"url": forms.URLInput(
attrs={
"placeholder": "https://..."
}
)
}
class BaseOrganizerFooterLinkFormSet(I18nFormSetMixin, forms.BaseInlineFormSet):

View File

@@ -126,9 +126,7 @@
{% endif %}
<a class="navbar-brand" href="{% url "control:index" %}">
<img src="{% static "pretixbase/img/pretix-icon-white-mini.svg" %}" />
<span>
{{ settings.PRETIX_INSTANCE_NAME }}
</span>
{{ settings.PRETIX_INSTANCE_NAME }}
</a>
</div>
<ul class="nav navbar-nav navbar-top-links navbar-left flip hidden-xs">

View File

@@ -43,7 +43,6 @@
{% bootstrap_field form.invoice_name_required layout="control" %}
{% bootstrap_field form.invoice_address_company_required layout="control" %}
{% bootstrap_field form.invoice_address_vatid layout="control" %}
{% bootstrap_field form.invoice_address_vatid_required_countries layout="control" %}
{% bootstrap_field form.invoice_address_beneficiary layout="control" %}
{% bootstrap_field form.invoice_address_not_asked_free layout="control" %}
{% bootstrap_field form.invoice_address_custom_field layout="control" %}

View File

@@ -20,20 +20,35 @@
</div>
<form class="panel-body filter-form" action="" method="get">
<div class="row">
<div class="col-md-2 col-xs-6">
{% bootstrap_field form.status %}
</div>
<div class="col-md-3 col-xs-6">
{% bootstrap_field form.item %}
</div>
{% if has_subevents %}
<div class="col-md-3 col-xs-6">
{% bootstrap_field form.subevent %}
<div class="col-lg-2 col-sm-6 col-xs-6">
<select name="status" class="form-control">
<option value="" {% if request.GET.status == "" %}selected="selected"{% endif %}>{% trans "All orders" %}</option>
<option value="p" {% if request.GET.status == "p" %}selected="selected"{% endif %}>{% trans "Paid" %}</option>
<option value="pv" {% if request.GET.status == "pv" %}selected="selected"{% endif %}>{% trans "Paid or confirmed" %}</option>
<option value="n" {% if request.GET.status == "n" %}selected="selected"{% endif %}>{% trans "Pending" %}</option>
<option value="np" {% if request.GET.status == "np" or "status" not in request.GET %}selected="selected"{% endif %}>{% trans "Pending or paid" %}</option>
<option value="o" {% if request.GET.status == "o" %}selected="selected"{% endif %}>{% trans "Pending (overdue)" %}</option>
<option value="e" {% if request.GET.status == "e" %}selected="selected"{% endif %}>{% trans "Expired" %}</option>
<option value="ne" {% if request.GET.status == "ne" %}selected="selected"{% endif %}>{% trans "Pending or expired" %}</option>
<option value="c" {% if request.GET.status == "c" %}selected="selected"{% endif %}>{% trans "Canceled" %}</option>
</select>
</div>
<div class="col-md-4 col-xs-6">
{% bootstrap_field form.date_range %}
<div class="col-lg-5 col-sm-6 col-xs-6">
<select name="item" class="form-control">
<option value="">{% trans "All products" %}</option>
{% for item in items %}
<option value="{{ item.id }}"
{% if request.GET.item|add:0 == item.id %}selected="selected"{% endif %}>
{{ item.name }}
</option>
{% endfor %}
</select>
</div>
{% endif %}
{% if request.event.has_subevents %}
<div class="col-lg-5 col-sm-6 col-xs-6">
{% include "pretixcontrol/event/fragment_subevent_choice_simple.html" %}
</div>
{% endif %}
</div>
<div class="text-right">
<button class="btn btn-primary btn-lg" type="submit">

View File

@@ -165,7 +165,7 @@
{% if v.budget|default_if_none:"NONE" != "NONE" %}
<br>
<small class="text-muted">
{{ v.budget_used|money:request.event.currency }} / {{ v.budget|money:request.event.currency }}
{{ v.budget_used_orders|money:request.event.currency }} / {{ v.budget|money:request.event.currency }}
</small>
{% endif %}
</td>

View File

@@ -60,6 +60,7 @@ from pretix.base.models import (
)
from pretix.base.services.quotas import QuotaAvailability
from pretix.base.timeline import timeline_for_event
from pretix.control.forms.event import CommentForm
from pretix.control.signals import (
event_dashboard_widgets, user_dashboard_widgets,
)
@@ -340,8 +341,6 @@ def welcome_wizard_widget(sender, **kwargs):
def event_index(request, organizer, event):
from pretix.control.forms.event import CommentForm
subevent = None
if request.GET.get("subevent", "") != "" and request.event.has_subevents:
i = request.GET.get("subevent", "")

View File

@@ -98,6 +98,7 @@ from pretix.control.views.mailsetup import MailSettingsSetupView
from pretix.control.views.user import RecentAuthenticationRequiredMixin
from pretix.helpers.database import rolledback_transaction
from pretix.multidomain.urlreverse import build_absolute_uri, get_event_domain
from pretix.plugins.stripe.payment import StripeSettingsHolder
from pretix.presale.views.widget import (
version_default as widget_version_default,
)
@@ -1665,8 +1666,6 @@ class QuickSetupView(FormView):
'or take your event live to start selling!'))
if form.cleaned_data.get('payment_stripe__enabled', False):
from pretix.plugins.stripe.payment import StripeSettingsHolder
self.request.session['payment_stripe_oauth_enable'] = True
return redirect(StripeSettingsHolder(self.request.event).get_connect_url(self.request))

View File

@@ -65,7 +65,7 @@ from pretix.api.serializers.item import (
)
from pretix.base.forms import I18nFormSet
from pretix.base.models import (
CartPosition, Item, ItemCategory, ItemProgramTime, ItemVariation,
CartPosition, Item, ItemCategory, ItemProgramTime, ItemVariation, Order,
OrderPosition, Question, QuestionAnswer, QuestionOption, Quota,
SeatCategoryMapping, Voucher,
)
@@ -74,7 +74,6 @@ from pretix.base.models.items import ItemAddOn, ItemBundle, ItemMetaValue
from pretix.base.services.quotas import QuotaAvailability
from pretix.base.services.tickets import invalidate_cache
from pretix.base.signals import quota_availability
from pretix.control.forms.filter import QuestionAnswerFilterForm
from pretix.control.forms.item import (
CategoryForm, ItemAddOnForm, ItemAddOnsFormSet, ItemBundleForm,
ItemBundleFormSet, ItemCreateForm, ItemMetaValueForm, ItemProgramTimeForm,
@@ -661,26 +660,46 @@ class QuestionMixin:
return ctx
class QuestionView(EventPermissionRequiredMixin, ChartContainingView, DetailView):
class QuestionView(EventPermissionRequiredMixin, QuestionMixin, ChartContainingView, DetailView):
model = Question
template_name = 'pretixcontrol/items/question.html'
permission = 'can_change_items'
template_name_field = 'question'
@cached_property
def filter_form(self):
return QuestionAnswerFilterForm(event=self.request.event, data=self.request.GET)
def get_answer_statistics(self):
opqs = OrderPosition.objects.filter(
order__event=self.request.event,
)
if self.filter_form.is_valid():
opqs = self.filter_form.filter_qs(opqs)
qs = QuestionAnswer.objects.filter(
question=self.object, orderposition__isnull=False,
)
if self.request.GET.get("subevent", "") != "":
opqs = opqs.filter(subevent=self.request.GET["subevent"])
s = self.request.GET.get("status", "np")
if s != "":
if s == 'o':
opqs = opqs.filter(order__status=Order.STATUS_PENDING,
order__expires__lt=now().replace(hour=0, minute=0, second=0))
elif s == 'np':
opqs = opqs.filter(order__status__in=[Order.STATUS_PENDING, Order.STATUS_PAID])
elif s == 'pv':
opqs = opqs.filter(
Q(order__status=Order.STATUS_PAID) |
Q(order__status=Order.STATUS_PENDING, order__valid_if_pending=True)
)
elif s == 'ne':
opqs = opqs.filter(order__status__in=[Order.STATUS_PENDING, Order.STATUS_EXPIRED])
else:
opqs = opqs.filter(order__status=s)
if s not in (Order.STATUS_CANCELED, ""):
opqs = opqs.filter(canceled=False)
if self.request.GET.get("item", "") != "":
i = self.request.GET.get("item", "")
opqs = opqs.filter(item_id__in=(i,))
qs = qs.filter(orderposition__in=opqs)
op_cnt = opqs.filter(item__in=self.object.items.all()).count()
@@ -727,11 +746,9 @@ class QuestionView(EventPermissionRequiredMixin, ChartContainingView, DetailView
def get_context_data(self, **kwargs):
ctx = super().get_context_data()
ctx['items'] = self.object.items.exists()
ctx['has_subevents'] = self.request.event.has_subevents
ctx['items'] = self.object.items.all()
stats = self.get_answer_statistics()
ctx['stats'], ctx['total'] = stats
ctx['form'] = self.filter_form
return ctx
def get_object(self, queryset=None) -> Question:

View File

@@ -38,6 +38,7 @@ from datetime import timedelta
from django.conf import settings
from django.contrib import messages
from django.http import Http404
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
from django.utils.functional import cached_property
@@ -85,6 +86,7 @@ class BaseImportView(TemplateView):
filename='import.csv',
type='text/csv',
)
cf.bind_to_session(request, "modelimport")
cf.file.save('import.csv', request.FILES['file'])
if self.request.POST.get("charset") in ENCODINGS:
@@ -137,7 +139,10 @@ class BaseProcessView(AsyncAction, FormView):
@cached_property
def file(self):
return get_object_or_404(CachedFile, pk=self.kwargs.get("file"), filename="import.csv")
cf = get_object_or_404(CachedFile, pk=self.kwargs.get("file"), filename="import.csv")
if not cf.allowed_for_session(self.request, "modelimport"):
raise Http404()
return cf
@cached_property
def parsed(self):
@@ -146,7 +151,7 @@ class BaseProcessView(AsyncAction, FormView):
else:
charset = None
try:
reader = parse_csv(self.file.file, 1024 * 1024, charset=charset)
return parse_csv(self.file.file, 1024 * 1024, charset=charset)
except UnicodeDecodeError:
messages.warning(
self.request,
@@ -155,16 +160,7 @@ class BaseProcessView(AsyncAction, FormView):
"Some characters were replaced with a placeholder."
)
)
reader = parse_csv(self.file.file, 1024 * 1024, "replace", charset=charset)
if reader and reader._had_duplicates:
messages.warning(
self.request,
_(
"Multiple columns of the CSV file have the same name and were renamed automatically. We "
"recommend that you rename these in your source file to avoid problems during import."
)
)
return reader
return parse_csv(self.file.file, 1024 * 1024, "replace", charset=charset)
@cached_property
def parsed_list(self):

View File

@@ -247,7 +247,7 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
cf = None
if request.POST.get("background", "").strip():
try:
cf = CachedFile.objects.get(id=request.POST.get("background"))
cf = CachedFile.objects.get(id=request.POST.get("background"), web_download=True)
except CachedFile.DoesNotExist:
pass

View File

@@ -38,7 +38,8 @@ from collections import OrderedDict
from zipfile import ZipFile
from django.contrib import messages
from django.shortcuts import get_object_or_404, redirect
from django.http import Http404
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.functional import cached_property
from django.utils.translation import get_language, gettext_lazy as _
@@ -94,6 +95,8 @@ class ShredDownloadView(RecentAuthenticationRequiredMixin, EventPermissionRequir
cf = CachedFile.objects.get(pk=kwargs['file'])
except CachedFile.DoesNotExist:
raise ShredError(_("The download file could no longer be found on the server, please try to start again."))
if not cf.allowed_for_session(self.request):
raise Http404()
with ZipFile(cf.file.file, 'r') as zipfile:
indexdata = json.loads(zipfile.read('index.json').decode())
@@ -111,7 +114,7 @@ class ShredDownloadView(RecentAuthenticationRequiredMixin, EventPermissionRequir
ctx = super().get_context_data(**kwargs)
ctx['shredders'] = self.shredders
ctx['download_on_shred'] = any(shredder.require_download_confirmation for shredder in shredders)
ctx['file'] = get_object_or_404(CachedFile, pk=kwargs.get("file"))
ctx['file'] = cf
return ctx

View File

@@ -87,7 +87,7 @@ class VoucherList(PaginationMixin, EventPermissionRequiredMixin, ListView):
@scopes_disabled() # we have an event check here, and we can save some performance on subqueries
def get_queryset(self):
qs = Voucher.annotate_budget_used(self.request.event.vouchers.exclude(
qs = Voucher.annotate_budget_used_orders(self.request.event.vouchers.exclude(
Exists(WaitingListEntry.objects.filter(voucher_id=OuterRef('pk')))
).select_related(
'item', 'variation', 'seat'

View File

@@ -1,62 +0,0 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-today pretix 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.conf import settings
THRESHOLD_DOWNGRADE_TO_MID = 50
THRESHOLD_DOWNGRADE_TO_LOW = 250
def get_task_priority(shard, organizer_id):
"""
This is an attempt to build a simple "fair-use" policy for webhooks and notifications. The problem is that when
one organizer creates e.g. 20,000 orders through the API, that might schedule 20,000 webhooks and every other
organizer will need to wait for these webhooks to go through.
We try to fix that by building three queues: high-prio, mid-prio, and low-prio. Every organizer starts in the
high-prio queue, and all their tasks are routed immediately. Once an organizer submits more than X jobs of a
certain type per minute, they get downgraded to the mid-prio queue, and then if they submit even more to the
low-prio queue. That way, if another organizer has "regular usage", they are prioritized over the organizer with
high load.
"""
from django_redis import get_redis_connection
if not settings.HAS_REDIS:
return settings.PRIORITY_CELERY_HIGH
# We use redis directly instead of the Django cache API since the Django cache API does not support INCR for
# nonexistant keys
rc = get_redis_connection("redis")
cache_key = f"pretix:task_priority:{shard}:{organizer_id}"
# Make sure counters expire after a while when not used
p = rc.pipeline()
p.incr(cache_key)
p.expire(cache_key, 60)
new_counter = p.execute()[0]
if new_counter >= THRESHOLD_DOWNGRADE_TO_LOW:
return settings.PRIORITY_CELERY_LOW
elif new_counter >= THRESHOLD_DOWNGRADE_TO_MID:
return settings.PRIORITY_CELERY_MID
else:
return settings.PRIORITY_CELERY_HIGH

View File

@@ -1,210 +0,0 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-today pretix 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 urllib.parse import quote, urlencode
import text_unidecode
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
def dotdecimal(value):
return str(value).replace(",", ".")
def commadecimal(value):
return str(value).replace(".", ",")
def generate_payment_qr_codes(
event,
code,
amount,
bank_details_sepa_bic,
bank_details_sepa_name,
bank_details_sepa_iban,
):
out = []
for method in [
swiss_qrbill,
czech_spayd,
euro_epc_qr,
euro_bezahlcode,
]:
data = method(
event,
code,
amount,
bank_details_sepa_bic,
bank_details_sepa_name,
bank_details_sepa_iban,
)
if data:
out.append(data)
return out
def euro_epc_qr(
event,
code,
amount,
bank_details_sepa_bic,
bank_details_sepa_name,
bank_details_sepa_iban,
):
if event.currency != 'EUR' or not bank_details_sepa_iban:
return
return {
"id": "girocode",
"label": "EPC-QR",
"qr_data": "\n".join(text_unidecode.unidecode(str(d or '')) for d in [
"BCD", # Service Tag: BCD
"002", # Version: V2
"2", # Character set: ISO 8859-1
"SCT", # Identification code: SCT
bank_details_sepa_bic, # AT-23 BIC of the Beneficiary Bank
bank_details_sepa_name, # AT-21 Name of the Beneficiary
bank_details_sepa_iban, # AT-20 Account number of the Beneficiary
f"{event.currency}{dotdecimal(amount)}", # AT-04 Amount of the Credit Transfer in Euro
"", # AT-44 Purpose of the Credit Transfer
"", # AT-05 Remittance Information (Structured)
code, # AT-05 Remittance Information (Unstructured)
"", # Beneficiary to originator information
"",
]),
}
def euro_bezahlcode(
event,
code,
amount,
bank_details_sepa_bic,
bank_details_sepa_name,
bank_details_sepa_iban,
):
if not bank_details_sepa_iban or bank_details_sepa_iban[:2] != 'DE':
return
if event.currency != 'EUR':
return
qr_data = "bank://singlepaymentsepa?" + urlencode({
"name": str(bank_details_sepa_name),
"iban": str(bank_details_sepa_iban),
"bic": str(bank_details_sepa_bic),
"amount": commadecimal(amount),
"reason": str(code),
"currency": str(event.currency),
}, quote_via=quote)
return {
"id": "bezahlcode",
"label": "BezahlCode",
"qr_data": mark_safe(qr_data),
"link": qr_data,
"link_aria_label": _("Open BezahlCode in your banking app to start the payment process."),
}
def swiss_qrbill(
event,
code,
amount,
bank_details_sepa_bic,
bank_details_sepa_name,
bank_details_sepa_iban,
):
if not bank_details_sepa_iban or not bank_details_sepa_iban[:2] in ('CH', 'LI'):
return
if event.currency not in ('EUR', 'CHF'):
return
if not event.settings.invoice_address_from or not event.settings.invoice_address_from_country:
return
data_fields = [
'SPC',
'0200',
'1',
bank_details_sepa_iban,
'K',
bank_details_sepa_name[:70],
event.settings.invoice_address_from.replace('\n', ', ')[:70],
(event.settings.invoice_address_from_zipcode + ' ' + event.settings.invoice_address_from_city)[:70],
'',
'',
str(event.settings.invoice_address_from_country),
'', # rfu
'', # rfu
'', # rfu
'', # rfu
'', # rfu
'', # rfu
'', # rfu
str(amount),
event.currency,
'', # debtor address
'', # debtor address
'', # debtor address
'', # debtor address
'', # debtor address
'', # debtor address
'', # debtor address
'NON',
'', # structured reference
code,
'EPD',
]
data_fields = [text_unidecode.unidecode(d or '') for d in data_fields]
qr_data = '\r\n'.join(data_fields)
return {
"id": "qrbill",
"label": "QR-bill",
"html_prefix": mark_safe(
'<svg class="banktransfer-swiss-cross" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.8 19.8">'
'<path stroke="#fff" stroke-width="1.436" d="M.7.7h18.4v18.4H.7z"/><path fill="#fff" d="M8.3 4h3.3v11H8.3z"/>'
'<path fill="#fff" d="M4.4 7.9h11v3.3h-11z"/></svg>'
),
"qr_data": qr_data,
"css_class": "banktransfer-swiss-cross-overlay",
}
def czech_spayd(
event,
code,
amount,
bank_details_sepa_bic,
bank_details_sepa_name,
bank_details_sepa_iban,
):
if not bank_details_sepa_iban or not bank_details_sepa_iban[:2] in ('CZ', 'SK'):
return
if event.currency not in ('EUR', 'CZK'):
return
qr_data = f"SPD*1.0*ACC:{bank_details_sepa_iban}*AM:{dotdecimal(amount)}*CC:{event.currency}*MSG:{code}"
return {
"id": "spayd",
"label": "SPAYD",
"qr_data": qr_data,
}

File diff suppressed because it is too large Load Diff

View File

@@ -8,16 +8,16 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-20 10:37+0000\n"
"PO-Revision-Date: 2025-12-03 23:00+0000\n"
"Last-Translator: sandra r <sandrarial@gestiontickets.online>\n"
"Language-Team: Galician <https://translate.pretix.eu/projects/pretix/"
"pretix-js/gl/>\n"
"PO-Revision-Date: 2022-02-22 22:00+0000\n"
"Last-Translator: Ismael Menéndez Fernández <ismael.menendez@balidea.com>\n"
"Language-Team: Galician <https://translate.pretix.eu/projects/pretix/pretix-"
"js/gl/>\n"
"Language: gl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.14.3\n"
"X-Generator: Weblate 4.8\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -31,104 +31,106 @@ msgstr "Comentario:"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:34
msgid "PayPal"
msgstr "PayPal"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:35
msgid "Venmo"
msgstr "Venmo"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
#: pretix/static/pretixpresale/js/walletdetection.js:38
msgid "Apple Pay"
msgstr "Apple Pay"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:37
msgid "Itaú"
msgstr "Itaú"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:38
msgid "PayPal Credit"
msgstr "Crédito PayPal"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:39
msgid "Credit Card"
msgstr "Tarxeta de crédito"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:40
msgid "PayPal Pay Later"
msgstr "PayPal Pagar Máis Tarde"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:41
msgid "iDEAL"
msgstr "iDEAL"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:42
msgid "SEPA Direct Debit"
msgstr "Débito directo SEPA"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:43
msgid "Bancontact"
msgstr "Bancontact"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:44
msgid "giropay"
msgstr "giropay"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:45
msgid "SOFORT"
msgstr "SOFORT"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:46
#, fuzzy
#| msgid "Yes"
msgid "eps"
msgstr "Si"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:47
msgid "MyBank"
msgstr "MyBank"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:48
msgid "Przelewy24"
msgstr "Przelewy24"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:49
msgid "Verkkopankki"
msgstr "Verkkopankki"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:50
msgid "PayU"
msgstr "PayU"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:51
msgid "BLIK"
msgstr "BLIK"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:52
msgid "Trustly"
msgstr "De confianza"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:53
msgid "Zimpler"
msgstr "Zimpler"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:54
msgid "Maxima"
msgstr "Máxima"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:55
msgid "OXXO"
msgstr "OXXO"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:56
msgid "Boleto"
msgstr "Ticket"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:57
msgid "WeChat Pay"
msgstr "Pagar con WeChat"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:58
msgid "Mercado Pago"
msgstr "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:167
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:50
@@ -147,7 +149,7 @@ msgstr "Confirmando o pagamento…"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:254
msgid "Payment method unavailable"
msgstr "O método de pago non está dispoñible"
msgstr ""
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:15
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
@@ -238,11 +240,11 @@ msgstr "Cancelado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:46
msgid "Confirmed"
msgstr "Confirmado"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:47
msgid "Approval pending"
msgstr "Aprobación pendente"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Redeemed"
@@ -298,12 +300,16 @@ msgid "Ticket code revoked/changed"
msgstr "Código de tícket revogado/cambiado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:63
#, fuzzy
#| msgid "Ticket not paid"
msgid "Ticket blocked"
msgstr "Ticket bloqueado"
msgstr "Tícket pendente de pago"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:64
#, fuzzy
#| msgid "Ticket not paid"
msgid "Ticket not valid at this time"
msgstr "O ticket non é válido neste momento"
msgstr "cket pendente de pago"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65
msgid "Order canceled"
@@ -311,11 +317,11 @@ msgstr "Pedido cancelado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Ticket code is ambiguous on list"
msgstr "O código do ticket é ambiguo na lista"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67
msgid "Order not approved"
msgstr "Orde non aprobada"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:68
msgid "Checked-in Tickets"
@@ -416,7 +422,7 @@ msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:276
msgid "If this takes longer than a few minutes, please contact us."
msgstr "Se isto leva máis duns minutos, póñase en contacto connosco."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:331
msgid "Close message"
@@ -446,7 +452,7 @@ msgstr "está despois"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:40
msgid "="
msgstr "="
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:99
msgid "Product"
@@ -458,7 +464,7 @@ msgstr "Ver variacións do produto"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:107
msgid "Gate"
msgstr "Porta"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:111
msgid "Current date and time"
@@ -466,11 +472,11 @@ msgstr "Data e hora actual"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:115
msgid "Current day of the week (1 = Monday, 7 = Sunday)"
msgstr "Día actual da semana (1 = luns, 7 = domingo)"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:119
msgid "Current entry status"
msgstr "Estado de entrada actual"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:123
msgid "Number of previous entries"
@@ -481,32 +487,40 @@ msgid "Number of previous entries since midnight"
msgstr "Número de entradas previas desde a medianoite"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:131
#, fuzzy
#| msgid "Number of previous entries"
msgid "Number of previous entries since"
msgstr "Número de entradas anteriores desde"
msgstr "Número de entradas previas"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:135
#, fuzzy
#| msgid "Number of previous entries"
msgid "Number of previous entries before"
msgstr "Número de entradas anteriores antes de"
msgstr "Número de entradas previas"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:139
msgid "Number of days with a previous entry"
msgstr "Número de días cunha entrada previa"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:143
#, fuzzy
#| msgid "Number of days with a previous entry"
msgid "Number of days with a previous entry since"
msgstr "Número de días cunha entrada previa desde"
msgstr "Número de días cunha entrada previa"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:147
#, fuzzy
#| msgid "Number of days with a previous entry"
msgid "Number of days with a previous entry before"
msgstr "Número de días cunha entrada previa"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:151
msgid "Minutes since last entry (-1 on first entry)"
msgstr "Minutos desde a última entrada (-1 na primeira entrada)"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:155
msgid "Minutes since first entry (-1 on first entry)"
msgstr "Minutos desde a primeira entrada (-1 na primeira entrada)"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:182
msgid "All of the conditions below (AND)"
@@ -550,25 +564,25 @@ msgstr "minutos"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:192
msgid "Duplicate"
msgstr "Duplicar"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:193
msgctxt "entry_status"
msgid "present"
msgstr "presente"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:194
msgctxt "entry_status"
msgid "absent"
msgstr "ausente"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:289
msgid "Error: Product not found!"
msgstr "Erro: Non se atopou o produto!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:296
msgid "Error: Variation not found!"
msgstr "Erro: Variación non atopada!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:171
msgid "Check-in QR"
@@ -583,12 +597,16 @@ msgid "Group of objects"
msgstr "Grupo de obxectos"
#: pretix/static/pretixcontrol/js/ui/editor.js:909
#, fuzzy
#| msgid "Text object"
msgid "Text object (deprecated)"
msgstr "Obxecto de texto (obsoleto)"
msgstr "Obxecto de texto"
#: pretix/static/pretixcontrol/js/ui/editor.js:911
#, fuzzy
#| msgid "Text object"
msgid "Text box"
msgstr "Caixa de texto"
msgstr "Obxecto de texto"
#: pretix/static/pretixcontrol/js/ui/editor.js:913
msgid "Barcode area"
@@ -637,26 +655,26 @@ msgid "Unknown error."
msgstr "Erro descoñecido."
#: pretix/static/pretixcontrol/js/ui/main.js:309
#, fuzzy
#| msgid "Your color has great contrast and is very easy to read!"
msgid "Your color has great contrast and will provide excellent accessibility."
msgstr ""
"A túa cor ten un gran contraste e proporcionará unha excelente "
"accesibilidade."
msgstr "A túa cor ten moito contraste e é moi doada de ler!"
#: pretix/static/pretixcontrol/js/ui/main.js:313
#, fuzzy
#| msgid "Your color has decent contrast and is probably good-enough to read!"
msgid ""
"Your color has decent contrast and is sufficient for minimum accessibility "
"requirements."
msgstr ""
"A túa cor ten un contraste decente e é suficiente para os requisitos mínimos "
"de accesibilidade."
"A túa cor ten un contraste axeitado e probablemente sexa suficientemente "
"lexible!"
#: pretix/static/pretixcontrol/js/ui/main.js:317
msgid ""
"Your color has insufficient contrast to white. Accessibility of your site "
"will be impacted."
msgstr ""
"A túa cor non ten suficiente contraste co branco. A accesibilidade do teu "
"sitio web verase afectada."
#: pretix/static/pretixcontrol/js/ui/main.js:443
#: pretix/static/pretixcontrol/js/ui/main.js:463
@@ -677,11 +695,11 @@ msgstr "Soamente seleccionados"
#: pretix/static/pretixcontrol/js/ui/main.js:839
msgid "Enter page number between 1 and %(max)s."
msgstr "Introduza o número de páxina entre 1 e %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:842
msgid "Invalid page number."
msgstr "Número de páxina non válido."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1000
msgid "Use a different name internally"
@@ -700,8 +718,10 @@ msgid "Calculating default price…"
msgstr "Calculando o prezo por defecto…"
#: pretix/static/pretixcontrol/js/ui/plugins.js:69
#, fuzzy
#| msgid "Search results"
msgid "No results"
msgstr "Sen resultados"
msgstr "Resultados da procura"
#: pretix/static/pretixcontrol/js/ui/question.js:41
msgid "Others"
@@ -732,7 +752,7 @@ msgstr "O carro da compra caducou"
#: pretix/static/pretixpresale/js/ui/cart.js:58
#: pretix/static/pretixpresale/js/ui/cart.js:84
msgid "Your cart is about to expire."
msgstr "O teu carriño está a piques de caducar."
msgstr ""
#: pretix/static/pretixpresale/js/ui/cart.js:62
msgid "The items in your cart are reserved for you for one minute."
@@ -742,10 +762,16 @@ msgstr[1] ""
"Os artigos da túa cesta están reservados para ti durante {num} minutos."
#: pretix/static/pretixpresale/js/ui/cart.js:83
#, fuzzy
#| msgid "Cart expired"
msgid "Your cart has expired."
msgstr "O carro da compra caducou."
msgstr "O carro da compra caducou"
#: pretix/static/pretixpresale/js/ui/cart.js:86
#, fuzzy
#| msgid ""
#| "The items in your cart are no longer reserved for you. You can still "
#| "complete your order as long as theyre available."
msgid ""
"The items in your cart are no longer reserved for you. You can still "
"complete your order as long as they're available."
@@ -755,11 +781,11 @@ msgstr ""
#: pretix/static/pretixpresale/js/ui/cart.js:87
msgid "Do you want to renew the reservation period?"
msgstr "Queres renovar o período de reserva?"
msgstr ""
#: pretix/static/pretixpresale/js/ui/cart.js:90
msgid "Renew reservation"
msgstr "Renovar reserva"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:194
msgid "The organizer keeps %(currency)s %(amount)s"
@@ -779,66 +805,71 @@ msgstr "A súa hora local:"
#: pretix/static/pretixpresale/js/walletdetection.js:39
msgid "Google Pay"
msgstr "Google Pay"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:16
msgctxt "widget"
msgid "Quantity"
msgstr "Cantidade"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:17
msgctxt "widget"
msgid "Decrease quantity"
msgstr "Diminuír a cantidade"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:18
msgctxt "widget"
msgid "Increase quantity"
msgstr "Aumentar a cantidade"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:19
msgctxt "widget"
msgid "Filter events by"
msgstr "Filtrar eventos por"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:20
msgctxt "widget"
msgid "Filter"
msgstr "Filtro"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:21
msgctxt "widget"
msgid "Price"
msgstr "Prezo"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:22
#, javascript-format
msgctxt "widget"
msgid "Original price: %s"
msgstr "Prezo orixinal: %s"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:23
#, javascript-format
msgctxt "widget"
msgid "New price: %s"
msgstr "Novo prezo: %s"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:24
#, fuzzy
#| msgid "Selected only"
msgctxt "widget"
msgid "Select"
msgstr "Seleccione"
msgstr "Soamente seleccionados"
#: pretix/static/pretixpresale/js/widget/widget.js:25
#, javascript-format
#, fuzzy, javascript-format
#| msgid "Selected only"
msgctxt "widget"
msgid "Select %s"
msgstr "Seleccione %s"
msgstr "Soamente seleccionados"
#: pretix/static/pretixpresale/js/widget/widget.js:26
#, javascript-format
#, fuzzy, javascript-format
#| msgctxt "widget"
#| msgid "See variations"
msgctxt "widget"
msgid "Select variant %s"
msgstr "Seleccione a variante %s"
msgstr "Ver variacións"
#: pretix/static/pretixpresale/js/widget/widget.js:27
msgctxt "widget"
@@ -874,7 +905,7 @@ msgstr "dende %(currency)s %(price)s"
#, javascript-format
msgctxt "widget"
msgid "Image of %s"
msgstr "Imaxe de %s"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:34
msgctxt "widget"
@@ -909,19 +940,25 @@ msgstr "Só dispoñible mediante vale"
#: pretix/static/pretixpresale/js/widget/widget.js:40
#: pretix/static/pretixpresale/js/widget/widget.js:43
#, fuzzy
#| msgctxt "widget"
#| msgid "currently available: %s"
msgctxt "widget"
msgid "Not yet available"
msgstr "Aínda non dispoñible"
msgstr "dispoñible actualmente: %s"
#: pretix/static/pretixpresale/js/widget/widget.js:41
msgctxt "widget"
msgid "Not available anymore"
msgstr "Xa non está dispoñible"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:42
#, fuzzy
#| msgctxt "widget"
#| msgid "currently available: %s"
msgctxt "widget"
msgid "Currently not available"
msgstr "Non dispoñible actualmente"
msgstr "dispoñible actualmente: %s"
#: pretix/static/pretixpresale/js/widget/widget.js:44
#, javascript-format
@@ -954,6 +991,9 @@ msgid "Open ticket shop"
msgstr "Abrir a tenda de tíckets"
#: pretix/static/pretixpresale/js/widget/widget.js:50
#, fuzzy
#| msgctxt "widget"
#| msgid "Resume checkout"
msgctxt "widget"
msgid "Checkout"
msgstr "Continuar co pagamento"
@@ -1013,14 +1053,17 @@ msgid "Close"
msgstr "Cerrar"
#: pretix/static/pretixpresale/js/widget/widget.js:62
#, fuzzy
#| msgctxt "widget"
#| msgid "Resume checkout"
msgctxt "widget"
msgid "Close checkout"
msgstr "Pagamento pechado"
msgstr "Continuar co pagamento"
#: pretix/static/pretixpresale/js/widget/widget.js:63
msgctxt "widget"
msgid "You cannot cancel this operation. Please wait for loading to finish."
msgstr "Non podes cancelar esta operación. Agarda a que remate a carga."
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:64
msgctxt "widget"
@@ -1028,14 +1071,20 @@ msgid "Continue"
msgstr "Continuar"
#: pretix/static/pretixpresale/js/widget/widget.js:65
#, fuzzy
#| msgctxt "widget"
#| msgid "See variations"
msgctxt "widget"
msgid "Show variants"
msgstr "Ver variacións"
#: pretix/static/pretixpresale/js/widget/widget.js:66
#, fuzzy
#| msgctxt "widget"
#| msgid "See variations"
msgctxt "widget"
msgid "Hide variants"
msgstr "Ocultar variantes"
msgstr "Ver variacións"
#: pretix/static/pretixpresale/js/widget/widget.js:67
msgctxt "widget"
@@ -1084,9 +1133,6 @@ msgid ""
"add yourself to the waiting list. We will then notify if seats are available "
"again."
msgstr ""
"Algunhas ou todas as categorías de entradas están esgotadas. Se queres, "
"podes engadirte á lista de espera. Despois avisarémosche se volven quedar "
"asentos dispoñibles."
#: pretix/static/pretixpresale/js/widget/widget.js:76
msgctxt "widget"
@@ -1123,31 +1169,31 @@ msgstr "Dom"
#: pretix/static/pretixpresale/js/widget/widget.js:85
msgid "Monday"
msgstr "Luns"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:86
msgid "Tuesday"
msgstr "Martes"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:87
msgid "Wednesday"
msgstr "Mércores"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:88
msgid "Thursday"
msgstr "Xoves"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:89
msgid "Friday"
msgstr "Venres"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:90
msgid "Saturday"
msgstr "Sábado"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:91
msgid "Sunday"
msgstr "Domingo"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:94
msgid "January"

View File

@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-27 13:57+0000\n"
"PO-Revision-Date: 2025-12-05 18:00+0000\n"
"PO-Revision-Date: 2025-11-26 17:00+0000\n"
"Last-Translator: Yasunobu YesNo Kawaguchi <kawaguti@gmail.com>\n"
"Language-Team: Japanese <https://translate.pretix.eu/projects/pretix/pretix/"
"ja/>\n"
@@ -2149,7 +2149,7 @@ msgstr "クライアントID"
#: pretix/base/exporters/items.py:91 pretix/base/models/items.py:667
#: pretix/base/models/items.py:1168
msgid "Original price"
msgstr "通常価格"
msgstr "元の価格"
#: pretix/base/exporters/items.py:92 pretix/base/models/items.py:684
msgid "This product is a gift card"
@@ -22741,8 +22741,9 @@ msgid ""
"This position has been created with a voucher with a limited budget. If you "
"change the price or item, the discount will still be calculated from the "
"original price at the time of purchase."
msgstr "このポジションは限られた予算のバウチャーで作成されました。価格やアイテムを変"
"更しても、割引は購入時の通常価格から計算されます。"
msgstr ""
"このポジションは限られた予算のバウチャーで作成されました。価格やアイテムを変"
"更しても、割引は購入時の元の価格から計算されます。"
#: pretix/control/templates/pretixcontrol/order/change.html:101
#: pretix/control/templates/pretixcontrol/order/change.html:413
@@ -33769,7 +33770,7 @@ msgstr "バリエーションを表示する"
#: pretix/presale/templates/pretixpresale/event/voucher.html:147
#: pretix/presale/templates/pretixpresale/event/voucher.html:304
msgid "Original price:"
msgstr "通常価格:"
msgstr "元の価格:"
#: pretix/presale/templates/pretixpresale/event/fragment_addon_choice.html:136
#: pretix/presale/templates/pretixpresale/event/fragment_addon_choice.html:278

View File

@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-27 13:57+0000\n"
"PO-Revision-Date: 2025-12-03 23:00+0000\n"
"Last-Translator: SJang1 <git@sjang.dev>\n"
"PO-Revision-Date: 2025-11-18 17:00+0000\n"
"Last-Translator: Hijiri Umemoto <hijiri@umemoto.org>\n"
"Language-Team: Korean <https://translate.pretix.eu/projects/pretix/pretix/ko/"
">\n"
"Language: ko\n"
@@ -290,27 +290,33 @@ msgstr "묶음 상품은 그 자체로 또 다른 묶음을 포함 할 수 없
#: pretix/api/serializers/item.py:235
msgid "The program start must not be empty."
msgstr "프로그램 시작일정은 비어 있어서는 안 됩니다."
msgstr ""
#: pretix/api/serializers/item.py:239
msgid "The program end must not be empty."
msgstr "프로그램 종료일정은 비어 있어서는 안 됩니다."
msgstr ""
#: pretix/api/serializers/item.py:242 pretix/base/models/items.py:2321
#, fuzzy
#| msgid "The maximum date must not be before the minimum value."
msgid "The program end must not be before the program start."
msgstr "종료일은 시작일보다 앞서면 안됩니다."
msgstr "종료일(최대 날짜)은 시작일(최소값)보다 앞서면 안됩니다."
#: pretix/api/serializers/item.py:247 pretix/base/models/items.py:2315
#, fuzzy
#| msgid "You can not select a subevent if your event is not an event series."
msgid "You cannot use program times on an event series."
msgstr "이벤트 시리즈에 있는 시간은 사용하실 수 없습니다."
msgstr ""
"당신의이벤트가 이벤트 시리즈가 아닌 경우 하위 이벤트를 선택할 수 없습니다."
#: pretix/api/serializers/item.py:337
#, fuzzy
msgid ""
"Updating add-ons, bundles, program times or variations via PATCH/PUT is not "
"supported. Please use the dedicated nested endpoint."
msgstr ""
"추가 기능, 묶음 상품들, 또는 변형은 PATCH/PUT를 통해 업데이트 할 수 없습니다."
" 전용 중첩은 마지막 지점에서 사용하세요."
"추가 기능, 묶음 상품들, 또는 변형은 PATCH/PUT를 통해 업데이트 할 수 없습니"
"다. 전용 중첩은 마지막 지점에서 사용하세요"
#: pretix/api/serializers/item.py:345
msgid "Only admission products can currently be personalized."
@@ -567,15 +573,22 @@ msgid "Event series date deleted"
msgstr "이벤트 시리즈 날짜 삭제"
#: pretix/api/webhooks.py:374
#, fuzzy
#| msgid "Product name"
msgid "Product changed"
msgstr "제품 변경됨"
msgstr "제품"
#: pretix/api/webhooks.py:375
#, fuzzy
#| msgid ""
#| "Product changed (including product added or deleted and including changes "
#| "to nested objects like variations or bundles)"
msgid ""
"This includes product added or deleted and changes to nested objects like "
"variations or bundles."
msgstr "여기에는 추가되거나 삭제되거나 변경된 중첩오브젝트나 번들과 같은 사항이 포함"
"됩니다."
msgstr ""
"제품 변경(제품 추가 또는 삭제, 변형 또는 번들과 같은 중첩된 객체에 대한 변경 "
"포함)"
#: pretix/api/webhooks.py:380
msgid "Shop taken live"
@@ -610,12 +623,16 @@ msgid "Waiting list entry received voucher"
msgstr "대기자 명단 항목이 바우처를 받았습니다"
#: pretix/api/webhooks.py:412
#, fuzzy
#| msgid "Voucher code"
msgid "Voucher added"
msgstr "바우처 추가됨"
msgstr "바우처 코드"
#: pretix/api/webhooks.py:416
#, fuzzy
#| msgid "Voucher assigned"
msgid "Voucher changed"
msgstr "바우처 변경됨"
msgstr "바우처 할당"
#: pretix/api/webhooks.py:417
msgid ""
@@ -626,8 +643,10 @@ msgstr ""
"하지 않습니다."
#: pretix/api/webhooks.py:421
#, fuzzy
#| msgid "Voucher redeemed"
msgid "Voucher deleted"
msgstr "바우처 제거됨"
msgstr "바우처 상환"
#: pretix/api/webhooks.py:425
msgid "Customer account created"
@@ -652,7 +671,7 @@ msgstr "고객 계정 익명화되었습니다"
#: pretix/plugins/banktransfer/payment.py:513
#: pretix/presale/forms/customer.py:152
msgid "This field is required."
msgstr "이 필드는 필수입니다."
msgstr "이 필드는 필수입니다"
#: pretix/base/addressvalidation.py:213
msgid "Enter a postal code in the format XXX."
@@ -702,7 +721,7 @@ msgstr "비밀번호"
#: pretix/base/auth.py:176 pretix/base/auth.py:183
msgid "Your password must contain both numeric and alphabetic characters."
msgstr "비밀번호는 숫자와 알파벳 문자가 모두 포함되어야 합니다."
msgstr "비밀번호는 숫자와 알파벳 문자가 모두 포함되어야 합니다"
#: pretix/base/auth.py:202 pretix/base/auth.py:212
#, python-format
@@ -796,21 +815,28 @@ msgstr ""
"소를 확인해 주십시요."
#: pretix/base/datasync/datasync.py:263
#, python-brace-format
#, fuzzy, python-brace-format
#| msgid ""
#| "Field \"{field_name}\" is not valid for {available_inputs}. Please check "
#| "your {provider_name} settings."
msgid ""
"Field \"{field_name}\" does not exist. Please check your {provider_name} "
"settings."
msgstr "필드 \"{field_name}\"은 존재하지 않습니다. 당신의 {provider_name} 설정을 확인"
"해 주세요."
msgstr ""
"필드 \"{field_name}\"은 {available_inputs}에 유효하지 않습니다. "
"{provider_name} 설정을 확인해 주세요."
#: pretix/base/datasync/datasync.py:270
#, python-brace-format
#, fuzzy, python-brace-format
#| msgid ""
#| "Field \"{field_name}\" is not valid for {available_inputs}. Please check "
#| "your {provider_name} settings."
msgid ""
"Field \"{field_name}\" requires {required_input}, but only got "
"{available_inputs}. Please check your {provider_name} settings."
msgstr ""
"필드 \"{field_name}\"는 {required_input}을 필요로 하지만, {available_inputs}"
"만 받았습니다. 당신의 {provider_name} 설정을 확인해 주세요."
"필드 \"{field_name}\" {available_inputs}에 유효하지 않습니다. "
"{provider_name} 설정을 확인해 주세요."
#: pretix/base/datasync/datasync.py:281
#, python-brace-format
@@ -822,12 +848,16 @@ msgstr ""
"지 않았습니다"
#: pretix/base/datasync/sourcefields.py:128
#, fuzzy
#| msgid "Order positions"
msgid "Order position details"
msgstr "주문 위치 세부 정보"
msgstr "주문 위치"
#: pretix/base/datasync/sourcefields.py:129
#, fuzzy
#| msgid "Attendee email"
msgid "Attendee details"
msgstr "참석자 정보들"
msgstr "참석자 이메일"
#: pretix/base/datasync/sourcefields.py:130 pretix/base/exporters/answers.py:66
#: pretix/base/models/items.py:1766 pretix/control/navigation.py:172
@@ -837,8 +867,10 @@ msgid "Questions"
msgstr "질문들"
#: pretix/base/datasync/sourcefields.py:131
#, fuzzy
#| msgid "Product data"
msgid "Product details"
msgstr "상품 정보"
msgstr "상품 데이터"
#: pretix/base/datasync/sourcefields.py:132
#: pretix/control/templates/pretixcontrol/event/settings.html:279
@@ -863,13 +895,17 @@ msgid "Invoice address"
msgstr "송장 주소"
#: pretix/base/datasync/sourcefields.py:134
#, fuzzy
#| msgid "Meta information"
msgid "Event information"
msgstr "이벤트 정보"
msgstr "메타 정보(데이타에 대한 정보)"
#: pretix/base/datasync/sourcefields.py:135
#, fuzzy
#| msgid "Send recovery information"
msgctxt "subevent"
msgid "Event or date information"
msgstr "이벤트 또는 날짜 정보"
msgstr "복구 정보를 전송하다"
#: pretix/base/datasync/sourcefields.py:175
#: pretix/base/exporters/orderlist.py:605
@@ -894,8 +930,10 @@ msgstr "참석자 이름"
#: pretix/base/datasync/sourcefields.py:187
#: pretix/base/datasync/sourcefields.py:604
#: pretix/base/datasync/sourcefields.py:628
#, fuzzy
#| msgid "Attendee name"
msgid "Attendee"
msgstr "참석자"
msgstr "참석자 이름"
#: pretix/base/datasync/sourcefields.py:207
#: pretix/base/exporters/orderlist.py:612 pretix/base/forms/questions.py:687
@@ -909,8 +947,10 @@ msgid "Attendee email"
msgstr "참석자 이메일"
#: pretix/base/datasync/sourcefields.py:219
#, fuzzy
#| msgid "Attendee email"
msgid "Attendee or order email"
msgstr "참석자 또는 구매 이메일"
msgstr "참석자 이메일"
#: pretix/base/datasync/sourcefields.py:232
#: pretix/base/exporters/orderlist.py:613 pretix/base/pdf.py:189
@@ -923,20 +963,28 @@ msgid "Attendee company"
msgstr "참석자 회사"
#: pretix/base/datasync/sourcefields.py:241
#, fuzzy
#| msgid "Attendee address"
msgid "Attendee address street"
msgstr "참석자 주소 도로명"
msgstr "참석자 주소"
#: pretix/base/datasync/sourcefields.py:250
#, fuzzy
#| msgid "Attendee ZIP code"
msgid "Attendee address ZIP code"
msgstr "참석자 주소 우편번호"
msgstr "참석자 우편번호"
#: pretix/base/datasync/sourcefields.py:259
#, fuzzy
#| msgid "Attendee address"
msgid "Attendee address city"
msgstr "참석자 주소 도시"
msgstr "참석자 주소"
#: pretix/base/datasync/sourcefields.py:268
#, fuzzy
#| msgid "Attendee address"
msgid "Attendee address country"
msgstr "참석자 주소 국가"
msgstr "참석자 주소"
#: pretix/base/datasync/sourcefields.py:279
#: pretix/base/exporters/orderlist.py:653 pretix/base/pdf.py:347
@@ -972,12 +1020,16 @@ msgid "Invoice address country"
msgstr "송장 주소 국가"
#: pretix/base/datasync/sourcefields.py:353
#, fuzzy
#| msgid "Order total"
msgid "Order email"
msgstr "주문 이메일"
msgstr "주문 합계"
#: pretix/base/datasync/sourcefields.py:362
#, fuzzy
#| msgid "Order time"
msgid "Order email domain"
msgstr "주문 이메일 도메인"
msgstr "주문 시간"
#: pretix/base/datasync/sourcefields.py:371
#: pretix/base/exporters/invoices.py:203 pretix/base/exporters/invoices.py:332
@@ -1009,8 +1061,10 @@ msgid "Order code"
msgstr "주문 코드"
#: pretix/base/datasync/sourcefields.py:380
#, fuzzy
#| msgid "Event end date and time"
msgid "Event and order code"
msgstr "이벤트와 주문 번호"
msgstr "이벤트 종료 날짜 및 시간"
#: pretix/base/datasync/sourcefields.py:389
#: pretix/base/exporters/orderlist.py:263 pretix/base/notifications.py:201
@@ -1022,8 +1076,10 @@ msgid "Order total"
msgstr "주문 합계"
#: pretix/base/datasync/sourcefields.py:398
#, fuzzy
#| msgid "Product name and variation"
msgid "Product and variation name"
msgstr "제품명 및 변형 이름"
msgstr "제품명 및 변형"
#: pretix/base/datasync/sourcefields.py:410 pretix/base/exporters/items.py:57
#: pretix/base/exporters/orderlist.py:598
@@ -1033,12 +1089,16 @@ msgid "Product ID"
msgstr "상품 식별 아이디"
#: pretix/base/datasync/sourcefields.py:419
#, fuzzy
#| msgid "Count add-on products"
msgid "Product is admission product"
msgstr "상품은 입장 상품입니다"
msgstr "추가된 제품을 포함합니다"
#: pretix/base/datasync/sourcefields.py:428
#, fuzzy
#| msgid "Short form"
msgid "Event short form"
msgstr "이벤트 짧은 형식"
msgstr "짧은 형식"
#: pretix/base/datasync/sourcefields.py:437 pretix/base/exporters/events.py:57
#: pretix/base/exporters/orderlist.py:263
@@ -1081,8 +1141,10 @@ msgid "Order code and position number"
msgstr "주문 코드 및 위치 번호"
#: pretix/base/datasync/sourcefields.py:482
#, fuzzy
#| msgid "Ticket code"
msgid "Ticket price"
msgstr "티켓 가격"
msgstr "티켓 코드"
#: pretix/base/datasync/sourcefields.py:491 pretix/base/notifications.py:204
#: pretix/control/forms/filter.py:216 pretix/control/forms/modelimport.py:90
@@ -1090,16 +1152,22 @@ msgid "Order status"
msgstr "주문 상태"
#: pretix/base/datasync/sourcefields.py:500
#, fuzzy
#| msgid "Device status"
msgid "Ticket status"
msgstr "티켓 상태"
msgstr "기기 상태"
#: pretix/base/datasync/sourcefields.py:509
#, fuzzy
#| msgid "Purchase date and time"
msgid "Order date and time"
msgstr "구매 날짜 및 시간"
#: pretix/base/datasync/sourcefields.py:518
#, fuzzy
#| msgid "Printing date and time"
msgid "Payment date and time"
msgstr "결제 날짜 및 시간"
msgstr "인쇄 날짜 및 시간"
#: pretix/base/datasync/sourcefields.py:527
#: pretix/base/exporters/orderlist.py:272
@@ -1110,27 +1178,35 @@ msgid "Order locale"
msgstr "주문 지역 설정"
#: pretix/base/datasync/sourcefields.py:536
#, fuzzy
#| msgid "Order position"
msgid "Order position ID"
msgstr "주문 위치 ID"
msgstr "주문 위치"
#: pretix/base/datasync/sourcefields.py:545
#: pretix/base/exporters/orderlist.py:292
#, fuzzy
#| msgid "Order time"
msgid "Order link"
msgstr "주문 링크"
msgstr "주문 시간"
#: pretix/base/datasync/sourcefields.py:560
#, fuzzy
#| msgid "Ticket design"
msgid "Ticket link"
msgstr "티켓 링크"
msgstr "티켓 디자인"
#: pretix/base/datasync/sourcefields.py:578
#, python-brace-format
#, fuzzy, python-brace-format
#| msgid "Check-in list {val}"
msgid "Check-in datetime on list {}"
msgstr "{} 리스트에 있는 체크인 날짜와 시간"
msgstr "체크인 목록 {val}"
#: pretix/base/datasync/sourcefields.py:590
#, python-brace-format
#, fuzzy, python-brace-format
#| msgid "Question {val}"
msgid "Question: {name}"
msgstr "질문: {name}"
msgstr "질문 {val}"
#: pretix/base/datasync/sourcefields.py:604
#: pretix/base/datasync/sourcefields.py:614 pretix/base/settings.py:3642
@@ -2346,9 +2422,9 @@ msgid "Fees"
msgstr "수수료"
#: pretix/base/exporters/orderlist.py:277
#, python-brace-format
#, fuzzy, python-brace-format
msgid "Gross at {rate} % tax"
msgstr "세율 {rate}%의 세금으로 총합"
msgstr "세율{%}의 세금으로 총합"
#: pretix/base/exporters/orderlist.py:278
#, python-brace-format
@@ -2396,9 +2472,9 @@ msgid "External customer ID"
msgstr "외부고객 아이디"
#: pretix/base/exporters/orderlist.py:296
#, python-brace-format
#, fuzzy, python-brace-format
msgid "Paid by {method}"
msgstr "{method}에 의해 결제됨"
msgstr "{방법}에 의해 결제됨"
#: pretix/base/exporters/orderlist.py:458
#: pretix/base/exporters/orderlist.py:914
@@ -2601,8 +2677,10 @@ msgid "Check-in lists"
msgstr "체크인 목록"
#: pretix/base/exporters/orderlist.py:672
#, fuzzy
#| msgid "Additional footer link"
msgid "Position order link"
msgstr "주문 링크 위치"
msgstr "추가 하단 링크"
#: pretix/base/exporters/orderlist.py:841
msgid "Order transaction data"
@@ -3242,13 +3320,15 @@ msgid "Repeat password"
msgstr "반복 비밀번호"
#: pretix/base/forms/auth.py:220 pretix/base/forms/user.py:99
#, fuzzy
#| msgid "Email address"
msgid "Your email address"
msgstr "당신의 이메일 주소"
msgstr "이메일 주소"
#: pretix/base/forms/auth.py:327 pretix/control/forms/orders.py:1041
#: pretix/control/templates/pretixcontrol/shredder/download.html:53
msgid "Confirmation code"
msgstr "확인 코드"
msgstr ""
#: pretix/base/forms/questions.py:137 pretix/base/forms/questions.py:264
msgctxt "name_salutation"
@@ -3331,12 +3411,14 @@ msgstr ""
"야 할 수도 있습니다."
#: pretix/base/forms/questions.py:1181
#, fuzzy
#| msgid "Cancellation requested"
msgid "No invoice requested"
msgstr "청구서 요청되지 않음"
msgstr "취소 요청"
#: pretix/base/forms/questions.py:1183
msgid "Invoice transmission method"
msgstr "청구서 전송 방식"
msgstr ""
#: pretix/base/forms/questions.py:1329
msgid "You need to provide a company name."
@@ -3350,20 +3432,21 @@ msgstr "이름을 입력해야 합니다."
msgid ""
"If you enter an invoice address, you also need to select an invoice "
"transmission method."
msgstr "당신이 청구서 주소를 입력하신다면, 청구서 수신 방법도 선택하셔야 합니다."
msgstr ""
#: pretix/base/forms/questions.py:1385
#, fuzzy
#| msgid "The selected media type is not enabled in your organizer settings."
msgid ""
"The selected transmission type is not available in your country or for your "
"type of address."
msgstr "선택한 전송 유형은 당신의 국가 또는 지역에서 이용하실 수 없습니다."
msgstr "선택한 미디어 유형이 정리함 설정에서 활성화되지 않았습니다."
#: pretix/base/forms/questions.py:1394
msgid ""
"The selected type of invoice transmission requires a field that is currently "
"not available, please reach out to the organizer."
msgstr "선택하신 청구서 전송 유형은 현재 사용할 수 없는 필드의 입력을 필요로 하니, 주"
"최자에게 문의 해 주세요."
msgstr ""
#: pretix/base/forms/questions.py:1398
msgid "This field is required for the selected type of invoice transmission."
@@ -3383,8 +3466,10 @@ msgstr ""
"대가 대신 사용됩니다."
#: pretix/base/forms/user.py:77
#, fuzzy
#| msgid "Attendee email address"
msgid "Change email address"
msgstr "이메일 주소 변경"
msgstr "참석자 이메일 주소"
#: pretix/base/forms/user.py:83
msgid "Device name"
@@ -3433,12 +3518,16 @@ msgstr ""
"이 이메일 주소와 관련된 계정이 이미 있습니다. 다른 계정을 선택해 주세요."
#: pretix/base/forms/user.py:179
#, fuzzy
#| msgid "Email address"
msgid "Old email address"
msgstr "이전 이메일 주소"
msgstr "이메일 주소"
#: pretix/base/forms/user.py:180
#, fuzzy
#| msgid "Email address"
msgid "New email address"
msgstr "이메일 주소"
msgstr "이메일 주소"
#: pretix/base/forms/validators.py:51
msgid ""
@@ -3483,22 +3572,23 @@ msgstr "개별 고객"
#: pretix/base/invoicing/email.py:50
msgid "Email invoice directly to accounting department"
msgstr "청구서를 회계 부서로 이메일로 바로 보내기"
msgstr ""
#: pretix/base/invoicing/email.py:51
msgid ""
"If not selected, the invoice will be sent to you using the email address "
"listed above."
msgstr "선택되지 않은 경우, 청구서는 위의 이메일 주소를 통해 당신에게 보내질 것 입니"
"다."
msgstr ""
#: pretix/base/invoicing/email.py:55
#, fuzzy
#| msgid "Email address verified"
msgid "Email address for invoice"
msgstr "청구서용 이메일 주소"
msgstr "이메일 주소 확인"
#: pretix/base/invoicing/email.py:91
msgid "PDF via email"
msgstr "이메일로 PDF"
msgstr ""
#: pretix/base/invoicing/national.py:37
msgctxt "italian_invoice"
@@ -3523,9 +3613,11 @@ msgid "Address for certified electronic mail"
msgstr ""
#: pretix/base/invoicing/national.py:57
#, fuzzy
#| msgid "Recipient"
msgctxt "italian_invoice"
msgid "Recipient code"
msgstr "영수증 코드"
msgstr "영수증"
#: pretix/base/invoicing/national.py:81
msgctxt "italian_invoice"
@@ -3605,6 +3697,7 @@ msgid ""
"until {to_date}"
msgstr ""
"{from_date}\n"
"\n"
"{to_date}까지"
#: pretix/base/invoicing/pdf.py:609 pretix/base/services/mail.py:512
@@ -3684,10 +3777,10 @@ msgid "Single price: {net_price} net / {gross_price} gross"
msgstr "단일 가격: {net_price} 순 / {gross_price} 총합"
#: pretix/base/invoicing/pdf.py:901
#, python-brace-format
#, fuzzy, python-brace-format
msgctxt "invoice"
msgid "Single price: {price}"
msgstr "단일 가격: {price}"
msgstr "단일 가격: {가격}"
#: pretix/base/invoicing/pdf.py:944 pretix/base/invoicing/pdf.py:949
msgctxt "invoice"
@@ -3715,10 +3808,12 @@ msgid "Remaining amount"
msgstr "잔액"
#: pretix/base/invoicing/pdf.py:1009
#, python-brace-format
#, fuzzy, python-brace-format
#| msgctxt "invoice"
#| msgid "Event date: {date_range}"
msgctxt "invoice"
msgid "Invoice period: {daterange}"
msgstr "청구서 기간: {daterange}"
msgstr "이벤트 날짜: {date_range}"
#: pretix/base/invoicing/pdf.py:1040
msgctxt "invoice"
@@ -3741,12 +3836,13 @@ msgid "Included taxes"
msgstr "세금 포함"
#: pretix/base/invoicing/pdf.py:1100
#, python-brace-format
#, fuzzy, python-brace-format
msgctxt "invoice"
msgid ""
"Using the conversion rate of 1:{rate} as published by the {authority} on "
"{date}, this corresponds to:"
msgstr "{date}에 {authority}에서 발표한 1:{rate}의 변환율을 사용하면 다음과 같습니다:"
msgstr ""
"{날짜}에 {당국}에서 발표한 1:{세율}의 변환율을 사용하면 다음과 같습니다:"
#: pretix/base/invoicing/pdf.py:1115
#, fuzzy, python-brace-format
@@ -3754,8 +3850,9 @@ msgctxt "invoice"
msgid ""
"Using the conversion rate of 1:{rate} as published by the {authority} on "
"{date}, the invoice total corresponds to {total}."
msgstr "{date}에 {authority}에서 게시한 1:{rate}의 변환율을 사용하면 송장 총액이 "
"{total}에 해당합니다."
msgstr ""
"{날짜}에 {당국}에서 게시한 1:{세율}의 변환율을 사용하면 송장 총액이 {총합}에 "
"해당합니다."
#: pretix/base/invoicing/pdf.py:1129
msgid "Default invoice renderer (European-style letter)"

View File

@@ -7,16 +7,16 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-27 13:57+0000\n"
"PO-Revision-Date: 2025-12-08 07:00+0000\n"
"Last-Translator: Ruud Hendrickx <ruud@leckxicon.eu>\n"
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix/nl/>"
"\n"
"PO-Revision-Date: 2025-10-26 18:00+0000\n"
"Last-Translator: Jan Van Haver <jan.van.haver@gmail.com>\n"
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix/nl/"
">\n"
"Language: nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.14.3\n"
"X-Generator: Weblate 5.14\n"
#: pretix/_base_settings.py:87
msgid "English"
@@ -44,7 +44,7 @@ msgstr "Catalaans"
#: pretix/_base_settings.py:93
msgid "Chinese (simplified)"
msgstr "Chinees (vereenvoudigd)"
msgstr "Chinees (versimpeld)"
#: pretix/_base_settings.py:94
msgid "Chinese (traditional)"

View File

@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-27 13:57+0000\n"
"PO-Revision-Date: 2025-12-08 07:00+0000\n"
"Last-Translator: Ruud Hendrickx <ruud@leckxicon.eu>\n"
"PO-Revision-Date: 2024-02-13 16:00+0000\n"
"Last-Translator: Wessel Stam <info@wesselstam.nl>\n"
"Language-Team: Dutch (informal) <https://translate.pretix.eu/projects/pretix/"
"pretix/nl_Informal/>\n"
"Language: nl_Informal\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.14.3\n"
"X-Generator: Weblate 5.3.1\n"
#: pretix/_base_settings.py:87
msgid "English"
@@ -37,15 +37,15 @@ msgstr "Arabisch"
#: pretix/_base_settings.py:91
msgid "Basque"
msgstr "Baskisch"
msgstr ""
#: pretix/_base_settings.py:92
msgid "Catalan"
msgstr "Catalaans"
msgstr ""
#: pretix/_base_settings.py:93
msgid "Chinese (simplified)"
msgstr "Chinees (vereenvoudigd)"
msgstr "Chinees (versimpeld)"
#: pretix/_base_settings.py:94
msgid "Chinese (traditional)"
@@ -57,7 +57,7 @@ msgstr "Tsjechisch"
#: pretix/_base_settings.py:96
msgid "Croatian"
msgstr "Kroatisch"
msgstr ""
#: pretix/_base_settings.py:97
msgid "Danish"
@@ -89,7 +89,7 @@ msgstr "Grieks"
#: pretix/_base_settings.py:104
msgid "Hebrew"
msgstr "Hebreeuws"
msgstr ""
#: pretix/_base_settings.py:105
msgid "Indonesian"
@@ -101,7 +101,7 @@ msgstr "Italiaans"
#: pretix/_base_settings.py:107
msgid "Japanese"
msgstr "Japans"
msgstr ""
#: pretix/_base_settings.py:108
msgid "Latvian"
@@ -133,11 +133,11 @@ msgstr "Russisch"
#: pretix/_base_settings.py:115
msgid "Slovak"
msgstr "Slowaaks"
msgstr ""
#: pretix/_base_settings.py:116
msgid "Swedish"
msgstr "Zweeds"
msgstr ""
#: pretix/_base_settings.py:117
msgid "Spanish"
@@ -145,7 +145,7 @@ msgstr "Spaans"
#: pretix/_base_settings.py:118
msgid "Spanish (Latin America)"
msgstr "Spaans (Latijns-Amerika)"
msgstr ""
#: pretix/_base_settings.py:119
msgid "Turkish"
@@ -261,7 +261,7 @@ msgstr ""
#: pretix/api/serializers/event.py:234 pretix/api/serializers/event.py:554
#, python-brace-format
msgid "Meta data property '{name}' does not exist."
msgstr "Metadata-eigenschap '{name}' bestaat niet."
msgstr "Metadataeigenschap '{name}' bestaat niet."
#: pretix/api/serializers/event.py:237 pretix/api/serializers/event.py:557
#, python-brace-format
@@ -32970,7 +32970,7 @@ msgstr ""
#: pretix/plugins/reports/exporters.py:257
msgctxt "export_category"
msgid "Analysis"
msgstr "Analyse"
msgstr ""
#: pretix/plugins/reports/accountingreport.py:83
#, fuzzy

View File

@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-27 13:57+0000\n"
"PO-Revision-Date: 2025-12-11 01:00+0000\n"
"PO-Revision-Date: 2025-09-10 05:00+0000\n"
"Last-Translator: Renne Rocha <renne@rocha.dev.br>\n"
"Language-Team: Portuguese (Brazil) <https://translate.pretix.eu/projects/"
"pretix/pretix/pt_BR/>\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 5.14.3\n"
"X-Generator: Weblate 5.13.2\n"
#: pretix/_base_settings.py:87
msgid "English"
@@ -253,8 +253,9 @@ msgid ""
"Events cannot be created as 'live'. Quotas and payment must be added to the "
"event before sales can go live."
msgstr ""
"Eventos não podem ser criados como 'ativo'. Cotas e métodos de pagamento "
"devem ser adicionados ao evento antes que as vendas se iniciem."
"Os eventos não podem ser criados como 'ativo'. As cotas e métodos de "
"pagamento devem ser adicionados ao evento antes que as vendas possam ser "
"iniciadas."
#: pretix/api/serializers/event.py:234 pretix/api/serializers/event.py:554
#, python-brace-format
@@ -357,8 +358,7 @@ msgstr ""
#: pretix/api/serializers/item.py:587 pretix/control/forms/item.py:177
msgid "Question cannot depend on a question asked during check-in."
msgstr ""
"A pergunta não pode depender de outra pergunta feita durante o check-in."
msgstr "A pergunta não pode depender de uma pergunta feita durante o check-in."
#: pretix/api/serializers/item.py:592 pretix/control/forms/item.py:182
msgid "Circular dependency between questions detected."
@@ -816,22 +816,28 @@ msgstr ""
"confirme primeiro o endereço de email na sua conta."
#: pretix/base/datasync/datasync.py:263
#, python-brace-format
#, fuzzy, python-brace-format
#| msgid ""
#| "Field \"{field_name}\" is not valid for {available_inputs}. Please check "
#| "your {provider_name} settings."
msgid ""
"Field \"{field_name}\" does not exist. Please check your {provider_name} "
"settings."
msgstr ""
"O campo \"{field_name}\" não existe. Por favor, verifique as configurações "
"de {provider_name}."
"O campo \"{field_name}\" não é válido para {available_inputs}. Verifique as "
"configurações de {provider_name}."
#: pretix/base/datasync/datasync.py:270
#, python-brace-format
#, fuzzy, python-brace-format
#| msgid ""
#| "Field \"{field_name}\" is not valid for {available_inputs}. Please check "
#| "your {provider_name} settings."
msgid ""
"Field \"{field_name}\" requires {required_input}, but only got "
"{available_inputs}. Please check your {provider_name} settings."
msgstr ""
"Campo \"{field_name}\" exige {required_input}, mas apenas {available_inputs} "
"foram fornecidas. Por favor, verifique as configurações de {provider_name}."
"O campo \"{field_name}\" não é válido para {available_inputs}. Verifique as "
"configurações de {provider_name}."
#: pretix/base/datasync/datasync.py:281
#, python-brace-format
@@ -2131,7 +2137,7 @@ msgstr "Comprar este produto requer aprovação"
#: pretix/base/exporters/items.py:85 pretix/base/models/items.py:627
msgid "Only sell this product as part of a bundle"
msgstr "Venda este produto apenas como parte de um pacote"
msgstr "Disponível apenas como parte de um pacote"
#: pretix/base/exporters/items.py:86 pretix/base/models/items.py:634
msgid "Allow product to be canceled or changed"
@@ -2833,7 +2839,7 @@ msgstr "Código de Status"
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_assign.html:25
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/transaction_list.html:13
msgid "Amount"
msgstr "Quantidade"
msgstr "Valor"
#: pretix/base/exporters/orderlist.py:1098
#: pretix/control/templates/pretixcontrol/boxoffice/payment.html:102
@@ -3280,8 +3286,10 @@ msgid "Repeat password"
msgstr "Repita a senha"
#: pretix/base/forms/auth.py:220 pretix/base/forms/user.py:99
#, fuzzy
#| msgid "Email address"
msgid "Your email address"
msgstr "Seu endereço de e-mail"
msgstr "Endereço de email"
#: pretix/base/forms/auth.py:327 pretix/control/forms/orders.py:1041
#: pretix/control/templates/pretixcontrol/shredder/download.html:53
@@ -3392,8 +3400,6 @@ msgid ""
"If you enter an invoice address, you also need to select an invoice "
"transmission method."
msgstr ""
"Se você informar um endereço para a fatura, você também deverá selecionar um "
"método para transmissão da fatura."
#: pretix/base/forms/questions.py:1385
msgid ""
@@ -3408,8 +3414,6 @@ msgid ""
"The selected type of invoice transmission requires a field that is currently "
"not available, please reach out to the organizer."
msgstr ""
"O tipo de transmissão de fatura selecionado exige um campo que não está "
"disponível atualmente. Por favor, entre em contato com a organização."
#: pretix/base/forms/questions.py:1398
msgid "This field is required for the selected type of invoice transmission."
@@ -3429,8 +3433,10 @@ msgstr ""
"as exibições de eventos, o fuso horário do evento é usado."
#: pretix/base/forms/user.py:77
#, fuzzy
#| msgid "Attendee email address"
msgid "Change email address"
msgstr "Alterar endereço de e-mail"
msgstr "Email do participante"
#: pretix/base/forms/user.py:83
msgid "Device name"
@@ -3480,12 +3486,16 @@ msgstr ""
"um diferente."
#: pretix/base/forms/user.py:179
#, fuzzy
#| msgid "Email address"
msgid "Old email address"
msgstr "Endereço de e-mail antigo"
msgstr "Endereço de email"
#: pretix/base/forms/user.py:180
#, fuzzy
#| msgid "Email address"
msgid "New email address"
msgstr "Endereço de e-mail novo"
msgstr "Endereço de email"
#: pretix/base/forms/validators.py:51
msgid ""
@@ -3530,17 +3540,22 @@ msgid "Individual customer"
msgstr "Cliente pessoa física"
#: pretix/base/invoicing/email.py:50
#, fuzzy
#| msgid ""
#| "To send the invoice directly to your accounting department, please enter "
#| "their email address:"
msgid "Email invoice directly to accounting department"
msgstr ""
"Enviar e-mail com a fatura diretamente para departamento de contabilidade"
"Para enviar a fatura diretamente ao seu departamento de contabilidade, "
"insira o endereço de e-mail:"
#: pretix/base/invoicing/email.py:51
#, fuzzy
#| msgid "The invoice was sent to the designated email address."
msgid ""
"If not selected, the invoice will be sent to you using the email address "
"listed above."
msgstr ""
"Se não selecionado, a fatura será enviada para o endereço de e-mail listado "
"a seguir."
msgstr "A fatura foi enviada para o endereço de e-mail designado."
#: pretix/base/invoicing/email.py:55
msgid "Email address for invoice"
@@ -3763,10 +3778,12 @@ msgid "Remaining amount"
msgstr "Valor restante"
#: pretix/base/invoicing/pdf.py:1009
#, python-brace-format
#, fuzzy, python-brace-format
#| msgctxt "invoice"
#| msgid "Event date: {date_range}"
msgctxt "invoice"
msgid "Invoice period: {daterange}"
msgstr "Período da fatura: {daterange}"
msgstr "Data do evento: {date_range}"
#: pretix/base/invoicing/pdf.py:1040
msgctxt "invoice"
@@ -3851,9 +3868,12 @@ msgid "Peppol participant ID"
msgstr ""
#: pretix/base/invoicing/peppol.py:170
#, fuzzy
#| msgctxt "italian_invoice"
#| msgid "Fiscal code"
msgctxt "peppol_invoice"
msgid "Visual copy"
msgstr "Cópia visual"
msgstr "Código fiscal"
#: pretix/base/invoicing/peppol.py:175
msgctxt "peppol_invoice"
@@ -4337,8 +4357,10 @@ msgid ""
msgstr ""
#: pretix/base/models/auth.py:392
#, fuzzy
#| msgid "Confirmation code"
msgid "pretix confirmation code"
msgstr "código de confirmação do pretix"
msgstr "Código de confirmação"
#: pretix/base/models/auth.py:435
#: pretix/control/templates/pretixcontrol/auth/forgot.html:7
@@ -4840,11 +4862,11 @@ msgid ""
"you can also choose to use a random value. This will be used in URLs, order "
"codes, invoice numbers, and bank transfer references."
msgstr ""
"Deve conter apenas letras minúsculas, números, pontos e traços, sendo "
"exclusiva para suas eventos. Recomendamos uma abreviação ou data com menos "
"de 10 caracteres e que seja facilmente lembrada, mas você também pode usar "
"um valor aleatório. Esta informação será usada em URLs, códigos de pedido, "
"número de faturas e referências de transações bancárias."
"Deve ser curts, conter apenas letras minúsculas, números, pontos e traços, e "
"ser exclusiva entre seus eventos. Recomendamos algum tipo de abreviação ou "
"uma data com menos de 10 caracteres que sejam facilmente lembradas, mas você "
"também pode usar um valor aleatório. Esta informação será usada em URLs, "
"códigos de pedido, números de fatura e referências de transferência bancária."
#: pretix/base/models/event.py:607 pretix/base/models/organizer.py:89
msgid "The slug may only contain letters, numbers, dots and dashes."
@@ -5372,16 +5394,24 @@ msgstr ""
"não tiver variações, este preço será usado."
#: pretix/base/models/items.py:506
#, fuzzy
#| msgid ""
#| "If this option is active, your users can choose the price themselves. The "
#| "price configured above is then interpreted as the minimum price a user "
#| "has to enter. You could use this e.g. to collect additional donations for "
#| "your event. This is currently not supported for products that are bought "
#| "as an add-on to other products."
msgid ""
"If this option is active, your users can choose the price themselves. The "
"price configured above is then interpreted as the minimum price a user has "
"to enter. You could use this e.g. to collect additional donations for your "
"event."
msgstr ""
"Se esta opção está selecionada, os usuários poderão pagar o preço que "
"desejarem. O preço padrão acima será interpretado como o preço mínimo que o "
"usuário deverá pagar. Você pode usar esta opção, por exemplo, para coletar "
"doações adicionais para o seu evento."
"Se esta opção estiver ativa, seus usuários podem escolher o próprio preço. O "
"preço configurado acima é então interpretado como o preço mínimo que um "
"usuário deve inserir. Você pode usar isso, por exemplo, para coletar doações "
"adicionais para o seu evento. No momento, isto não é suportado por produtos "
"comprados como um complemento de outros produtos."
#: pretix/base/models/items.py:511 pretix/base/models/items.py:1175
msgid "Suggested price"
@@ -6268,6 +6298,9 @@ msgstr ""
"insira um valor possível por linha."
#: pretix/base/models/items.py:2310
#, fuzzy
#| msgctxt "timeframe"
#| msgid "Start"
msgid "Start"
msgstr "Início"
@@ -7879,8 +7912,10 @@ msgid "123.45 EUR"
msgstr "123.45 BRL"
#: pretix/base/pdf.py:166
#, fuzzy
#| msgid "Price including add-ons"
msgid "Price including bundled products"
msgstr "Preços incluindo produtos empacotados"
msgstr "Preço incluindo complementos"
#: pretix/base/pdf.py:175
#, fuzzy
@@ -9843,8 +9878,10 @@ msgid "Require a phone number per order"
msgstr "Exigir um número de telefone por pedido"
#: pretix/base/settings.py:482
#, fuzzy
#| msgid "including all taxes"
msgid "Rounding of taxes"
msgstr "Arredondamento dos impostos"
msgstr "incluindo todos os impostos"
#: pretix/base/settings.py:486
msgid ""
@@ -10387,12 +10424,18 @@ msgid "Automatic, but prefer invoice date over event date"
msgstr ""
#: pretix/base/settings.py:1142 pretix/base/settings.py:1153
#, fuzzy
#| msgctxt "invoice"
#| msgid "Invoice date"
msgid "Invoice date"
msgstr "Data da fatura"
#: pretix/base/settings.py:1146
#, fuzzy
#| msgctxt "subevent"
#| msgid "Date ordering"
msgid "Date of service"
msgstr "Data do serviço"
msgstr "Ordenação de datas"
#: pretix/base/settings.py:1155
msgid ""
@@ -11256,8 +11299,8 @@ msgstr "Endereço de contato"
#: pretix/base/settings.py:2161 pretix/control/forms/event.py:1824
msgid "We'll show this publicly to allow attendees to contact you."
msgstr ""
"Será exibido publicamente para que participantes possam entrar em contato "
"com você."
"Mostraremos isso publicamente para permitir que os participantes entrem em "
"contato com você."
#: pretix/base/settings.py:2169 pretix/control/forms/event.py:1816
msgid "Imprint URL"
@@ -12123,7 +12166,17 @@ msgid "Invoice {invoice_number}"
msgstr "Fatura {invoice_number}"
#: pretix/base/settings.py:2789
#, python-brace-format
#, fuzzy, python-brace-format
#| msgid ""
#| "Hello,\n"
#| "\n"
#| "somebody requested a list of your orders for {event}.\n"
#| "The list is as follows:\n"
#| "\n"
#| "{orders}\n"
#| "\n"
#| "Best regards, \n"
#| "Your {event} team"
msgid ""
"Hello,\n"
"\n"
@@ -12136,12 +12189,12 @@ msgid ""
msgstr ""
"Olá,\n"
"\n"
"Em anexo você encontrará uma nova fatura para o pedido {code} para {event}. "
"Este pedido foi feito por {order_email}.\n"
"Segue anexo uma nova fatura para o pedido {code} para {evento} . Este pedido "
"foi feito por {order_email}.\n"
"\n"
"Atenciosamente, \n"
"\n"
"Organização {event}"
"Equipe organizadora de {event}"
#: pretix/base/settings.py:2807 pretix/base/settings.py:2823
#, python-brace-format
@@ -13203,13 +13256,13 @@ msgid "Contact:"
msgstr "Contato:"
#: pretix/base/templates/pretixbase/email/order_details.html:54
#, python-format
#, fuzzy, python-format
#| msgid ""
#| "You are receiving this email because you placed an order for {event}."
msgid ""
"You are receiving this email because you placed an order for "
"<strong>%(event)s</strong>."
msgstr ""
"Você está recebendo este email por ter realizado um pedido para <strong>%"
"(event)s</strong>."
msgstr "Você está recebendo este email porque fez um pedido para {event}."
#: pretix/base/templates/pretixbase/email/order_details.html:93
#: pretix/control/templates/pretixcontrol/organizers/customer.html:23
@@ -13884,7 +13937,7 @@ msgstr "Padrão ({value})"
#: pretix/control/forms/event.py:380
msgid "The currency cannot be changed because orders already exist."
msgstr "A moeda não pode ser alterada pois já foram realizados pedidos."
msgstr ""
#: pretix/control/forms/event.py:391 pretix/control/forms/event.py:404
msgid "Domain"
@@ -13970,16 +14023,21 @@ msgstr ""
"que o cartão-presente é emitido."
#: pretix/control/forms/event.py:813
#, fuzzy
#| msgid "including all taxes"
msgid "Prices including tax"
msgstr "Preços incluindo impostos"
msgstr "incluindo todos os impostos"
#: pretix/control/forms/event.py:814
msgid "Recommended if you sell tickets at least partly to consumers."
msgstr ""
#: pretix/control/forms/event.py:818
#, fuzzy
#| msgctxt "reporting_timeframe"
#| msgid "All future (excluding today)"
msgid "Prices excluding tax"
msgstr "Preços excluindo impostos"
msgstr "Todos os futuros (excluindo hoje)"
#: pretix/control/forms/event.py:819
msgid "Recommended only if you sell tickets primarily to business customers."
@@ -17343,14 +17401,19 @@ msgid "Your account has been disabled."
msgstr "Sua conta foi desativada."
#: pretix/control/logdisplay.py:672
#, python-brace-format
#, fuzzy, python-brace-format
#| msgid ""
#| "The email address has been changed from \"{old_email}\" to \"{new_email}"
#| "\"."
msgid "Your email address has been changed from {old_email} to {email}."
msgstr "Seu endereço de email foi modificado de {old_email} para {email}."
msgstr ""
"O endereço de e-mail foi alterado de \"{old_email}\" para \"{new_email}\"."
#: pretix/control/logdisplay.py:673
#, python-brace-format
#, fuzzy, python-brace-format
#| msgid "Your email address has been updated."
msgid "Your email address {email} has been confirmed."
msgstr "Seu endereço de e-mail {email} foi confirmado."
msgstr "Seu endereço de email foi atualizado."
#: pretix/control/logdisplay.py:685
#, python-brace-format
@@ -19627,20 +19690,6 @@ msgid ""
"Best regards,\n"
"Your pretix team\n"
msgstr ""
"Olá,\n"
"\n"
"%(reason)s\n"
"\n"
" %(code)s\n"
"\n"
"Por favor, nunca forneça este código para outra pessoa. Nosso time de "
"suporte nunca irá solicitar este código.\n"
"\n"
"Se você não solicitou este código, por favor entre em contato conosco "
"imediatamente.\n"
"\n"
"Atenciosamente,\n"
"Time pretix\n"
#: pretix/control/templates/pretixcontrol/email/email_setup.txt:1
#, python-format
@@ -21265,8 +21314,10 @@ msgid "with custom rules"
msgstr "Regras personalizadas"
#: pretix/control/templates/pretixcontrol/event/tax.html:110
#, fuzzy
#| msgid "Base settings"
msgid "Tax settings"
msgstr "Configurações de impostos"
msgstr "Configurações base"
#: pretix/control/templates/pretixcontrol/event/tax_delete.html:4
#: pretix/control/templates/pretixcontrol/event/tax_delete.html:6
@@ -21929,9 +21980,6 @@ msgid ""
"your event. By default, we will only offer ticket downloads for these "
"products."
msgstr ""
"Cada compra deste produto representa uma pessoa que terá permissão de entrar "
"no seu evento. Por padrão, só oferecemos o download de ingressos para estes "
"produtos."
#: pretix/control/templates/pretixcontrol/item/create.html:33
#: pretix/control/templates/pretixcontrol/item/index.html:41
@@ -26096,7 +26144,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/shredder/download.html:29
msgid "Download data"
msgstr "Baixar dados"
msgstr ""
#: pretix/control/templates/pretixcontrol/shredder/download.html:34
msgid "Step 2: Confirm deletion"
@@ -26427,7 +26475,7 @@ msgstr "Adicionar um dispositivo de autenticação de dois fatores"
#: pretix/control/templates/pretixcontrol/user/2fa_confirm_totp.html:8
msgid "To set up this device, please follow the following steps:"
msgstr "Para configurar este dispositivo, por favor siga os passos a seguir:"
msgstr ""
#: pretix/control/templates/pretixcontrol/user/2fa_confirm_totp.html:12
msgid "Download the Google Authenticator application to your phone:"
@@ -26853,11 +26901,11 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/user/staff_session_edit.html:6
msgid "Session notes"
msgstr "Notas de sessão"
msgstr ""
#: pretix/control/templates/pretixcontrol/user/staff_session_edit.html:17
msgid "Audit log"
msgstr "Log de auditoria"
msgstr ""
#: pretix/control/templates/pretixcontrol/user/staff_session_edit.html:30
msgid "Method"
@@ -27901,7 +27949,7 @@ msgstr ""
#: pretix/control/views/item.py:237
msgid "The selected category has been deleted."
msgstr "A categoria selecionada foi excluída"
msgstr ""
#: pretix/control/views/item.py:322
msgid "The new category has been created."
@@ -29826,7 +29874,7 @@ msgstr ""
#: pretix/plugins/banktransfer/payment.py:239
msgid "Please fill out your bank account details."
msgstr "Por favor, preencha os detalhes da sua conta bancária."
msgstr ""
#: pretix/plugins/banktransfer/payment.py:243
msgid "Please enter your bank account details."
@@ -30127,7 +30175,7 @@ msgstr ""
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/pending.html:122
msgid "Scan the QR code with your banking app"
msgstr "Escanear o QR Code com o seu aplicativo bancário"
msgstr ""
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/refund_export.html:5
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/refund_export.html:7
@@ -30698,8 +30746,6 @@ msgid ""
"We're waiting for an answer from PayPal regarding your payment. Please "
"contact us, if this takes more than a few hours."
msgstr ""
"Estamos aguardando por informações do PayPal referentes ao seu pagamento. "
"Por favor, entre em contato conosco se isso demorar mais que algumas horas."
#: pretix/plugins/paypal/templates/pretixplugins/paypal/redirect.html:17
#: pretix/plugins/paypal2/templates/pretixplugins/paypal2/redirect.html:17
@@ -33324,17 +33370,19 @@ msgstr "Organizador: {organizer}"
#: pretix/presale/ical.py:139
#, python-brace-format
msgid "{event} - {item}"
msgstr "{event} - {item}"
msgstr ""
#: pretix/presale/ical.py:147
#, python-brace-format
#, fuzzy, python-brace-format
#| msgid "Start date"
msgid "Start: {datetime}"
msgstr "Início: {datetime}"
msgstr "Data inicial"
#: pretix/presale/ical.py:150
#, python-brace-format
#, fuzzy, python-brace-format
#| msgid "Admission: {datetime}"
msgid "End: {datetime}"
msgstr "Término: {datetime}"
msgstr "Admissão: {datetime}"
#: pretix/presale/templates/pretixpresale/base.html:44
#, fuzzy
@@ -33726,11 +33774,12 @@ msgstr "Selecione como deseja pagar o saldo restante:"
#: pretix/presale/templates/pretixpresale/event/checkout_payment.html:82
#: pretix/presale/templates/pretixpresale/event/order_pay_change.html:45
#, python-format
#, fuzzy, python-format
#| msgid "%(num)s available"
msgid "(%(count)s available)"
msgid_plural "(%(count)s available)"
msgstr[0] "(%(count)s disponível)"
msgstr[1] "(%(count)s disponíveis)"
msgstr[0] "%(num)s disponíveis"
msgstr[1] "%(num)s disponíveis"
#: pretix/presale/templates/pretixpresale/event/checkout_payment.html:101
msgid "This sales channel does not provide support for test mode."
@@ -34509,11 +34558,13 @@ msgstr "Mostrar imagem em tamanho real de %(item)s"
#: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:131
#: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:288
#, python-format
#, fuzzy, python-format
#| msgid "%(count)s event"
#| msgid_plural "%(count)s events"
msgid "%(amount)s× in your cart"
msgid_plural "%(amount)s× in your cart"
msgstr[0] "%(amount)s× no seu carrinho"
msgstr[1] "%(amount)s× no seu carrinho"
msgstr[0] "%(count)s evento"
msgstr[1] "%(count)s events"
#: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:209
#: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:374
@@ -35581,8 +35632,10 @@ msgid "The following gift cards are available in your customer account:"
msgstr ""
#: pretix/presale/templates/pretixpresale/giftcard/checkout.html:24
#, fuzzy
#| msgid "Issued gift cards"
msgid "Use gift card"
msgstr "Usar cartão-presente"
msgstr "Cartões-presente emitidos"
#: pretix/presale/templates/pretixpresale/giftcard/checkout_confirm.html:4
#, python-format
@@ -35599,7 +35652,7 @@ msgid ""
"This is a self-hosted installation of <a %(a_attr)s>pretix, your free and "
"open source ticket sales software</a>."
msgstr ""
"Esta é uma instalação auto-hospedada do <a %(a_attr)s>pretix, seu aplicativo "
"Está é uma instalação self-hosted do <a %(a_attr)s>pretix, seu aplicativo "
"livre e de código aberto para venda de ingressos.</a>."
#: pretix/presale/templates/pretixpresale/index.html:15
@@ -35695,18 +35748,22 @@ msgid "Issued on %(date)s"
msgstr "Leitura negada: %(date)s"
#: pretix/presale/templates/pretixpresale/organizers/customer_giftcards.html:38
#, python-format
#, fuzzy, python-format
#| msgid "Expired since"
msgid "Expired since %(date)s"
msgstr "Expirado desde %(date)s"
msgstr "Expirado desde"
#: pretix/presale/templates/pretixpresale/organizers/customer_giftcards.html:46
#, python-format
#, fuzzy, python-format
#| msgid "Valid until %(datetime)s"
msgid "Valid until %(date)s"
msgstr "Válido até %(date)s"
msgstr "Válido até %(datetime)s"
#: pretix/presale/templates/pretixpresale/organizers/customer_giftcards.html:66
#, fuzzy
#| msgid "Remaining balance"
msgid "Remaining value:"
msgstr "Valor restante:"
msgstr "Saldo restante"
#: pretix/presale/templates/pretixpresale/organizers/customer_giftcards.html:76
#, fuzzy
@@ -36230,8 +36287,10 @@ msgid "The selected date does not exist in this event series."
msgstr "A data selecionada não existe nesta série de eventos."
#: pretix/presale/views/widget.py:412
#, fuzzy
#| msgid "The selected seat \"{seat}\" is not available."
msgid "The selected date is not available."
msgstr "A data selecionada não está disponível."
msgstr "O assento selecionado \"{seat}\" não está disponível."
#: pretix/presale/views/widget.py:476
#, python-format

View File

@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-20 10:37+0000\n"
"PO-Revision-Date: 2025-12-09 00:47+0000\n"
"PO-Revision-Date: 2025-08-28 13:43+0000\n"
"Last-Translator: Renne Rocha <renne@rocha.dev.br>\n"
"Language-Team: Portuguese (Brazil) <https://translate.pretix.eu/projects/"
"pretix/pretix-js/pt_BR/>\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 5.14.3\n"
"X-Generator: Weblate 5.13\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -566,11 +566,11 @@ msgstr "ausente"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:289
msgid "Error: Product not found!"
msgstr "Erro: Produto não encontrado!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:296
msgid "Error: Variation not found!"
msgstr "Erro: Variação não encontrada!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:171
msgid "Check-in QR"

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-20 10:37+0000\n"
"PO-Revision-Date: 2025-12-10 15:49+0000\n"
"PO-Revision-Date: 2025-11-11 21:00+0000\n"
"Last-Translator: Ana Rute Pacheco Vivas <rute.vivas@om.org>\n"
"Language-Team: Portuguese (Portugal) <https://translate.pretix.eu/projects/"
"pretix/pretix-js/pt_PT/>\n"
@@ -798,7 +798,7 @@ msgstr ""
#: pretix/static/pretixpresale/js/ui/cart.js:90
msgid "Renew reservation"
msgstr "Renovar a reserva"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:194
msgid "The organizer keeps %(currency)s %(amount)s"

View File

@@ -8,36 +8,34 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-27 13:57+0000\n"
"PO-Revision-Date: 2025-12-14 00:00+0000\n"
"Last-Translator: Lachlan Struthers <lachlan.struthers@om.org>\n"
"Language-Team: Albanian <https://translate.pretix.eu/projects/pretix/pretix/"
"sq/>\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: sq\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.14.3\n"
#: pretix/_base_settings.py:87
msgid "English"
msgstr "Anglisht"
msgstr ""
#: pretix/_base_settings.py:88
msgid "German"
msgstr "Gjermanisht"
msgstr ""
#: pretix/_base_settings.py:89
msgid "German (informal)"
msgstr "Gjermanisht (joformale)"
msgstr ""
#: pretix/_base_settings.py:90
msgid "Arabic"
msgstr "Arabisht"
msgstr ""
#: pretix/_base_settings.py:91
msgid "Basque"
msgstr "Baskisht"
msgstr ""
#: pretix/_base_settings.py:92
msgid "Catalan"
@@ -452,7 +450,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/event/mail.html:114
#: pretix/control/views/orders.py:1569
msgid "Order canceled"
msgstr "Porositja ësthë anuluar"
msgstr ""
#: pretix/api/webhooks.py:278 pretix/base/notifications.py:257
msgid "Order reactivated"
@@ -1409,7 +1407,7 @@ msgstr ""
#: pretix/plugins/checkinlists/exporters.py:827
#: pretix/plugins/checkinlists/exporters.py:828
msgid "Yes"
msgstr "Po"
msgstr ""
#: pretix/base/exporters/customers.py:100
#: pretix/base/exporters/customers.py:101 pretix/base/exporters/events.py:83
@@ -1433,7 +1431,7 @@ msgstr "Po"
#: pretix/plugins/checkinlists/exporters.py:827
#: pretix/plugins/checkinlists/exporters.py:828
msgid "No"
msgstr "Jo"
msgstr ""
#: pretix/base/exporters/dekodi.py:42 pretix/base/exporters/invoices.py:66
msgctxt "export_category"
@@ -2447,7 +2445,7 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:12
#: pretix/presale/templates/pretixpresale/organizers/customer_membership.html:91
msgid "Product"
msgstr "Produkti"
msgstr ""
#: pretix/base/exporters/orderlist.py:619 pretix/base/models/vouchers.py:315
#: pretix/control/templates/pretixcontrol/vouchers/bulk.html:5
@@ -2785,7 +2783,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/event/cancel.html:20
#: pretix/control/views/item.py:971
msgid "Paid orders"
msgstr "Porositje të paguara"
msgstr ""
#: pretix/base/exporters/orderlist.py:1155 pretix/control/views/item.py:976
msgid "Pending orders"
@@ -2954,7 +2952,7 @@ msgstr ""
#: pretix/plugins/reports/accountingreport.py:105
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:67
msgid "All"
msgstr "Të gjitha"
msgstr ""
#: pretix/base/exporters/orderlist.py:1329 pretix/control/forms/filter.py:1450
msgid "Live"
@@ -3881,7 +3879,7 @@ msgstr ""
#: pretix/base/modelimport_vouchers.py:205 pretix/base/models/items.py:1256
#: pretix/base/models/vouchers.py:266 pretix/base/models/waitinglist.py:99
msgid "Product variation"
msgstr "Varianti i produktit"
msgstr ""
#: pretix/base/modelimport_orders.py:161
msgid "The variation can be specified by its internal ID or full name."
@@ -4280,19 +4278,19 @@ msgstr ""
#: pretix/base/models/checkin.py:336
msgid "Entry"
msgstr "Hyrje"
msgstr ""
#: pretix/base/models/checkin.py:337
msgid "Exit"
msgstr "Dalje"
msgstr ""
#: pretix/base/models/checkin.py:356
msgid "Unknown ticket"
msgstr "Biletë e panjohur"
msgstr ""
#: pretix/base/models/checkin.py:357
msgid "Ticket not paid"
msgstr "Bileta nuk është paguar"
msgstr ""
#: pretix/base/models/checkin.py:358
msgid "Forbidden by custom rule"
@@ -4300,23 +4298,23 @@ msgstr ""
#: pretix/base/models/checkin.py:359
msgid "Ticket code revoked/changed"
msgstr "Kodi i biletës është anuluar/ndryshuar"
msgstr ""
#: pretix/base/models/checkin.py:360
msgid "Information required"
msgstr "Informacion i domosdoshëm"
msgstr ""
#: pretix/base/models/checkin.py:361
msgid "Ticket already used"
msgstr "Bileta tashmë është përdorur"
msgstr ""
#: pretix/base/models/checkin.py:362
msgid "Ticket type not allowed here"
msgstr "Ky lloj bilete nuk lejohet këtu"
msgstr ""
#: pretix/base/models/checkin.py:363
msgid "Ticket code is ambiguous on list"
msgstr "Kodi i biletës është e paqartë në listë"
msgstr ""
#: pretix/base/models/checkin.py:364
msgid "Server error"
@@ -4324,15 +4322,15 @@ msgstr ""
#: pretix/base/models/checkin.py:365
msgid "Ticket blocked"
msgstr "Bileta u bllokua"
msgstr ""
#: pretix/base/models/checkin.py:366
msgid "Order not approved"
msgstr "Porositje nuk është aprovuar"
msgstr ""
#: pretix/base/models/checkin.py:367
msgid "Ticket not valid at this time"
msgstr "Bileta nuk është e vlefshme për momentin"
msgstr ""
#: pretix/base/models/checkin.py:368
msgid "Check-in annulled"
@@ -4462,7 +4460,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/organizers/gates.html:16
#: pretix/plugins/checkinlists/exporters.py:769
msgid "Gate"
msgstr "Porta"
msgstr ""
#: pretix/base/models/devices.py:131
#: pretix/control/templates/pretixcontrol/organizers/devices.html:83
@@ -5930,7 +5928,7 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/organizers/customer_membership.html:34
#: pretix/presale/templates/pretixpresale/organizers/customer_memberships.html:44
msgid "Canceled"
msgstr "e anuluar"
msgstr ""
#: pretix/base/models/memberships.py:134
#: pretix/control/templates/pretixcontrol/organizers/customer.html:117
@@ -6673,7 +6671,7 @@ msgstr ""
#: pretix/base/models/vouchers.py:204 pretix/control/views/vouchers.py:120
msgid "Redeemed"
msgstr "e përdorur"
msgstr ""
#: pretix/base/models/vouchers.py:209
msgid ""
@@ -7434,7 +7432,7 @@ msgstr ""
#: pretix/base/pdf.py:278 pretix/base/pdf.py:307
#: pretix/base/services/checkin.py:362 pretix/control/forms/filter.py:1271
msgid "Friday"
msgstr "E Premte"
msgstr ""
#: pretix/base/pdf.py:282
msgid "Event end date and time"
@@ -7706,15 +7704,15 @@ msgstr ""
#: pretix/base/reldate.py:38
msgid "Event start"
msgstr "Fillimi i eventit"
msgstr ""
#: pretix/base/reldate.py:39
msgid "Event end"
msgstr "Mbarimi i eventit"
msgstr ""
#: pretix/base/reldate.py:40
msgid "Event admission"
msgstr "Hyrja në event"
msgstr ""
#: pretix/base/reldate.py:41
msgid "Presale start"
@@ -8132,27 +8130,27 @@ msgstr ""
#: pretix/base/services/checkin.py:358 pretix/control/forms/filter.py:1267
msgid "Monday"
msgstr "E Hënë"
msgstr ""
#: pretix/base/services/checkin.py:359 pretix/control/forms/filter.py:1268
msgid "Tuesday"
msgstr "E Martë"
msgstr ""
#: pretix/base/services/checkin.py:360 pretix/control/forms/filter.py:1269
msgid "Wednesday"
msgstr "E Mërkurë"
msgstr ""
#: pretix/base/services/checkin.py:361 pretix/control/forms/filter.py:1270
msgid "Thursday"
msgstr "E Enjte"
msgstr ""
#: pretix/base/services/checkin.py:363 pretix/control/forms/filter.py:1272
msgid "Saturday"
msgstr "E Shtunë"
msgstr ""
#: pretix/base/services/checkin.py:364 pretix/control/forms/filter.py:1273
msgid "Sunday"
msgstr "E Diel"
msgstr ""
#: pretix/base/services/checkin.py:368
#, python-brace-format
@@ -12891,7 +12889,7 @@ msgstr ""
#: pretix/plugins/reports/exporters.py:391
#: pretix/presale/templates/pretixpresale/event/fragment_order_status.html:7
msgid "Approval pending"
msgstr "Duke pritur aprovimin"
msgstr ""
#: pretix/control/forms/filter.py:247
msgid "Follow-up configured"
@@ -13044,7 +13042,7 @@ msgstr ""
#: pretix/control/forms/filter.py:2052 pretix/control/forms/filter.py:2054
#: pretix/control/forms/filter.py:2620 pretix/control/forms/filter.py:2622
msgid "Search query"
msgstr "Kërkim"
msgstr ""
#: pretix/control/forms/filter.py:1528 pretix/control/forms/filter.py:1600
#: pretix/control/templates/pretixcontrol/organizers/customer.html:47
@@ -13159,7 +13157,7 @@ msgstr ""
#: pretix/control/forms/filter.py:2117
#: pretix/presale/templates/pretixpresale/organizers/customer_giftcards.html:51
msgid "Valid"
msgstr "e vlefshme"
msgstr ""
#: pretix/control/forms/filter.py:2118
msgid "Unredeemed"
@@ -16429,7 +16427,7 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/event/order_pay_change.html:75
#: pretix/presale/templates/pretixpresale/event/position_change.html:29
msgid "Continue"
msgstr "Vazhdoni"
msgstr ""
#: pretix/control/templates/pretixcontrol/auth/oauth_authorization.html:8
msgid "Authorize an application"
@@ -16677,7 +16675,7 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/postmessage.html:27
#: pretix/presale/templates/pretixpresale/waiting.html:42
msgid "If this takes longer than a few minutes, please contact us."
msgstr "Nëse kalon më shumë se disa minuta, ju lutemi t'na kontaktoni."
msgstr ""
#: pretix/control/templates/pretixcontrol/boxoffice/payment.html:4
#: pretix/control/templates/pretixcontrol/organizers/devices.html:71
@@ -16907,7 +16905,7 @@ msgstr[1] ""
#: pretix/presale/templates/pretixpresale/event/position_change.html:24
#: pretix/presale/templates/pretixpresale/event/position_modify.html:44
msgid "Cancel"
msgstr "Anuloni"
msgstr ""
#: pretix/control/templates/pretixcontrol/checkin/bulk_revert_confirm.html:27
#: pretix/control/templates/pretixcontrol/checkin/list_delete.html:24
@@ -17021,7 +17019,7 @@ msgstr ""
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/transaction_list.html:14
#: pretix/plugins/checkinlists/exporters.py:770
msgid "Result"
msgstr "Rezultati"
msgstr ""
#: pretix/control/templates/pretixcontrol/checkin/checkins.html:78
#: pretix/control/templates/pretixcontrol/order/index.html:437
@@ -17365,7 +17363,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/checkin/simulator.html:68
msgid "Additional information required"
msgstr "Më shumë informacione kërkohen"
msgstr ""
#: pretix/control/templates/pretixcontrol/checkin/simulator.html:70
msgid ""
@@ -18274,7 +18272,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/organizers/device_logs.html:50
#: pretix/control/templates/pretixcontrol/organizers/logs.html:80
msgid "No results"
msgstr "S'ka rezultate"
msgstr ""
#: pretix/control/templates/pretixcontrol/event/mail.html:7
#: pretix/control/templates/pretixcontrol/organizers/mail.html:11
@@ -18479,7 +18477,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/event/plugins.html:34
#: pretix/control/templates/pretixcontrol/organizers/plugins.html:34
msgid "Search results"
msgstr "Rezultatet e kërkimit"
msgstr ""
#: pretix/control/templates/pretixcontrol/event/plugins.html:56
#: pretix/control/templates/pretixcontrol/organizers/plugins.html:56
@@ -19326,51 +19324,51 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/global_sysreport.html:16
msgid "January"
msgstr "Janar"
msgstr ""
#: pretix/control/templates/pretixcontrol/global_sysreport.html:17
msgid "February"
msgstr "Shkurt"
msgstr ""
#: pretix/control/templates/pretixcontrol/global_sysreport.html:18
msgid "March"
msgstr "Mars"
msgstr ""
#: pretix/control/templates/pretixcontrol/global_sysreport.html:19
msgid "April"
msgstr "Prill"
msgstr ""
#: pretix/control/templates/pretixcontrol/global_sysreport.html:20
msgid "May"
msgstr "Maj"
msgstr ""
#: pretix/control/templates/pretixcontrol/global_sysreport.html:21
msgid "June"
msgstr "Qershor"
msgstr ""
#: pretix/control/templates/pretixcontrol/global_sysreport.html:22
msgid "July"
msgstr "Korrik"
msgstr ""
#: pretix/control/templates/pretixcontrol/global_sysreport.html:23
msgid "August"
msgstr "Gusht"
msgstr ""
#: pretix/control/templates/pretixcontrol/global_sysreport.html:24
msgid "September"
msgstr "Shtator"
msgstr ""
#: pretix/control/templates/pretixcontrol/global_sysreport.html:25
msgid "October"
msgstr "Tetor"
msgstr ""
#: pretix/control/templates/pretixcontrol/global_sysreport.html:26
msgid "November"
msgstr "Nëntor"
msgstr ""
#: pretix/control/templates/pretixcontrol/global_sysreport.html:27
msgid "December"
msgstr "Dhjetor"
msgstr ""
#: pretix/control/templates/pretixcontrol/global_sysreport.html:32
msgid "Generate report"
@@ -19745,7 +19743,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/subevents/bulk.html:355
#: pretix/control/templates/pretixcontrol/subevents/bulk.html:364
msgid "minutes"
msgstr "minuta"
msgstr ""
#: pretix/control/templates/pretixcontrol/item/index.html:229
msgid "hours"
@@ -20098,7 +20096,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/items/question.html:91
msgid "Count"
msgstr "Sasia"
msgstr ""
#: pretix/control/templates/pretixcontrol/items/question.html:92
#, python-format
@@ -21016,7 +21014,7 @@ msgstr ""
#: pretix/plugins/reports/exporters.py:969
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:469
msgid "Total"
msgstr "Shuma totale"
msgstr ""
#: pretix/control/templates/pretixcontrol/order/index.html:789
#: pretix/presale/templates/pretixpresale/event/order.html:210
@@ -23098,7 +23096,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/pdf/index.html:52
msgid "Text box"
msgstr "Kutia teksti"
msgstr ""
#: pretix/control/templates/pretixcontrol/pdf/index.html:59
msgid "QR Code"
@@ -23137,7 +23135,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/pdf/index.html:107
msgid "Duplicate"
msgstr "Kopjoni"
msgstr ""
#: pretix/control/templates/pretixcontrol/pdf/index.html:117
msgid "Undo"
@@ -27331,7 +27329,7 @@ msgstr ""
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/transaction_list.html:80
msgid "Comment:"
msgstr "Komente:"
msgstr ""
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/transaction_list.html:98
msgid "No order code detected"
@@ -27574,7 +27572,7 @@ msgstr ""
#: pretix/plugins/paypal2/payment.py:1097
#: pretix/plugins/paypal2/payment.py:1098 pretix/plugins/stripe/payment.py:1816
msgid "PayPal"
msgstr "PayPal"
msgstr ""
#: pretix/plugins/paypal/apps.py:53
msgid ""
@@ -29014,9 +29012,8 @@ msgid "Credit card payments"
msgstr ""
#: pretix/plugins/stripe/payment.py:342 pretix/plugins/stripe/payment.py:1527
#, fuzzy
msgid "iDEAL"
msgstr "iDEAL"
msgstr ""
#: pretix/plugins/stripe/payment.py:344 pretix/plugins/stripe/payment.py:352
#: pretix/plugins/stripe/payment.py:360 pretix/plugins/stripe/payment.py:395
@@ -29035,14 +29032,12 @@ msgid "Alipay"
msgstr ""
#: pretix/plugins/stripe/payment.py:358 pretix/plugins/stripe/payment.py:1564
#, fuzzy
msgid "Bancontact"
msgstr "Bancontact"
msgstr ""
#: pretix/plugins/stripe/payment.py:366
#, fuzzy
msgid "SEPA Direct Debit"
msgstr "Debit direkt me SEPA"
msgstr ""
#: pretix/plugins/stripe/payment.py:369
msgid ""
@@ -29077,14 +29072,12 @@ msgid "Multibanco"
msgstr ""
#: pretix/plugins/stripe/payment.py:409 pretix/plugins/stripe/payment.py:1730
#, fuzzy
msgid "Przelewy24"
msgstr "Przelewy24"
msgstr ""
#: pretix/plugins/stripe/payment.py:417 pretix/plugins/stripe/payment.py:1769
#, fuzzy
msgid "WeChat Pay"
msgstr "WeChat Pay"
msgstr ""
#: pretix/plugins/stripe/payment.py:433 pretix/plugins/stripe/payment.py:1824
msgid "Swish"
@@ -29228,9 +29221,8 @@ msgid "giropay via Stripe"
msgstr ""
#: pretix/plugins/stripe/payment.py:1480
#, fuzzy
msgid "giropay"
msgstr "giropay"
msgstr ""
#: pretix/plugins/stripe/payment.py:1483
msgid ""
@@ -29765,7 +29757,7 @@ msgstr ""
#: pretix/plugins/ticketoutputpdf/templates/pretixplugins/ticketoutputpdf/edit.html:23
msgid "Ticket design"
msgstr "Dizajni i biletës"
msgstr ""
#: pretix/plugins/ticketoutputpdf/templates/pretixplugins/ticketoutputpdf/edit.html:27
msgid "You can modify the design after you saved this page."
@@ -30328,7 +30320,7 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:27
#: pretix/presale/templates/pretixpresale/event/fragment_cart_box.html:18
msgid "Cart expired"
msgstr "Shporta juaj u skadua"
msgstr ""
#: pretix/presale/templates/pretixpresale/event/checkout_base.html:36
msgid "Show full cart"
@@ -30936,13 +30928,11 @@ msgid ""
"The items in your cart are no longer reserved for you. You can still "
"complete your order as long as theyre available."
msgstr ""
"Sendet në shportën tuaj nuk janë të rezervuar më për ju. Ju mund t'a "
"përmbushni porosinë tuaj derisa janë ende të disponueshëm."
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:514
#: pretix/presale/templates/pretixpresale/fragment_modals.html:48
msgid "Renew reservation"
msgstr "Rivendosni rezervimin"
msgstr ""
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:526
msgid "Reservation renewed"
@@ -32454,7 +32444,7 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/postmessage.html:21
#: pretix/presale/templates/pretixpresale/waiting.html:22
msgid "We are processing your request …"
msgstr "Ne po e proçesojmë kërkesën tuaj …"
msgstr ""
#: pretix/presale/utils.py:271 pretix/presale/utils.py:417
#: pretix/presale/utils.py:418

File diff suppressed because it is too large Load Diff

View File

@@ -41,20 +41,15 @@ for app in apps.get_app_configs():
if hasattr(app, 'PretixPluginMeta'):
if importlib.util.find_spec(app.name + '.urls'):
urlmod = importlib.import_module(app.name + '.urls')
single_plugin_patterns = []
if hasattr(urlmod, 'event_patterns'):
patterns = plugin_event_urls(urlmod.event_patterns, plugin=app.name)
single_plugin_patterns.append(
re_path(r'^(?P<event>[^/]+)/', include(patterns))
)
if hasattr(urlmod, 'organizer_patterns'):
single_plugin_patterns += plugin_event_urls(urlmod.organizer_patterns, plugin=app.name)
if single_plugin_patterns:
raw_plugin_patterns.append(
re_path(r'', include((single_plugin_patterns, app.label)))
re_path(r'^(?P<event>[^/]+)/', include((patterns, app.label)))
)
if hasattr(urlmod, 'organizer_patterns'):
patterns = plugin_event_urls(urlmod.organizer_patterns, plugin=app.name)
raw_plugin_patterns.append(
re_path(r'', include((patterns, app.label)))
)
plugin_patterns = [

View File

@@ -42,20 +42,15 @@ for app in apps.get_app_configs():
if hasattr(app, 'PretixPluginMeta'):
if importlib.util.find_spec(app.name + '.urls'):
urlmod = importlib.import_module(app.name + '.urls')
single_plugin_patterns = []
if hasattr(urlmod, 'organizer_patterns'):
single_plugin_patterns += plugin_event_urls(urlmod.organizer_patterns, plugin=app.name)
if hasattr(urlmod, 'event_patterns'):
plugin_event_patterns = plugin_event_urls(urlmod.event_patterns, plugin=app.name)
single_plugin_patterns.append(
re_path(r'^(?P<event>[^/]+)/', include(plugin_event_patterns))
)
if single_plugin_patterns:
patterns = plugin_event_urls(urlmod.event_patterns, plugin=app.name)
raw_plugin_patterns.append(
re_path(r'', include((single_plugin_patterns, app.label)))
re_path(r'^(?P<event>[^/]+)/', include((patterns, app.label)))
)
if hasattr(urlmod, 'organizer_patterns'):
patterns = plugin_event_urls(urlmod.organizer_patterns, plugin=app.name)
raw_plugin_patterns.append(
re_path(r'', include((patterns, app.label)))
)
plugin_patterns = [

View File

@@ -1,71 +0,0 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-today pretix 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.utils.translation import gettext_lazy as _
from lxml import etree
def parse(file):
# Spec: https://www.ebics.de/de/datenformate
data = file.read()
root = etree.fromstring(data)
statements = root.findall("{*}BkToCstmrStmt/{*}Stmt")
if not statements:
raise ValueError(_("Empty file or unknown format."))
def get_text(findall_result):
if len(findall_result) == 1:
return findall_result[0].text
return ""
rows = []
for stmt in statements:
for ntry in stmt.findall("{*}Ntry"):
minus = ""
otherparty = "Dbtr"
if ntry.findall("{*}CdtDbtInd")[0].text == "DBIT":
otherparty = "Cdtr"
minus = "-"
reference_parts = [
get_text(ntry.findall("{*}NtryDtls/{*}TxDtls/{*}RmtInf/{*}Ustrd")),
get_text(ntry.findall("{*}NtryDtls/{*}TxDtls/{*}Refs/{*}EndToEndId")),
get_text(ntry.findall("{*}NtryDtls/{*}TxDtls/{*}Refs/{*}InstructionIdentification")),
]
if ntry.findall("{*}NtryDtls/{*}Btch"):
# Batch booking, we do not support splitting yet
reference_parts.insert(0, get_text(ntry.findall("{*}NtryDtls/{*}Btch/{*}PmtInfId")))
row = {
'amount': minus + ntry.findall("{*}Amt")[0].text,
'date': get_text(ntry.findall("{*}BookgDt/{*}Dt")),
'reference': "\n".join(filter(lambda a: bool(a) and a != "NOTPROVIDED", reference_parts))
}
if ext_id := get_text(ntry.findall("{*}AcctSvcrRef")):
row['external_id'] = ext_id
if iban := get_text(ntry.findall(f"{{*}}NtryDtls/{{*}}TxDtls/{{*}}RltdPties/{{*}}{otherparty}Acct/{{*}}Id/{{*}}IBAN")):
row['iban'] = iban
if bic := get_text(ntry.findall(f"{{*}}NtryDtls/{{*}}TxDtls/{{*}}RltdAgts/{{*}}{otherparty}Agt/{{*}}FinInstnId/{{*}}BICFI")):
row['bic'] = bic
if payer := get_text(ntry.findall(f"{{*}}NtryDtls/{{*}}TxDtls/{{*}}RltdPties/{{*}}{otherparty}/{{*}}Nm")):
row['payer'] = payer
rows.append(row)
return rows

View File

@@ -46,12 +46,12 @@ from i18nfield.forms import I18nTextInput
from i18nfield.strings import LazyI18nString
from localflavor.generic.forms import BICFormField, IBANFormField
from localflavor.generic.validators import IBANValidator
from text_unidecode import unidecode
from pretix.base.forms import I18nMarkdownTextarea
from pretix.base.models import InvoiceAddress, Order, OrderPayment, OrderRefund
from pretix.base.payment import BasePaymentProvider
from pretix.base.templatetags.money import money_filter
from pretix.helpers.payment import generate_payment_qr_codes
from pretix.plugins.banktransfer.templatetags.ibanformat import ibanformat
from pretix.presale.views.cart import cart_session
@@ -313,6 +313,51 @@ class BankTransfer(BasePaymentProvider):
t += str(self.settings.get('bank_details', as_type=LazyI18nString))
return t
def swiss_qrbill(self, payment):
if not self.settings.get('bank_details_sepa_iban') or not self.settings.get('bank_details_sepa_iban')[:2] in ('CH', 'LI'):
return
if self.event.currency not in ('EUR', 'CHF'):
return
if not self.event.settings.invoice_address_from or not self.event.settings.invoice_address_from_country:
return
data_fields = [
'SPC',
'0200',
'1',
self.settings.get('bank_details_sepa_iban'),
'K',
self.settings.get('bank_details_sepa_name')[:70],
self.event.settings.invoice_address_from.replace('\n', ', ')[:70],
(self.event.settings.invoice_address_from_zipcode + ' ' + self.event.settings.invoice_address_from_city)[:70],
'',
'',
str(self.event.settings.invoice_address_from_country),
'', # rfu
'', # rfu
'', # rfu
'', # rfu
'', # rfu
'', # rfu
'', # rfu
str(payment.amount),
self.event.currency,
'', # debtor address
'', # debtor address
'', # debtor address
'', # debtor address
'', # debtor address
'', # debtor address
'', # debtor address
'NON',
'', # structured reference
self._code(payment.order),
'EPD',
]
data_fields = [unidecode(d or '') for d in data_fields]
return '\r\n'.join(data_fields)
def payment_pending_render(self, request: HttpRequest, payment: OrderPayment):
template = get_template('pretixplugins/banktransfer/pending.html')
ctx = {
@@ -322,18 +367,13 @@ class BankTransfer(BasePaymentProvider):
'amount': payment.amount,
'payment_info': payment.info_data,
'settings': self.settings,
'payment_qr_codes': generate_payment_qr_codes(
event=self.event,
code=self._code(payment.order),
amount=payment.amount,
bank_details_sepa_bic=self.settings.get('bank_details_sepa_bic'),
bank_details_sepa_name=self.settings.get('bank_details_sepa_name'),
bank_details_sepa_iban=self.settings.get('bank_details_sepa_iban'),
) if self.settings.bank_details_type == "sepa" else None,
'swiss_qrbill': self.swiss_qrbill(payment),
'eu_barcodes': self.event.currency == 'EUR',
'pending_description': self.settings.get('pending_description', as_type=LazyI18nString),
'details': self.settings.get('bank_details', as_type=LazyI18nString),
'has_invoices': payment.order.invoices.exists(),
}
ctx['any_barcodes'] = ctx['swiss_qrbill'] or ctx['eu_barcodes']
return template.render(ctx, request=request)
def payment_control_render(self, request: HttpRequest, payment: OrderPayment) -> str:

View File

@@ -34,10 +34,8 @@
import json
import logging
import operator
import re
from decimal import Decimal
from functools import reduce
import dateutil.parser
from celery.exceptions import MaxRetriesExceededError
@@ -119,26 +117,20 @@ def _find_order_for_code(base_qs, code):
pass
def _find_order_for_invoice_id(base_qs, prefixes, number):
def _find_order_for_invoice_id(base_qs, prefix, number):
try:
# Working with __iregex here is an experiment, if this turns out to be too slow in production
# we might need to switch to a different approach.
r = [
Q(
prefix__istartswith=prefix, # redundant, but hopefully makes it a little faster
full_invoice_no__iregex=prefix + r'[\- ]*0*' + number
)
for prefix in set(prefixes)
]
return base_qs.select_related('order').get(
reduce(operator.or_, r)
prefix__istartswith=prefix, # redundant, but hopefully makes it a little faster
full_invoice_no__iregex=prefix + r'[\- ]*0*' + number
).order
except (Invoice.DoesNotExist, Invoice.MultipleObjectsReturned):
pass
@transaction.atomic
def _handle_transaction(trans: BankTransaction, matches: tuple, regex_match_to_slug, event: Event = None, organizer: Organizer = None):
def _handle_transaction(trans: BankTransaction, matches: tuple, event: Event = None, organizer: Organizer = None):
orders = []
if event:
for slug, code in matches:
@@ -147,19 +139,18 @@ def _handle_transaction(trans: BankTransaction, matches: tuple, regex_match_to_s
if order.code not in {o.code for o in orders}:
orders.append(order)
else:
order = _find_order_for_invoice_id(Invoice.objects.filter(event=event), (slug, regex_match_to_slug.get(slug, slug)), code)
order = _find_order_for_invoice_id(Invoice.objects.filter(event=event), slug, code)
if order and order.code not in {o.code for o in orders}:
orders.append(order)
else:
qs = Order.objects.filter(event__organizer=organizer)
for slug, code in matches:
original_slug = regex_match_to_slug.get(slug, slug)
order = _find_order_for_code(qs.filter(Q(event__slug__iexact=slug) | Q(event__slug__iexact=original_slug)), code)
order = _find_order_for_code(qs.filter(event__slug__iexact=slug), code)
if order:
if order.code not in {o.code for o in orders}:
orders.append(order)
else:
order = _find_order_for_invoice_id(Invoice.objects.filter(event__organizer=organizer), (slug, original_slug), code)
order = _find_order_for_invoice_id(Invoice.objects.filter(event__organizer=organizer), slug, code)
if order and order.code not in {o.code for o in orders}:
orders.append(order)
@@ -375,37 +366,22 @@ def process_banktransfers(self, job: int, data: list) -> None:
transactions = _get_unknown_transactions(job, data, **job.owner_kwargs)
# Match order codes
regex_match_to_slug = {}
code_len_agg = Order.objects.filter(event__organizer=job.organizer).annotate(
clen=Length('code')
).aggregate(min=Min('clen'), max=Max('clen'))
if job.event:
prefixes = {job.event.slug.upper(), job.event.slug.upper().replace("-", "")}
if "-" in job.event.slug:
regex_match_to_slug[job.event.slug.upper().replace("-", "")] = job.event.slug
prefixes = {job.event.slug.upper()}
else:
prefixes = set()
for e in job.organizer.events.all():
prefixes.add(e.slug.upper())
if "-" in e.slug:
prefixes.add(e.slug.upper().replace("-", ""))
regex_match_to_slug[e.slug.upper().replace("-", "")] = e.slug
prefixes = {e.slug.upper() for e in job.organizer.events.all()}
# Match invoice numbers
inr_len_agg = Invoice.objects.filter(event__organizer=job.organizer).annotate(
clen=Length('invoice_no')
).aggregate(min=Min('clen'), max=Max('clen'))
if job.event:
invoice_prefixes = Invoice.objects.filter(event=job.event)
prefixes |= {p.rstrip(' -') for p in Invoice.objects.filter(event=job.event).distinct().values_list('prefix', flat=True)}
else:
invoice_prefixes = Invoice.objects.filter(event__organizer=job.organizer)
for p in invoice_prefixes.order_by().distinct().values_list('prefix', flat=True):
prefix = p.rstrip(" -")
prefixes.add(prefix)
if "-" in prefix:
prefix_nodash = prefix.replace("-", "")
prefixes.add(prefix_nodash)
regex_match_to_slug[prefix_nodash] = prefix
prefixes |= {p.rstrip(' -') for p in Invoice.objects.filter(event__organizer=job.organizer).distinct().values_list('prefix', flat=True)}
pattern = re.compile(
"(%s)[ \\-_]*([A-Z0-9]{%s,%s})" % (
@@ -419,11 +395,6 @@ def process_banktransfers(self, job: int, data: list) -> None:
)
for trans in transactions:
if trans.amount == Decimal("0.00"):
# Ignore all zero-valued transactions
trans.state = BankTransaction.STATE_DISCARDED
trans.save()
continue
# Whitespace in references is unreliable since linebreaks and spaces can occur almost anywhere, e.g.
# DEMOCON-123\n45 should be matched to DEMOCON-12345. However, sometimes whitespace is important,
# e.g. when there are two references. "DEMOCON-12345 DEMOCON-45678" would otherwise be parsed as
@@ -438,9 +409,9 @@ def process_banktransfers(self, job: int, data: list) -> None:
if matches:
if job.event:
_handle_transaction(trans, matches, regex_match_to_slug, event=job.event)
_handle_transaction(trans, matches, event=job.event)
else:
_handle_transaction(trans, matches, regex_match_to_slug, organizer=job.organizer)
_handle_transaction(trans, matches, organizer=job.organizer)
else:
trans.state = BankTransaction.STATE_NOMATCH
trans.save()

View File

@@ -1,6 +1,7 @@
{% load i18n %}
{% load l10n %}
{% load commadecimal %}
{% load static %}
{% load dotdecimal %}
{% load ibanformat %}
{% load money %}
@@ -16,7 +17,7 @@
{% endblocktrans %}</p>
<div class="row">
<div class="{% if payment_qr_codes %}col-md-6{% else %}col-md-12{% endif %} col-xs-12">
<div class="{% if settings.bank_details_type == "sepa" %}col-md-6{% else %}col-md-12{% endif %} col-xs-12">
<dl class="dl-horizontal">
<dt>{% trans "Reference code (important):" %}</dt><dd><b>{{ code }}</b></dd>
<dt>{% trans "Amount:" %}</dt><dd>{{ amount|money:event.currency }}</dd>
@@ -35,7 +36,94 @@
{% trans "We will send you an email as soon as we received your payment." %}
</p>
</div>
{% if payment_qr_codes %}
{% include "pretixpresale/event/payment_qr_codes.html" %}
{% if settings.bank_details_type == "sepa" and any_barcodes %}
<div class="tabcontainer col-md-6 col-sm-6 hidden-xs text-center js-only blank-after">
<div id="banktransfer_qrcodes_tabs_content" class="tabpanels blank-after">
{% if swiss_qrbill %}
<div id="banktransfer_qrcodes_qrbill"
role="tabpanel"
tabindex="0"
aria-labelledby="banktransfer_qrcodes_qrbill_tab"
>
<div class="banktransfer-swiss-cross-overlay" role="figure" aria-labelledby="banktransfer_qrcodes_qrbill_tab banktransfer_qrcodes_label">
<svg class="banktransfer-swiss-cross" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.8 19.8"><path stroke="#fff" stroke-width="1.436" d="M.7.7h18.4v18.4H.7z"/><path fill="#fff" d="M8.3 4h3.3v11H8.3z"/><path fill="#fff" d="M4.4 7.9h11v3.3h-11z"/></svg>
<script type="text/plain" data-size="150" data-replace-with-qr data-desc="{% trans 'Scan this image with your banking apps QR-Reader to start the payment process.' %}">{{swiss_qrbill}}</script>
</div>
</div>
{% endif %}
{% if eu_barcodes %}
<div id="banktransfer_qrcodes_girocode"
role="tabpanel"
tabindex="0"
{{ swiss_qrbill|yesno:'hidden,' }}
aria-labelledby="banktransfer_qrcodes_girocode_tab"
>
<div role="figure" aria-labelledby="banktransfer_qrcodes_girocode_tab banktransfer_qrcodes_label">
<script type="text/plain" data-size="150" data-replace-with-qr data-desc="{% trans 'Scan this image with your banking apps QR-Reader to start the payment process.' %}">BCD
002
2
SCT
{{ settings.bank_details_sepa_bic }}
{{ settings.bank_details_sepa_name|unidecode }}
{{ settings.bank_details_sepa_iban }}
{{ event.currency }}{{ amount|dotdecimal }}
{{ code }}
</script>
</div>
</div>
<div id="banktransfer_qrcodes_bezahlcode"
role="tabpanel"
tabindex="0"
hidden
aria-labelledby="banktransfer_qrcodes_bezahlcode_tab"
>
<a aria-label="{% trans "Open BezahlCode in your banking app to start the payment process." %}" href="bank://singlepaymentsepa?name={{ settings.bank_details_sepa_name|urlencode }}&iban={{ settings.bank_details_sepa_iban }}&bic={{ settings.bank_details_sepa_bic }}&amount={{ amount|commadecimal }}&reason={{ code }}&currency={{ event.currency }}">
<div role="figure" aria-labelledby="banktransfer_qrcodes_bezahlcode_tab banktransfer_qrcodes_label">
<script type="text/plain" data-size="150" data-replace-with-qr data-desc="{% trans 'Scan this image with your banking apps QR-Reader to start the payment process.' %}">bank://singlepaymentsepa?name={{ settings.bank_details_sepa_name|urlencode }}&iban={{ settings.bank_details_sepa_iban }}&bic={{ settings.bank_details_sepa_bic }}&amount={{ amount|commadecimal }}&reason={{ code }}&currency={{ event.currency }}</script>
</div>
</a>
</div>
{% endif %}
</div>
<div id="banktransfer_qrcodes_tabs" role="tablist" aria-labelledby="banktransfer_qrcodes_label" class="blank-after btn-group">
{% if swiss_qrbill %}
<button
class="btn btn-default"
id="banktransfer_qrcodes_qrbill_tab"
type="button"
role="tab"
aria-controls="banktransfer_qrcodes_qrbill"
aria-selected="true"
tabindex="-1">QR-bill</button>
{% endif %}
{% if eu_barcodes %}
<button
class="btn btn-default"
id="banktransfer_qrcodes_girocode_tab"
type="button"
role="tab"
aria-controls="banktransfer_qrcodes_girocode"
aria-selected="{{ swiss_qrbill|yesno:"false,true" }}"
tabindex="-1">EPC-QR</button>
<button
class="btn btn-default"
id="banktransfer_qrcodes_bezahlcode_tab"
type="button"
role="tab"
aria-controls="banktransfer_qrcodes_bezahlcode"
aria-selected="false"
tabindex="-1">BezahlCode</button>
{% endif %}
</div>
<p class="text-muted" id="banktransfer_qrcodes_label">
{% trans "Scan the QR code with your banking app" %}
</p>
</div>
{% endif %}
</div>
</div>
{% if swiss_qrbill %}
<link rel="stylesheet" href="{% static "pretixplugins/banktransfer/swisscross.css" %}">
{% endif %}

View File

@@ -66,7 +66,7 @@ from pretix.control.permissions import (
)
from pretix.control.views.organizer import OrganizerDetailViewMixin
from pretix.helpers.json import CustomJSONEncoder
from pretix.plugins.banktransfer import camtimport, csvimport, mt940import
from pretix.plugins.banktransfer import csvimport, mt940import
from pretix.plugins.banktransfer.models import (
BankImportJob, BankTransaction, RefundExport,
)
@@ -419,9 +419,6 @@ class ImportView(ListView):
):
return self.process_mt940()
elif 'file' in self.request.FILES and '.xml' in self.request.FILES.get('file').name.lower():
return self.process_camt()
elif self.request.FILES.get('file') is None:
messages.error(self.request, _('You must choose a file to import.'))
return self.redirect_back()
@@ -435,14 +432,6 @@ class ImportView(ListView):
def settings(self):
return SettingsSandbox('payment', 'banktransfer', getattr(self.request, 'event', self.request.organizer))
def process_camt(self):
try:
return self.start_processing(camtimport.parse(self.request.FILES.get('file')))
except:
logger.exception('Failed to import CAMT file')
messages.error(self.request, _('We were unable to process your input.'))
return self.redirect_back()
def process_mt940(self):
try:
return self.start_processing(mt940import.parse(self.request.FILES.get('file')))

View File

@@ -711,7 +711,7 @@ class PaypalMethod(BasePaymentProvider):
description = '{prefix}{orderstring}{postfix}'.format(
prefix='{} '.format(self.settings.prefix) if self.settings.prefix else '',
orderstring=__('Order {order} for {event}').format(
event=self.event.name,
event=request.event.name,
order=payment.order.code
),
postfix=' {}'.format(self.settings.postfix) if self.settings.postfix else ''

View File

@@ -644,9 +644,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
FontFallbackParagraph(
_("Pending payments at {datetime}").format(
datetime=date_format(
(df_start - datetime.timedelta.resolution).astimezone(
self.timezone
),
df_start - datetime.timedelta.resolution,
"SHORT_DATETIME_FORMAT",
)
),
@@ -696,9 +694,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
Paragraph(
_("Pending payments at {datetime}").format(
datetime=date_format(
((df_end or now()) - datetime.timedelta.resolution).astimezone(
self.timezone
),
(df_end or now()) - datetime.timedelta.resolution,
"SHORT_DATETIME_FORMAT",
)
),
@@ -755,9 +751,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
Paragraph(
_("Total gift card value at {datetime}").format(
datetime=date_format(
(df_start - datetime.timedelta.resolution).astimezone(
self.timezone
),
df_start - datetime.timedelta.resolution,
"SHORT_DATETIME_FORMAT",
)
),
@@ -795,9 +789,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
Paragraph(
_("Total gift card value at {datetime}").format(
datetime=date_format(
((df_end or now()) - datetime.timedelta.resolution).astimezone(
self.timezone
),
(df_end or now()) - datetime.timedelta.resolution,
"SHORT_DATETIME_FORMAT",
)
),

View File

@@ -137,7 +137,7 @@ logger = logging.getLogger('pretix.plugins.stripe')
# Real-time payments
# - Swish: ✓
# - PayNow: ✗
# - PromptPay:
# - PromptPay:
# - Pix: ✗
#
# Vouchers
@@ -412,18 +412,6 @@ class StripeSettingsHolder(BasePaymentProvider):
'before they work properly.'),
required=False,
)),
('method_pay_by_bank',
forms.BooleanField(
label=_('Pay by bank'),
disabled=self.event.currency not in ['EUR', 'GBP'],
help_text=' '.join([
str(_('Some payment methods might need to be enabled in the settings of your Stripe account '
'before they work properly.')),
str(_('Currently only available for charges in GBP and customers with UK bank accounts, and '
'in private preview for France and Germany.'))
]),
required=False,
)),
('method_wechatpay',
forms.BooleanField(
label=_('WeChat Pay'),
@@ -440,14 +428,6 @@ class StripeSettingsHolder(BasePaymentProvider):
'before they work properly.'),
required=False,
)),
('method_promptpay',
forms.BooleanField(
label='PromptPay',
disabled=self.event.currency != 'THB',
help_text=_('Some payment methods might need to be enabled in the settings of your Stripe account '
'before they work properly.'),
required=False,
)),
('method_swish',
forms.BooleanField(
label=_('Swish'),
@@ -1830,32 +1810,6 @@ class StripeRevolutPay(StripeRedirectMethod):
}
class StripePayByBank(StripeRedirectMethod):
identifier = 'stripe_pay_by_bank'
verbose_name = _('Pay by bank via Stripe')
public_name = _('Pay by bank')
method = 'pay_by_bank'
redirect_in_widget_allowed = False
confirmation_method = 'automatic'
explanation = _(
'Pay by bank allows you to authorize a secure Open Banking payment from your banking app. Currently available '
'only with a UK bank account.'
)
def is_allowed(self, request: HttpRequest, total: Decimal=None) -> bool:
return super().is_allowed(request, total) and self.event.currency == 'GBP'
def _payment_intent_kwargs(self, request, payment):
return {
"payment_method_data": {
"type": "pay_by_bank",
"billing_details": {
"email": payment.order.email,
},
},
}
class StripePayPal(StripeRedirectMethod):
identifier = 'stripe_paypal'
verbose_name = _('PayPal via Stripe')
@@ -1888,30 +1842,6 @@ class StripeSwish(StripeRedirectMethod):
}
class StripePromptPay(StripeRedirectMethod):
identifier = 'stripe_promptpay'
verbose_name = _('PromptPay via Stripe')
public_name = 'PromptPay'
method = 'promptpay'
confirmation_method = 'automatic'
explanation = _(
'This payment method is available to PromptPay users in Thailand. Please have your app ready.'
)
def is_allowed(self, request: HttpRequest, total: Decimal=None) -> bool:
return super().is_allowed(request, total) and request.event.currency == "THB"
def _payment_intent_kwargs(self, request, payment):
return {
"payment_method_data": {
"type": "promptpay",
"billing_details": {
"email": payment.order.email,
},
},
}
class StripeTwint(StripeRedirectMethod):
identifier = 'stripe_twint'
verbose_name = _('TWINT via Stripe')

View File

@@ -38,6 +38,7 @@ from pretix.base.signals import (
)
from pretix.control.signals import nav_organizer
from pretix.plugins.stripe.forms import StripeKeyValidator
from pretix.plugins.stripe.payment import StripeMethod
from pretix.presale.signals import html_head, process_response
@@ -46,17 +47,15 @@ def register_payment_provider(sender, **kwargs):
from .payment import (
StripeAffirm, StripeAlipay, StripeBancontact, StripeCC, StripeEPS,
StripeGiropay, StripeIdeal, StripeKlarna, StripeMobilePay,
StripeMultibanco, StripePayByBank, StripePayPal, StripePromptPay,
StripePrzelewy24, StripeRevolutPay, StripeSEPADirectDebit,
StripeSettingsHolder, StripeSofort, StripeSwish, StripeTwint,
StripeWeChatPay,
StripeMultibanco, StripePayPal, StripePrzelewy24, StripeRevolutPay,
StripeSEPADirectDebit, StripeSettingsHolder, StripeSofort, StripeSwish,
StripeTwint, StripeWeChatPay,
)
return [
StripeSettingsHolder, StripeCC, StripeGiropay, StripeIdeal, StripeAlipay, StripeBancontact,
StripeSofort, StripeEPS, StripeMultibanco, StripePayByBank, StripePrzelewy24, StripePromptPay, StripeRevolutPay,
StripeWeChatPay, StripeSEPADirectDebit, StripeAffirm, StripeKlarna, StripePayPal, StripeSwish,
StripeTwint, StripeMobilePay
StripeSofort, StripeEPS, StripeMultibanco, StripePrzelewy24, StripeRevolutPay, StripeWeChatPay,
StripeSEPADirectDebit, StripeAffirm, StripeKlarna, StripePayPal, StripeSwish, StripeTwint, StripeMobilePay
]
@@ -190,8 +189,6 @@ def nav_o(sender, request, organizer, **kwargs):
@receiver(signal=process_response, dispatch_uid="stripe_middleware_resp")
def signal_process_response(sender, request: HttpRequest, response: HttpResponse, **kwargs):
from pretix.plugins.stripe.payment import StripeMethod
provider = StripeMethod(sender)
url = resolve(request.path_info)

View File

@@ -79,9 +79,3 @@
.vcenter {
margin: auto;
}
.stripe-qr-code {
max-width: 80%;
width: 200px;
height: auto;
}

View File

@@ -325,8 +325,6 @@ $(function () {
} else if ($("#stripe_payment_intent_next_action_redirect_url").length) {
let payment_intent_next_action_redirect_url = $.trim($("#stripe_payment_intent_next_action_redirect_url").html());
pretixstripe.handlePaymentRedirectAction(payment_intent_next_action_redirect_url);
} else if ($.trim($("#stripe_payment_intent_action_type").html()) === "promptpay_display_qr_code") {
waitingDialog.hide();
} else if ($.trim($("#stripe_payment_intent_action_type").html()) === "wechat_pay_display_qr_code") {
let payment_intent_client_secret = $.trim($("#stripe_payment_intent_client_secret").html());
pretixstripe.handleWechatAction(payment_intent_client_secret);

View File

@@ -22,6 +22,7 @@
import logging
from urllib.parse import urlsplit
import stripe
from django.conf import settings
from pretix.base.services.tasks import EventTask
@@ -49,10 +50,7 @@ def get_stripe_account_key(prov):
@app.task(base=EventTask, max_retries=5, default_retry_delay=1)
def stripe_verify_domain(event, domain):
import stripe
from pretix.plugins.stripe.payment import StripeCC
prov = StripeCC(event)
account = get_stripe_account_key(prov)

View File

@@ -27,21 +27,9 @@
<div class="stripe-errors sr-only panel-body">
</div>
{% if payment_intent_promptpay_image_url %}
<div class="panel-body">
<p>{% blocktrans trimmed %}
Please scan the QR code below to complete your PromptPay payment.
Once you have completed your payment, you can refresh this page.
{% endblocktrans %}</p>
<div class="text-center">
<img src="{{ payment_intent_promptpay_image_url }}" alt="{% trans 'PromptPay QR code' %}"
class="stripe-qr-code" />
</div>
</div>
{% else %}
<div class="panel-body embed-responsive embed-responsive-sca" id="scacontainer">
</div>
{% endif %}
<div class="panel-body embed-responsive embed-responsive-sca" id="scacontainer">
</div>
</div>
<div class="row checkout-button-row">
<div class="col-md-4">

View File

@@ -37,6 +37,7 @@ import logging
import urllib.parse
import requests
import stripe
from django.contrib import messages
from django.core import signing
from django.db import transaction
@@ -67,6 +68,7 @@ from pretix.helpers.http import redirect_to_url
from pretix.multidomain.urlreverse import build_absolute_uri, eventreverse
from pretix.plugins.stripe.forms import OrganizerStripeSettingsForm
from pretix.plugins.stripe.models import ReferencedStripeObject
from pretix.plugins.stripe.payment import StripeCC, StripeSettingsHolder
from pretix.plugins.stripe.tasks import (
get_domain_for_event, stripe_verify_domain,
)
@@ -98,8 +100,6 @@ def redirect_view(request, *args, **kwargs):
@scopes_disabled()
def oauth_return(request, *args, **kwargs):
import stripe
if 'payment_stripe_oauth_event' not in request.session:
messages.error(request, _('An error occurred during connecting with Stripe, please try again.'))
return redirect('control:index')
@@ -268,10 +268,6 @@ SOURCE_TYPES = {
def charge_webhook(event, event_json, charge_id, rso):
import stripe
from pretix.plugins.stripe.payment import StripeCC
prov = StripeCC(event)
prov._init_api()
@@ -375,10 +371,6 @@ def charge_webhook(event, event_json, charge_id, rso):
def source_webhook(event, event_json, source_id, rso):
import stripe
from pretix.plugins.stripe.payment import StripeCC
prov = StripeCC(event)
prov._init_api()
try:
@@ -448,10 +440,6 @@ def source_webhook(event, event_json, source_id, rso):
def paymentintent_webhook(event, event_json, paymentintent_id, rso):
import stripe
from pretix.plugins.stripe.payment import StripeCC
prov = StripeCC(event)
prov._init_api()
@@ -528,8 +516,6 @@ class StripeOrderView:
@method_decorator(xframe_options_exempt, 'dispatch')
class ReturnView(StripeOrderView, View):
def get(self, request, *args, **kwargs):
import stripe
prov = self.pprov
prov._init_api()
try:
@@ -582,10 +568,6 @@ class ReturnView(StripeOrderView, View):
class ScaView(StripeOrderView, View):
def get(self, request, *args, **kwargs):
import stripe
from pretix.plugins.stripe.payment import StripeSettingsHolder
prov = self.pprov
prov._init_api()
@@ -613,7 +595,7 @@ class ScaView(StripeOrderView, View):
if intent.status == 'requires_action' and intent.next_action.type in [
'use_stripe_sdk', 'redirect_to_url', 'alipay_handle_redirect', 'wechat_pay_display_qr_code',
'swish_handle_redirect_or_display_qr_code', 'multibanco_display_details', 'promptpay_display_qr_code',
'swish_handle_redirect_or_display_qr_code', 'multibanco_display_details',
]:
ctx = {
'order': self.order,
@@ -631,8 +613,6 @@ class ScaView(StripeOrderView, View):
elif intent.next_action.type == 'multibanco_display_details':
ctx['payment_intent_next_action_redirect_url'] = intent.next_action.multibanco_display_details['hosted_voucher_url']
ctx['payment_intent_redirect_action_handling'] = 'iframe'
elif intent.next_action.type == 'promptpay_display_qr_code':
ctx['payment_intent_promptpay_image_url'] = intent.next_action.promptpay_display_qr_code['image_url_svg']
r = render(request, 'pretixplugins/stripe/sca.html', ctx)
r._csp_ignore = True

View File

@@ -1545,8 +1545,6 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
ctx['invoice_address_asked'] = self.address_asked
ctx['customer'] = self.cart_customer
ctx['transmission_fields'] = self.invoice_address.describe_transmission(self.event)
self.cart_session['shown_total'] = str(ctx['cart']['total'])
email = self.cart_session.get('contact_form_data', {}).get('email')

View File

@@ -111,7 +111,7 @@
<dt>{% trans "Internal reference" %}</dt>
<dd>{{ addr.internal_reference }}</dd>
{% endif %}
{% for k, v in transmission_fields %}
{% for k, v in addr.describe_transmission %}
<dt>{{ k }}</dt>
<dd>{{ v }}</dd>
{% endfor %}

View File

@@ -1,4 +1,3 @@
{% load html_time %}
{% load i18n %}
{% load icon %}
{% load eventurl %}
@@ -22,18 +21,20 @@
{% if event.settings.show_times %}
<br>
<span data-time="{{ ev.date_from.isoformat }}" data-timezone="{{ request.event.timezone }}">
{% html_time ev.date_from "TIME_FORMAT" attr_fmt="H:i" as time%}
{% blocktrans with time=time %}
Begin: {{ time }}
{% endblocktrans %}
{% with time_human=ev.date_from|date:"TIME_FORMAT" time_24=ev.date_from|time:"H:i" %}
{% blocktrans trimmed with time='<time datetime="'|add:time_24|add:'">'|add:time_human|add:"</time>"|safe %}
Begin: {{ time }}
{% endblocktrans %}
{% endwith %}
</span>
{% if event.settings.show_date_to and ev.date_to %}
<br>
<span data-time="{{ ev.date_to.isoformat }}" data-timezone="{{ request.event.timezone }}">
{% html_time ev.date_to "TIME_FORMAT" attr_fmt="H:i" as time%}
{% blocktrans with time=time %}
End: {{ time }}
{% endblocktrans %}
{% with time_human=ev.date_to|date:"TIME_FORMAT" time_24=ev.date_to|time:"H:i" %}
{% blocktrans trimmed with time='<time datetime="'|add:time_24|add:'">'|add:time_human|add:"</time>"|safe %}
End: {{ time }}
{% endblocktrans %}
{% endwith %}
</span>
{% endif %}
{% endif %}
@@ -41,17 +42,19 @@
<br>
{% if ev.date_admission|date:"SHORT_DATE_FORMAT" == ev.date_from|date:"SHORT_DATE_FORMAT" %}
<span data-time="{{ ev.date_admission.isoformat }}" data-timezone="{{ request.event.timezone }}">
{% html_time ev.date_admission "TIME_FORMAT" attr_fmt="H:i" as time%}
{% blocktrans trimmed with time=time %}
Admission: {{ time }}
{% endblocktrans %}
{% with time_human=ev.date_admission|date:"TIME_FORMAT" time_24=ev.date_admission|time:"H:i" %}
{% blocktrans trimmed with time='<time datetime="'|add:time_24|add:'">'|add:time_human|add:"</time>"|safe %}
Admission: {{ time }}
{% endblocktrans %}
{% endwith %}
</span>
{% else %}
<span data-time="{{ ev.date_admission.isoformat }}" data-timezone="{{ request.event.timezone }}">
{% html_time ev.date_admission "SHORT_DATETIME_FORMAT" attr_fmt="Y-m-d H:i" as datetime%}
{% blocktrans trimmed with datetime=datetime %}
Admission: {{ datetime }}
{% endblocktrans %}
{% with datetime_human=ev.date_admission|date:"SHORT_DATETIME_FORMAT" datetime_iso=ev.date_admission|time:"Y-m-d H:i" %}
{% blocktrans trimmed with datetime='<time datetime="'|add:datetime_iso|add:'">'|add:datetime_human|add:"</time>"|safe %}
Admission: {{ datetime }}
{% endblocktrans %}
{% endwith %}
</span>
{% endif %}
{% endif %}

View File

@@ -1,5 +1,4 @@
{% extends "pretixpresale/event/base.html" %}
{% load html_time %}
{% load i18n %}
{% load bootstrap3 %}
{% load eventsignal %}
@@ -93,10 +92,11 @@
A payment of {{ total }} is still pending for this order.
{% endblocktrans %}</strong>
<strong>
{% html_time order.expires "format_expires" as date %}
{% blocktrans trimmed with date=date %}
{% with date_human=order|format_expires|safe date_iso=order.expires|date:"c" %}
{% blocktrans trimmed with date='<time datetime="'|add:date_iso|add:'">'|add:date_human|add:"</time>"|safe %}
Please complete your payment before {{ date }}
{% endblocktrans %}
{% endwith %}
</strong>
</p>
{% if last_payment %}

View File

@@ -1,44 +0,0 @@
{% load i18n %}
{% load static %}
{% if payment_qr_codes %}
<div class="tabcontainer col-md-6 col-sm-6 hidden-xs text-center js-only blank-after">
<div id="banktransfer_qrcodes_tabs_content" class="tabpanels blank-after">
{% for code_info in payment_qr_codes %}
<div id="banktransfer_qrcodes_{{ code_info.id }}"
role="tabpanel"
tabindex="0"
{% if not forloop.first %}hidden{% endif %}
aria-labelledby="banktransfer_qrcodes_{{ code_info.id }}_tab"
>
{% if code_info.link %}<a aria-label="{{ code_info.link_aria_label }}" href="{{ code_info.link }}">{% endif %}
<div class="{{ code_info.css_class }}" role="figure" aria-labelledby="banktransfer_qrcodes_{{ code_info.id }}_tab banktransfer_qrcodes_label">
{{ code_info.html_prefix }}
<script type="text/plain" data-size="150" data-replace-with-qr data-desc="{% trans 'Scan this image with your banking apps QR-Reader to start the payment process.' %}">{{ code_info.qr_data }}</script>
</div>
{% if code_info.link %}</a>{% endif %}
</div>
{% endfor %}
</div>
<div id="banktransfer_qrcodes_tabs" role="tablist" aria-labelledby="banktransfer_qrcodes_label" class="blank-after btn-group">
{% for code_info in payment_qr_codes %}
<button
class="btn btn-default"
id="banktransfer_qrcodes_{{ code_info.id }}_tab"
type="button"
role="tab"
aria-controls="banktransfer_qrcodes_{{ code_info.id }}"
aria-selected="{{ forloop.first|yesno:"true,false" }}"
tabindex="-1">{{ code_info.label }}</button>
{% endfor %}
</div>
<p class="text-muted" id="banktransfer_qrcodes_label">
{% trans "Scan the QR code with your banking app" %}
</p>
</div>
{% for code_info in payment_qr_codes %}
{% if code_info.id == "qrbill" %}
<link rel="stylesheet" href="{% static "pretixplugins/banktransfer/swisscross.css" %}">
{% endif %}
{% endfor %}
{% endif %}

View File

@@ -1,4 +1,3 @@
{% load html_time %}
{% load i18n %}
{% load date_fast %}
{% load calendarhead %}
@@ -56,7 +55,7 @@
running
{% elif event.event.presale_has_ended %}
over
{% elif event.event.settings.presale_start_show_date and event.event.effective_presale_start %}
{% elif event.event.settings.presale_start_show_date and event.event.presale_start %}
soon
{% else %}
soon
@@ -109,12 +108,13 @@
<span class="fa fa-ticket" aria-hidden="true"></span> {% trans "Book now" %}
{% elif event.event.presale_has_ended %}
{% trans "Sale over" %}
{% elif event.event.settings.presale_start_show_date and event.event.effective_presale_start %}
{% elif event.event.settings.presale_start_show_date and event.event.presale_start %}
<span class="fa fa-ticket" aria-hidden="true"></span>
{% html_time event.event.effective_presale_start "SHORT_DATE_FORMAT" as start_date %}
{% blocktrans with start_date=start_date %}
{% with date_human=event.event.presale_start|date_fast:"SHORT_DATE_FORMAT" date_iso=event.event.presale_start|date_fast:"c" %}
{% blocktrans with start_date="<time datetime='"|add:date_iso|add:"'>"|add:date_human|add:"</time>"|safe %}
from {{ start_date }}
{% endblocktrans %}
{% endwith %}
{% else %}
<span class="fa fa-ticket" aria-hidden="true"></span> {% trans "Soon" %}
{% endif %}

View File

@@ -1,4 +1,3 @@
{% load html_time %}
{% load i18n %}
{% load eventurl %}
<div class="day-calendar cal-size-{{ raster_to_shortest_ratio }}{% if no_headlines %} no-headlines{% endif %}"
@@ -53,7 +52,7 @@
running
{% elif event.event.presale_has_ended %}
over
{% elif event.event.settings.presale_start_show_date and event.event.effective_presale_start %}
{% elif event.event.settings.presale_start_show_date and event.event.presale_start %}
soon
{% else %}
soon
@@ -115,10 +114,9 @@
<span class="fa fa-ticket" aria-hidden="true"></span> {% trans "Book now" %}
{% elif event.event.presale_has_ended %}
<span class="fa fa-ticket" aria-hidden="true"></span> {% trans "Sale over" %}
{% elif event.event.settings.presale_start_show_date and event.event.effective_presale_start %}
{% elif event.event.settings.presale_start_show_date and event.event.presale_start %}
<span class="fa fa-ticket" aria-hidden="true"></span>
{% html_time event.event.effective_presale_start "SHORT_DATE_FORMAT" as start_date %}
{% blocktrans with start_date=start_date %}
{% blocktrans with start_date=event.event.presale_start|date:"SHORT_DATE_FORMAT" %}
from {{ start_date }}
{% endblocktrans %}
{% else %}

View File

@@ -1,4 +1,3 @@
{% load html_time %}
{% load i18n %}
{% load icon %}
{% load textbubble %}
@@ -53,10 +52,11 @@
{% endtextbubble %}
{% if event.settings.presale_start_show_date %}
<br><span class="text-muted">
{% html_time event.event.effective_presale_start "SHORT_DATE_FORMAT" as date %}
{% blocktrans with date=date %}
{% with date_iso=event.effective_presale_start.isoformat date_human=event.effective_presale_start|date:"SHORT_DATE_FORMAT" %}
{% blocktrans trimmed with date='<time datetime="'|add:date_iso|add:'">'|add:date_human|add:"</time>"|safe %}
Sale starts {{ date }}
{% endblocktrans %}
{% endwith %}
</span>
{% endif %}
{% endif %}

View File

@@ -1,4 +1,3 @@
{% load html_time %}
{% load i18n %}
{% load date_fast %}
<div class="week-calendar">
@@ -25,7 +24,7 @@
running
{% elif event.event.presale_has_ended %}
over
{% elif event.event.settings.presale_start_show_date and event.event.effective_presale_start %}
{% elif event.event.settings.presale_start_show_date and event.event.presale_start %}
soon
{% else %}
soon
@@ -78,10 +77,9 @@
<span class="fa fa-ticket" aria-hidden="true"></span> {% trans "Book now" %}
{% elif event.event.presale_has_ended %}
{% trans "Sale over" %}
{% elif event.event.settings.presale_start_show_date and event.event.effective_presale_start %}
{% elif event.event.settings.presale_start_show_date and event.event.presale_start %}
<span class="fa fa-ticket" aria-hidden="true"></span>
{% html_time event.event.effective_presale_start "SHORT_DATE_FORMAT" as start_date %}
{% blocktrans with start_date=start_date %}
{% blocktrans with start_date=event.event.presale_start|date_fast:"SHORT_DATE_FORMAT" %}
from {{ start_date }}
{% endblocktrans %}
{% else %}

View File

@@ -438,6 +438,9 @@ def get_grouped_items(event, *, channel: SalesChannel, subevent=None, voucher=No
base_price_is='net' if event.settings.display_net_prices else 'gross') # backwards-compat
) if var.original_price or item.original_price else None
if not display_add_to_cart:
display_add_to_cart = not item.requires_seat and var.order_max > 0
var.current_unavailability_reason = var.unavailability_reason(has_voucher=voucher, subevent=subevent)
item.original_price = (
@@ -468,8 +471,6 @@ def get_grouped_items(event, *, channel: SalesChannel, subevent=None, voucher=No
item.best_variation_availability = max([v.cached_availability[0] for v in item.available_variations])
item._remove = not bool(item.available_variations)
if not item._remove and not display_add_to_cart:
display_add_to_cart = not item.requires_seat and any(v.order_max > 0 for v in item.available_variations)
if not quota_cache_existed and not voucher and not allow_addons and not base_qs_set and not filter_items and not filter_categories:
event.cache.set(quota_cache_key, quota_cache, 5)

View File

@@ -471,11 +471,10 @@ class WidgetAPIProductList(EventListMixin, View):
availability['color'] = 'red'
availability['text'] = gettext('Sale over')
availability['reason'] = 'over'
elif event.settings.presale_start_show_date and ev.effective_presale_start:
elif event.settings.presale_start_show_date and ev.presale_start:
availability['color'] = 'orange'
availability['text'] = gettext('from %(start_date)s') % {
'start_date': date_format(ev.effective_presale_start.astimezone(tz or event.timezone),
"SHORT_DATE_FORMAT")
'start_date': date_format(ev.presale_start.astimezone(tz or event.timezone), "SHORT_DATE_FORMAT")
}
availability['reason'] = 'soon'
else:

View File

@@ -347,53 +347,11 @@ if HAS_CELERY:
CELERY_RESULT_BACKEND = config.get('celery', 'backend')
if HAS_CELERY_BROKER_TRANSPORT_OPTS:
CELERY_BROKER_TRANSPORT_OPTIONS = loads(config.get('celery', 'broker_transport_options'))
else:
CELERY_BROKER_TRANSPORT_OPTIONS = {}
if HAS_CELERY_BACKEND_TRANSPORT_OPTS:
CELERY_RESULT_BACKEND_TRANSPORT_OPTIONS = loads(config.get('celery', 'backend_transport_options'))
CELERY_BROKER_CONNECTION_RETRY_ON_STARTUP = True
if CELERY_BROKER_URL.startswith("amqp://"):
# https://docs.celeryq.dev/en/latest/userguide/routing.html#routing-options-rabbitmq-priorities
# Enable priorities for all queues
CELERY_TASK_QUEUE_MAX_PRIORITY = 3
# On RabbitMQ, higher number is higher priority, and having less levels makes rabbitmq use less CPU and RAM
PRIORITY_CELERY_LOW = 1
PRIORITY_CELERY_MID = 2
PRIORITY_CELERY_HIGH = 3
PRIORITY_CELERY_LOWEST_FUNC = min
PRIORITY_CELERY_HIGHEST_FUNC = max
# Set default
CELERY_TASK_DEFAULT_PRIORITY = PRIORITY_CELERY_MID
elif CELERY_BROKER_URL.startswith("redis://"):
# https://docs.celeryq.dev/en/latest/userguide/routing.html#redis-message-priorities
CELERY_BROKER_TRANSPORT_OPTIONS.update({
"queue_order_strategy": "priority",
"sep": ":",
"priority_steps": [0, 4, 8]
})
# On redis, lower number is higher priority, and it appears that there are always levels 0-9 even though it
# is only really executed based on the 3 steps listed above.
PRIORITY_CELERY_LOW = 9
PRIORITY_CELERY_MID = 5
PRIORITY_CELERY_HIGH = 0
PRIORITY_CELERY_LOWEST_FUNC = max
PRIORITY_CELERY_HIGHEST_FUNC = min
CELERY_TASK_DEFAULT_PRIORITY = PRIORITY_CELERY_MID
else:
# No priority support assumed
PRIORITY_CELERY_LOW = 0
PRIORITY_CELERY_MID = 0
PRIORITY_CELERY_HIGH = 0
PRIORITY_CELERY_LOWEST_FUNC = min
PRIORITY_CELERY_HIGHEST_FUNC = max
else:
CELERY_TASK_ALWAYS_EAGER = True
PRIORITY_CELERY_LOW = 0
PRIORITY_CELERY_MID = 0
PRIORITY_CELERY_HIGH = 0
PRIORITY_CELERY_LOWEST_FUNC = min
PRIORITY_CELERY_HIGHEST_FUNC = max
CACHE_TICKETS_HOURS = config.getint('cache', 'tickets', fallback=24 * 3)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 429 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 842 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Some files were not shown because too many files have changed in this diff Show More