mirror of
https://github.com/pretix/pretix.git
synced 2025-12-29 18:02:26 +00:00
Compare commits
37 Commits
fix-datasy
...
mail-model
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77380214a8 | ||
|
|
ad8ed599dc | ||
|
|
4c2efa0a97 | ||
|
|
6efcd4b983 | ||
|
|
c29b7f28f1 | ||
|
|
871a8a2620 | ||
|
|
b7803565d6 | ||
|
|
f3b6627e63 | ||
|
|
574513550d | ||
|
|
f145d447a2 | ||
|
|
72b9b49b9d | ||
|
|
6d20d0e840 | ||
|
|
4a662a1aa1 | ||
|
|
8213b09847 | ||
|
|
c54f776b39 | ||
|
|
fdd03536f2 | ||
|
|
44303a0030 | ||
|
|
5ba10416ce | ||
|
|
efa117c836 | ||
|
|
70cd2265db | ||
|
|
b5afbfa1bf | ||
|
|
2dffe0e2c8 | ||
|
|
df0e0f9115 | ||
|
|
2fc47c5d71 | ||
|
|
c23d2e5504 | ||
|
|
58c7e3d316 | ||
|
|
2d5c3fbea6 | ||
|
|
222851620e | ||
|
|
9ac772b2f3 | ||
|
|
1408f31ec5 | ||
|
|
04f32284a8 | ||
|
|
318b80c3a5 | ||
|
|
102d172942 | ||
|
|
c084698821 | ||
|
|
edffe5c9dd | ||
|
|
09e9273a57 | ||
|
|
24ac588119 |
@@ -90,7 +90,7 @@ dependencies = [
|
||||
"qrcode==8.2",
|
||||
"redis==6.4.*",
|
||||
"reportlab==4.4.*",
|
||||
"requests==2.31.*",
|
||||
"requests==2.32.*",
|
||||
"sentry-sdk==2.35.*",
|
||||
"sepaxml==2.6.*",
|
||||
"stripe==7.9.*",
|
||||
|
||||
@@ -49,7 +49,7 @@ from pretix.base.plugins import (
|
||||
PLUGIN_LEVEL_EVENT, PLUGIN_LEVEL_EVENT_ORGANIZER_HYBRID,
|
||||
PLUGIN_LEVEL_ORGANIZER,
|
||||
)
|
||||
from pretix.base.services.mail import SendMailException, mail
|
||||
from pretix.base.services.mail import mail
|
||||
from pretix.base.settings import validate_organizer_settings
|
||||
from pretix.helpers.urls import build_absolute_uri as build_global_uri
|
||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||
@@ -363,24 +363,21 @@ class TeamInviteSerializer(serializers.ModelSerializer):
|
||||
)
|
||||
|
||||
def _send_invite(self, instance):
|
||||
try:
|
||||
mail(
|
||||
instance.email,
|
||||
_('pretix account invitation'),
|
||||
'pretixcontrol/email/invitation.txt',
|
||||
{
|
||||
'user': self,
|
||||
'organizer': self.context['organizer'].name,
|
||||
'team': instance.team.name,
|
||||
'url': build_global_uri('control:auth.invite', kwargs={
|
||||
'token': instance.token
|
||||
})
|
||||
},
|
||||
event=None,
|
||||
locale=get_language_without_region() # TODO: expose?
|
||||
)
|
||||
except SendMailException:
|
||||
pass # Already logged
|
||||
mail(
|
||||
instance.email,
|
||||
_('pretix account invitation'),
|
||||
'pretixcontrol/email/invitation.txt',
|
||||
{
|
||||
'user': self,
|
||||
'organizer': self.context['organizer'].name,
|
||||
'team': instance.team.name,
|
||||
'url': build_global_uri('control:auth.invite', kwargs={
|
||||
'token': instance.token
|
||||
})
|
||||
},
|
||||
event=None,
|
||||
locale=get_language_without_region() # TODO: expose?
|
||||
)
|
||||
|
||||
def create(self, validated_data):
|
||||
if 'email' in validated_data:
|
||||
|
||||
@@ -90,7 +90,6 @@ from pretix.base.services.invoices import (
|
||||
generate_cancellation, generate_invoice, invoice_pdf, invoice_qualified,
|
||||
regenerate_invoice, transmit_invoice,
|
||||
)
|
||||
from pretix.base.services.mail import SendMailException
|
||||
from pretix.base.services.orders import (
|
||||
OrderChangeManager, OrderError, _order_placed_email,
|
||||
_order_placed_email_attendee, approve_order, cancel_order, deny_order,
|
||||
@@ -438,8 +437,6 @@ class EventOrderViewSet(OrderViewSetMixin, viewsets.ModelViewSet):
|
||||
return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
except PaymentException as e:
|
||||
return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
except SendMailException:
|
||||
pass
|
||||
|
||||
return self.retrieve(request, [], **kwargs)
|
||||
return Response(
|
||||
@@ -633,10 +630,7 @@ class EventOrderViewSet(OrderViewSetMixin, viewsets.ModelViewSet):
|
||||
order = self.get_object()
|
||||
if not order.email:
|
||||
return Response({'detail': 'There is no email address associated with this order.'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
try:
|
||||
order.resend_link(user=self.request.user, auth=self.request.auth)
|
||||
except SendMailException:
|
||||
return Response({'detail': _('There was an error sending the mail. Please try again later.')}, status=status.HTTP_503_SERVICE_UNAVAILABLE)
|
||||
order.resend_link(user=self.request.user, auth=self.request.auth)
|
||||
|
||||
return Response(
|
||||
status=status.HTTP_204_NO_CONTENT
|
||||
@@ -1609,8 +1603,6 @@ class PaymentViewSet(CreateModelMixin, viewsets.ReadOnlyModelViewSet):
|
||||
)
|
||||
except Quota.QuotaExceededException:
|
||||
pass
|
||||
except SendMailException:
|
||||
pass
|
||||
|
||||
serializer = OrderPaymentSerializer(r, context=serializer.context)
|
||||
|
||||
@@ -1648,8 +1640,6 @@ class PaymentViewSet(CreateModelMixin, viewsets.ReadOnlyModelViewSet):
|
||||
return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
except PaymentException as e:
|
||||
return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
except SendMailException:
|
||||
pass
|
||||
return self.retrieve(request, [], **kwargs)
|
||||
|
||||
@action(detail=True, methods=['POST'])
|
||||
|
||||
@@ -33,7 +33,7 @@ from pretix.base.invoicing.transmission import (
|
||||
transmission_types,
|
||||
)
|
||||
from pretix.base.models import Invoice, InvoiceAddress
|
||||
from pretix.base.services.mail import SendMailException, mail, render_mail
|
||||
from pretix.base.services.mail import mail, render_mail
|
||||
from pretix.helpers.format import format_map
|
||||
|
||||
|
||||
@@ -133,41 +133,37 @@ class EmailTransmissionProvider(TransmissionProvider):
|
||||
template = invoice.order.event.settings.get('mail_text_order_invoice', as_type=LazyI18nString)
|
||||
subject = invoice.order.event.settings.get('mail_subject_order_invoice', as_type=LazyI18nString)
|
||||
|
||||
try:
|
||||
# Do not set to completed because that is done by the email sending task
|
||||
subject = format_map(subject, context)
|
||||
email_content = render_mail(template, context)
|
||||
mail(
|
||||
[recipient],
|
||||
subject,
|
||||
template,
|
||||
context=context,
|
||||
event=invoice.order.event,
|
||||
locale=invoice.order.locale,
|
||||
order=invoice.order,
|
||||
invoices=[invoice],
|
||||
attach_tickets=False,
|
||||
auto_email=True,
|
||||
attach_ical=False,
|
||||
plain_text_only=True,
|
||||
no_order_links=True,
|
||||
)
|
||||
except SendMailException:
|
||||
raise
|
||||
else:
|
||||
invoice.order.log_action(
|
||||
'pretix.event.order.email.invoice',
|
||||
user=None,
|
||||
auth=None,
|
||||
data={
|
||||
'subject': subject,
|
||||
'message': email_content,
|
||||
'position': None,
|
||||
'recipient': recipient,
|
||||
'invoices': [invoice.pk],
|
||||
'attach_tickets': False,
|
||||
'attach_ical': False,
|
||||
'attach_other_files': [],
|
||||
'attach_cached_files': [],
|
||||
}
|
||||
)
|
||||
# Do not set to completed because that is done by the email sending task
|
||||
subject = format_map(subject, context)
|
||||
email_content = render_mail(template, context)
|
||||
mail(
|
||||
[recipient],
|
||||
subject,
|
||||
template,
|
||||
context=context,
|
||||
event=invoice.order.event,
|
||||
locale=invoice.order.locale,
|
||||
order=invoice.order,
|
||||
invoices=[invoice],
|
||||
attach_tickets=False,
|
||||
auto_email=True,
|
||||
attach_ical=False,
|
||||
plain_text_only=True,
|
||||
no_order_links=True,
|
||||
)
|
||||
invoice.order.log_action(
|
||||
'pretix.event.order.email.invoice',
|
||||
user=None,
|
||||
auth=None,
|
||||
data={
|
||||
'subject': subject,
|
||||
'message': email_content,
|
||||
'position': None,
|
||||
'recipient': recipient,
|
||||
'invoices': [invoice.pk],
|
||||
'attach_tickets': False,
|
||||
'attach_ical': False,
|
||||
'attach_other_files': [],
|
||||
'attach_cached_files': [],
|
||||
}
|
||||
)
|
||||
|
||||
112
src/pretix/base/migrations/0289_outgoingmail.py
Normal file
112
src/pretix/base/migrations/0289_outgoingmail.py
Normal file
@@ -0,0 +1,112 @@
|
||||
# Generated by Django 4.2.17 on 2025-09-04 12:35
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("pretixbase", "0288_invoice_transmission"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="OutgoingMail",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True, primary_key=True, serialize=False
|
||||
),
|
||||
),
|
||||
("status", models.CharField(default="queued", max_length=200)),
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
("sent", models.DateTimeField(blank=True, null=True)),
|
||||
("error", models.TextField(null=True)),
|
||||
("error_detail", models.TextField(null=True)),
|
||||
("subject", models.TextField()),
|
||||
("body_plain", models.TextField()),
|
||||
("body_html", models.TextField()),
|
||||
("sender", models.CharField(max_length=500)),
|
||||
("headers", models.JSONField(default=dict)),
|
||||
("to", models.JSONField(default=list)),
|
||||
("cc", models.JSONField(default=list)),
|
||||
("bcc", models.JSONField(default=list)),
|
||||
("should_attach_tickets", models.BooleanField(default=False)),
|
||||
("should_attach_ical", models.BooleanField(default=False)),
|
||||
("should_attach_other_files", models.JSONField(default=list)),
|
||||
("actual_attachments", models.JSONField(default=list)),
|
||||
(
|
||||
"customer",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="outgoing_mails",
|
||||
to="pretixbase.customer",
|
||||
),
|
||||
),
|
||||
(
|
||||
"event",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="outgoing_mails",
|
||||
to="pretixbase.event",
|
||||
),
|
||||
),
|
||||
(
|
||||
"order",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="outgoing_mails",
|
||||
to="pretixbase.order",
|
||||
),
|
||||
),
|
||||
(
|
||||
"orderposition",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="outgoing_mails",
|
||||
to="pretixbase.orderposition",
|
||||
),
|
||||
),
|
||||
(
|
||||
"organizer",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="outgoing_mails",
|
||||
to="pretixbase.organizer",
|
||||
),
|
||||
),
|
||||
(
|
||||
"should_attach_cached_files",
|
||||
models.ManyToManyField(
|
||||
related_name="outgoing_mails", to="pretixbase.cachedfile"
|
||||
),
|
||||
),
|
||||
(
|
||||
"should_attach_invoices",
|
||||
models.ManyToManyField(
|
||||
related_name="outgoing_mails", to="pretixbase.invoice"
|
||||
),
|
||||
),
|
||||
(
|
||||
"user",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="outgoing_mails",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"ordering": ("-created",),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -40,6 +40,7 @@ from .items import (
|
||||
SubEventItem, SubEventItemVariation, itempicture_upload_to,
|
||||
)
|
||||
from .log import LogEntry
|
||||
from .mail import OutgoingMail
|
||||
from .media import ReusableMedium
|
||||
from .memberships import Membership, MembershipType
|
||||
from .notifications import NotificationSetting
|
||||
|
||||
@@ -331,27 +331,24 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
|
||||
return self.email
|
||||
|
||||
def send_security_notice(self, messages, email=None):
|
||||
from pretix.base.services.mail import SendMailException, mail
|
||||
from pretix.base.services.mail import mail
|
||||
|
||||
try:
|
||||
with language(self.locale):
|
||||
msg = '- ' + '\n- '.join(str(m) for m in messages)
|
||||
with language(self.locale):
|
||||
msg = '- ' + '\n- '.join(str(m) for m in messages)
|
||||
|
||||
mail(
|
||||
email or self.email,
|
||||
_('Account information changed'),
|
||||
'pretixcontrol/email/security_notice.txt',
|
||||
{
|
||||
'user': self,
|
||||
'messages': msg,
|
||||
'url': build_absolute_uri('control:user.settings')
|
||||
},
|
||||
event=None,
|
||||
user=self,
|
||||
locale=self.locale
|
||||
)
|
||||
except SendMailException:
|
||||
pass # Already logged
|
||||
mail(
|
||||
email or self.email,
|
||||
_('Account information changed'),
|
||||
'pretixcontrol/email/security_notice.txt',
|
||||
{
|
||||
'user': self,
|
||||
'messages': msg,
|
||||
'url': build_absolute_uri('control:user.settings')
|
||||
},
|
||||
event=None,
|
||||
user=self,
|
||||
locale=self.locale
|
||||
)
|
||||
|
||||
def send_password_reset(self):
|
||||
from pretix.base.services.mail import mail
|
||||
|
||||
149
src/pretix/base/models/mail.py
Normal file
149
src/pretix/base/models/mail.py
Normal file
@@ -0,0 +1,149 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-2021 rami.io GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from django.core.mail import get_connection
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_scopes import scope, scopes_disabled
|
||||
|
||||
|
||||
class OutgoingMail(models.Model):
|
||||
STATUS_QUEUED = "queued"
|
||||
STATUS_INFLIGHT = "inflight"
|
||||
STATUS_AWAWITING_RETRY = "awaiting_retry"
|
||||
STATUS_FAILED = "failed"
|
||||
STATUS_SENT = "sent"
|
||||
STATUS_CHOICES = (
|
||||
(STATUS_QUEUED, _("queued")),
|
||||
(STATUS_INFLIGHT, _("being sent")),
|
||||
(STATUS_AWAWITING_RETRY, _("awaiting retry")),
|
||||
(STATUS_FAILED, _("failed")),
|
||||
(STATUS_SENT, _("sent")),
|
||||
)
|
||||
|
||||
status = models.CharField(max_length=200, choices=STATUS_CHOICES, default=STATUS_QUEUED)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
sent = models.DateTimeField(null=True, blank=True)
|
||||
error = models.TextField(null=True, blank=True)
|
||||
error_detail = models.TextField(null=True, blank=True)
|
||||
|
||||
organizer = models.ForeignKey(
|
||||
'pretixbase.Organizer',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='outgoing_mails',
|
||||
null=True, blank=True,
|
||||
)
|
||||
event = models.ForeignKey(
|
||||
'pretixbase.Event',
|
||||
on_delete=models.SET_NULL, # todo think, only for non-queued!
|
||||
related_name='outgoing_mails',
|
||||
null=True, blank=True,
|
||||
)
|
||||
order = models.ForeignKey(
|
||||
'pretixbase.Order',
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='outgoing_mails',
|
||||
null=True, blank=True,
|
||||
)
|
||||
orderposition = models.ForeignKey(
|
||||
'pretixbase.OrderPosition',
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='outgoing_mails',
|
||||
null=True, blank=True,
|
||||
)
|
||||
customer = models.ForeignKey(
|
||||
'pretixbase.Customer',
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='outgoing_mails',
|
||||
null=True, blank=True,
|
||||
)
|
||||
user = models.ForeignKey(
|
||||
'pretixbase.User',
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='outgoing_mails',
|
||||
null=True, blank=True,
|
||||
)
|
||||
|
||||
subject = models.TextField()
|
||||
body_plain = models.TextField()
|
||||
body_html = models.TextField()
|
||||
sender = models.CharField(max_length=500)
|
||||
headers = models.JSONField(default=dict)
|
||||
to = models.JSONField(default=list)
|
||||
cc = models.JSONField(default=list)
|
||||
bcc = models.JSONField(default=list)
|
||||
|
||||
should_attach_invoices = models.ManyToManyField(
|
||||
'pretixbase.Invoice',
|
||||
related_name='outgoing_mails'
|
||||
)
|
||||
should_attach_tickets = models.BooleanField(default=False)
|
||||
should_attach_ical = models.BooleanField(default=False)
|
||||
should_attach_cached_files = models.ManyToManyField(
|
||||
'pretixbase.CachedFile',
|
||||
related_name='outgoing_mails',
|
||||
)
|
||||
should_attach_other_files = models.JSONField(default=list)
|
||||
|
||||
actual_attachments = models.JSONField(default=list)
|
||||
|
||||
class Meta:
|
||||
ordering = ('-created',)
|
||||
|
||||
def get_mail_backend(self):
|
||||
if self.event:
|
||||
return self.event.get_mail_backend()
|
||||
elif self.organizer:
|
||||
return self.organizer.get_mail_backend()
|
||||
else:
|
||||
return get_connection(fail_silently=False)
|
||||
|
||||
def scope_manager(self):
|
||||
if self.organizer:
|
||||
return scope(organizer=self.organizer) # noqa
|
||||
else:
|
||||
return scopes_disabled() # noqa
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.orderposition_id and not self.order_id:
|
||||
self.order = self.orderposition.order
|
||||
if self.order_id and not self.event_id:
|
||||
self.event = self.order.event
|
||||
if self.event_id and not self.organizer_id:
|
||||
self.organizer = self.event.organizer
|
||||
if self.customer_id and not self.organizer_id:
|
||||
self.organizer = self.customer.organizer
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def log_parameters(self):
|
||||
if self.order:
|
||||
error_log_action_type = 'pretix.event.order.email.error'
|
||||
log_target = self.order
|
||||
elif self.customer:
|
||||
error_log_action_type = 'pretix.customer.email.error'
|
||||
log_target = self.customer
|
||||
elif self.user:
|
||||
error_log_action_type = 'pretix.user.email.error'
|
||||
log_target = self.user
|
||||
else:
|
||||
error_log_action_type = 'pretix.email.error'
|
||||
log_target = None
|
||||
return log_target, error_log_action_type
|
||||
@@ -1162,9 +1162,7 @@ class Order(LockModel, LoggedModel):
|
||||
only be attached for this position and child positions, the link will only point to the
|
||||
position and the attendee email will be used if available.
|
||||
"""
|
||||
from pretix.base.services.mail import (
|
||||
SendMailException, mail, render_mail,
|
||||
)
|
||||
from pretix.base.services.mail import mail, render_mail
|
||||
|
||||
if not self.email and not (position and position.attendee_email):
|
||||
return
|
||||
@@ -1174,35 +1172,31 @@ class Order(LockModel, LoggedModel):
|
||||
if position and position.attendee_email:
|
||||
recipient = position.attendee_email
|
||||
|
||||
try:
|
||||
email_content = render_mail(template, context)
|
||||
subject = format_map(subject, context)
|
||||
mail(
|
||||
recipient, subject, template, context,
|
||||
self.event, self.locale, self, headers=headers, sender=sender,
|
||||
invoices=invoices, attach_tickets=attach_tickets,
|
||||
position=position, auto_email=auto_email, attach_ical=attach_ical,
|
||||
attach_other_files=attach_other_files, attach_cached_files=attach_cached_files,
|
||||
)
|
||||
except SendMailException:
|
||||
raise
|
||||
else:
|
||||
self.log_action(
|
||||
log_entry_type,
|
||||
user=user,
|
||||
auth=auth,
|
||||
data={
|
||||
'subject': subject,
|
||||
'message': email_content,
|
||||
'position': position.positionid if position else None,
|
||||
'recipient': recipient,
|
||||
'invoices': [i.pk for i in invoices] if invoices else [],
|
||||
'attach_tickets': attach_tickets,
|
||||
'attach_ical': attach_ical,
|
||||
'attach_other_files': attach_other_files,
|
||||
'attach_cached_files': [cf.filename for cf in attach_cached_files] if attach_cached_files else [],
|
||||
}
|
||||
)
|
||||
email_content = render_mail(template, context)
|
||||
subject = format_map(subject, context)
|
||||
mail(
|
||||
recipient, subject, template, context,
|
||||
self.event, self.locale, self, headers=headers, sender=sender,
|
||||
invoices=invoices, attach_tickets=attach_tickets,
|
||||
position=position, auto_email=auto_email, attach_ical=attach_ical,
|
||||
attach_other_files=attach_other_files, attach_cached_files=attach_cached_files,
|
||||
)
|
||||
self.log_action(
|
||||
log_entry_type,
|
||||
user=user,
|
||||
auth=auth,
|
||||
data={
|
||||
'subject': subject,
|
||||
'message': email_content,
|
||||
'position': position.positionid if position else None,
|
||||
'recipient': recipient,
|
||||
'invoices': [i.pk for i in invoices] if invoices else [],
|
||||
'attach_tickets': attach_tickets,
|
||||
'attach_ical': attach_ical,
|
||||
'attach_other_files': attach_other_files,
|
||||
'attach_cached_files': [cf.filename for cf in attach_cached_files] if attach_cached_files else [],
|
||||
}
|
||||
)
|
||||
|
||||
def resend_link(self, user=None, auth=None):
|
||||
with language(self.locale, self.event.settings.region):
|
||||
@@ -1984,40 +1978,30 @@ class OrderPayment(models.Model):
|
||||
transmit_invoice.apply_async(args=(self.order.event_id, invoice.pk, False))
|
||||
|
||||
def _send_paid_mail_attendee(self, position, user):
|
||||
from pretix.base.services.mail import SendMailException
|
||||
|
||||
with language(self.order.locale, self.order.event.settings.region):
|
||||
email_template = self.order.event.settings.mail_text_order_paid_attendee
|
||||
email_subject = self.order.event.settings.mail_subject_order_paid_attendee
|
||||
email_context = get_email_context(event=self.order.event, order=self.order, position=position)
|
||||
try:
|
||||
position.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.order_paid', user,
|
||||
invoices=[],
|
||||
attach_tickets=True,
|
||||
attach_ical=self.order.event.settings.mail_attach_ical
|
||||
)
|
||||
except SendMailException:
|
||||
logger.exception('Order paid email could not be sent')
|
||||
position.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.order_paid', user,
|
||||
invoices=[],
|
||||
attach_tickets=True,
|
||||
attach_ical=self.order.event.settings.mail_attach_ical
|
||||
)
|
||||
|
||||
def _send_paid_mail(self, invoice, user, mail_text):
|
||||
from pretix.base.services.mail import SendMailException
|
||||
|
||||
with language(self.order.locale, self.order.event.settings.region):
|
||||
email_template = self.order.event.settings.mail_text_order_paid
|
||||
email_subject = self.order.event.settings.mail_subject_order_paid
|
||||
email_context = get_email_context(event=self.order.event, order=self.order, payment_info=mail_text)
|
||||
try:
|
||||
self.order.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.order_paid', user,
|
||||
invoices=[invoice] if invoice else [],
|
||||
attach_tickets=True,
|
||||
attach_ical=self.order.event.settings.mail_attach_ical
|
||||
)
|
||||
except SendMailException:
|
||||
logger.exception('Order paid email could not be sent')
|
||||
self.order.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.order_paid', user,
|
||||
invoices=[invoice] if invoice else [],
|
||||
attach_tickets=True,
|
||||
attach_ical=self.order.event.settings.mail_attach_ical
|
||||
)
|
||||
|
||||
@property
|
||||
def refunded_amount(self):
|
||||
@@ -2835,45 +2819,39 @@ class OrderPosition(AbstractPosition):
|
||||
:param attach_tickets: Attach tickets of this order, if they are existing and ready to download
|
||||
:param attach_ical: Attach relevant ICS files
|
||||
"""
|
||||
from pretix.base.services.mail import (
|
||||
SendMailException, mail, render_mail,
|
||||
)
|
||||
from pretix.base.services.mail import mail, render_mail
|
||||
|
||||
if not self.attendee_email:
|
||||
return
|
||||
|
||||
with language(self.order.locale, self.order.event.settings.region):
|
||||
recipient = self.attendee_email
|
||||
try:
|
||||
email_content = render_mail(template, context)
|
||||
subject = format_map(subject, context)
|
||||
mail(
|
||||
recipient, subject, template, context,
|
||||
self.event, self.order.locale, order=self.order, headers=headers, sender=sender,
|
||||
position=self,
|
||||
invoices=invoices,
|
||||
attach_tickets=attach_tickets,
|
||||
attach_ical=attach_ical,
|
||||
attach_other_files=attach_other_files,
|
||||
)
|
||||
except SendMailException:
|
||||
raise
|
||||
else:
|
||||
self.order.log_action(
|
||||
log_entry_type,
|
||||
user=user,
|
||||
auth=auth,
|
||||
data={
|
||||
'subject': subject,
|
||||
'message': email_content,
|
||||
'recipient': recipient,
|
||||
'invoices': [i.pk for i in invoices] if invoices else [],
|
||||
'attach_tickets': attach_tickets,
|
||||
'attach_ical': attach_ical,
|
||||
'attach_other_files': attach_other_files,
|
||||
'attach_cached_files': [],
|
||||
}
|
||||
)
|
||||
email_content = render_mail(template, context)
|
||||
subject = format_map(subject, context)
|
||||
mail(
|
||||
recipient, subject, template, context,
|
||||
self.event, self.order.locale, order=self.order, headers=headers, sender=sender,
|
||||
position=self,
|
||||
invoices=invoices,
|
||||
attach_tickets=attach_tickets,
|
||||
attach_ical=attach_ical,
|
||||
attach_other_files=attach_other_files,
|
||||
)
|
||||
self.order.log_action(
|
||||
log_entry_type,
|
||||
user=user,
|
||||
auth=auth,
|
||||
data={
|
||||
'subject': subject,
|
||||
'message': email_content,
|
||||
'recipient': recipient,
|
||||
'invoices': [i.pk for i in invoices] if invoices else [],
|
||||
'attach_tickets': attach_tickets,
|
||||
'attach_ical': attach_ical,
|
||||
'attach_other_files': attach_other_files,
|
||||
'attach_cached_files': [],
|
||||
}
|
||||
)
|
||||
|
||||
def resend_link(self, user=None, auth=None):
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ from phonenumber_field.modelfields import PhoneNumberField
|
||||
from pretix.base.email import get_email_context
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import User, Voucher
|
||||
from pretix.base.services.mail import SendMailException, mail, render_mail
|
||||
from pretix.base.services.mail import mail, render_mail
|
||||
|
||||
from ...helpers.format import format_map
|
||||
from ...helpers.names import build_name
|
||||
@@ -265,34 +265,30 @@ class WaitingListEntry(LoggedModel):
|
||||
with language(self.locale, self.event.settings.region):
|
||||
recipient = self.email
|
||||
|
||||
try:
|
||||
email_content = render_mail(template, context)
|
||||
subject = format_map(subject, context)
|
||||
mail(
|
||||
recipient, subject, template, context,
|
||||
self.event,
|
||||
self.locale,
|
||||
headers=headers,
|
||||
sender=sender,
|
||||
auto_email=auto_email,
|
||||
attach_other_files=attach_other_files,
|
||||
attach_cached_files=attach_cached_files,
|
||||
)
|
||||
except SendMailException:
|
||||
raise
|
||||
else:
|
||||
self.log_action(
|
||||
log_entry_type,
|
||||
user=user,
|
||||
auth=auth,
|
||||
data={
|
||||
'subject': subject,
|
||||
'message': email_content,
|
||||
'recipient': recipient,
|
||||
'attach_other_files': attach_other_files,
|
||||
'attach_cached_files': [cf.filename for cf in attach_cached_files] if attach_cached_files else [],
|
||||
}
|
||||
)
|
||||
email_content = render_mail(template, context)
|
||||
subject = format_map(subject, context)
|
||||
mail(
|
||||
recipient, subject, template, context,
|
||||
self.event,
|
||||
self.locale,
|
||||
headers=headers,
|
||||
sender=sender,
|
||||
auto_email=auto_email,
|
||||
attach_other_files=attach_other_files,
|
||||
attach_cached_files=attach_cached_files,
|
||||
)
|
||||
self.log_action(
|
||||
log_entry_type,
|
||||
user=user,
|
||||
auth=auth,
|
||||
data={
|
||||
'subject': subject,
|
||||
'message': email_content,
|
||||
'recipient': recipient,
|
||||
'attach_other_files': attach_other_files,
|
||||
'attach_cached_files': [cf.filename for cf in attach_cached_files] if attach_cached_files else [],
|
||||
}
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def clean_itemvar(event, item, variation):
|
||||
|
||||
@@ -35,7 +35,7 @@ from pretix.base.models import (
|
||||
SubEvent, TaxRule, User, WaitingListEntry,
|
||||
)
|
||||
from pretix.base.services.locking import LockTimeoutException
|
||||
from pretix.base.services.mail import SendMailException, mail
|
||||
from pretix.base.services.mail import mail
|
||||
from pretix.base.services.orders import (
|
||||
OrderChangeManager, OrderError, _cancel_order, _try_auto_refund,
|
||||
)
|
||||
@@ -51,17 +51,14 @@ logger = logging.getLogger(__name__)
|
||||
def _send_wle_mail(wle: WaitingListEntry, subject: LazyI18nString, message: LazyI18nString, subevent: SubEvent):
|
||||
with language(wle.locale, wle.event.settings.region):
|
||||
email_context = get_email_context(event_or_subevent=subevent or wle.event, event=wle.event)
|
||||
try:
|
||||
mail(
|
||||
wle.email,
|
||||
format_map(subject, email_context),
|
||||
message,
|
||||
email_context,
|
||||
wle.event,
|
||||
locale=wle.locale
|
||||
)
|
||||
except SendMailException:
|
||||
logger.exception('Waiting list canceled email could not be sent')
|
||||
mail(
|
||||
wle.email,
|
||||
format_map(subject, email_context),
|
||||
message,
|
||||
email_context,
|
||||
wle.event,
|
||||
locale=wle.locale
|
||||
)
|
||||
|
||||
|
||||
def _send_mail(order: Order, subject: LazyI18nString, message: LazyI18nString, subevent: SubEvent,
|
||||
@@ -75,14 +72,11 @@ def _send_mail(order: Order, subject: LazyI18nString, message: LazyI18nString, s
|
||||
email_context = get_email_context(event_or_subevent=subevent or order.event, refund_amount=refund_amount,
|
||||
order=order, position_or_address=ia, event=order.event)
|
||||
real_subject = format_map(subject, email_context)
|
||||
try:
|
||||
order.send_mail(
|
||||
real_subject, message, email_context,
|
||||
'pretix.event.order.email.event_canceled',
|
||||
user,
|
||||
)
|
||||
except SendMailException:
|
||||
logger.exception('Order canceled email could not be sent')
|
||||
order.send_mail(
|
||||
real_subject, message, email_context,
|
||||
'pretix.event.order.email.event_canceled',
|
||||
user,
|
||||
)
|
||||
|
||||
for p in positions:
|
||||
if subevent and p.subevent_id != subevent.id:
|
||||
@@ -95,15 +89,12 @@ def _send_mail(order: Order, subject: LazyI18nString, message: LazyI18nString, s
|
||||
refund_amount=refund_amount,
|
||||
position_or_address=p,
|
||||
order=order, position=p)
|
||||
try:
|
||||
order.send_mail(
|
||||
real_subject, message, email_context,
|
||||
'pretix.event.order.email.event_canceled',
|
||||
position=p,
|
||||
user=user
|
||||
)
|
||||
except SendMailException:
|
||||
logger.exception('Order canceled email could not be sent to attendee')
|
||||
order.send_mail(
|
||||
real_subject, message, email_context,
|
||||
'pretix.event.order.email.event_canceled',
|
||||
position=p,
|
||||
user=user
|
||||
)
|
||||
|
||||
|
||||
@app.task(base=ProfiledEventTask, bind=True, max_retries=5, default_retry_delay=1, throws=(OrderError,))
|
||||
|
||||
@@ -42,7 +42,7 @@ import smtplib
|
||||
import warnings
|
||||
from email.mime.image import MIMEImage
|
||||
from email.utils import formataddr
|
||||
from typing import Any, Dict, List, Sequence, Union
|
||||
from typing import Any, Dict, Sequence, Union
|
||||
from urllib.parse import urljoin, urlparse
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
@@ -52,16 +52,13 @@ from celery import chain
|
||||
from celery.exceptions import MaxRetriesExceededError
|
||||
from django.conf import settings
|
||||
from django.core.files.storage import default_storage
|
||||
from django.core.mail import (
|
||||
EmailMultiAlternatives, SafeMIMEMultipart, get_connection,
|
||||
)
|
||||
from django.core.mail import EmailMultiAlternatives, SafeMIMEMultipart
|
||||
from django.core.mail.message import SafeMIMEText
|
||||
from django.db import transaction
|
||||
from django.template.loader import get_template
|
||||
from django.utils.html import escape
|
||||
from django.utils.timezone import now, override
|
||||
from django.utils.translation import gettext as _, pgettext
|
||||
from django_scopes import scope, scopes_disabled
|
||||
from i18nfield.strings import LazyI18nString
|
||||
from text_unidecode import unidecode
|
||||
|
||||
@@ -69,13 +66,15 @@ from pretix.base.email import ClassicMailRenderer
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import (
|
||||
CachedFile, Customer, Event, Invoice, InvoiceAddress, Order, OrderPosition,
|
||||
Organizer, User,
|
||||
Organizer,
|
||||
)
|
||||
from pretix.base.models.mail import OutgoingMail
|
||||
from pretix.base.services.invoices import invoice_pdf_task
|
||||
from pretix.base.services.tasks import TransactionAwareTask
|
||||
from pretix.base.services.tickets import get_tickets_for_order
|
||||
from pretix.base.signals import email_filter, global_email_filter
|
||||
from pretix.celery_app import app
|
||||
from pretix.helpers import OF_SELF
|
||||
from pretix.helpers.format import SafeFormatter, format_map
|
||||
from pretix.helpers.hierarkey import clean_filename
|
||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||
@@ -92,6 +91,9 @@ class TolerantDict(dict):
|
||||
|
||||
|
||||
class SendMailException(Exception):
|
||||
"""
|
||||
Deprecated, not thrown any more.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@@ -203,161 +205,119 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
|
||||
if no_order_links and not plain_text_only:
|
||||
raise ValueError('If you set no_order_links, you also need to set plain_text_only.')
|
||||
|
||||
settings_holder = event or organizer
|
||||
|
||||
headers = headers or {}
|
||||
if auto_email:
|
||||
headers['X-Auto-Response-Suppress'] = 'OOF, NRN, AutoReply, RN'
|
||||
headers['Auto-Submitted'] = 'auto-generated'
|
||||
headers.setdefault('X-Mailer', 'pretix')
|
||||
|
||||
bcc = list(bcc or [])
|
||||
if settings_holder and settings_holder.settings.mail_bcc:
|
||||
for bcc_mail in settings_holder.settings.mail_bcc.split(','):
|
||||
bcc.append(bcc_mail.strip())
|
||||
|
||||
if (settings_holder
|
||||
and settings_holder.settings.mail_from in (settings.DEFAULT_FROM_EMAIL, settings.MAIL_FROM_ORGANIZERS)
|
||||
and settings_holder.settings.contact_mail and not headers.get('Reply-To')):
|
||||
headers['Reply-To'] = settings_holder.settings.contact_mail
|
||||
|
||||
if settings_holder:
|
||||
timezone = settings_holder.timezone
|
||||
elif user:
|
||||
timezone = ZoneInfo(user.timezone)
|
||||
else:
|
||||
timezone = ZoneInfo(settings.TIME_ZONE)
|
||||
|
||||
if event and attach_tickets and not event.settings.mail_attach_tickets:
|
||||
attach_tickets = False
|
||||
|
||||
with language(locale):
|
||||
if isinstance(context, dict) and order:
|
||||
try:
|
||||
context.update({
|
||||
'invoice_name': order.invoice_address.name,
|
||||
'invoice_company': order.invoice_address.company
|
||||
})
|
||||
except InvoiceAddress.DoesNotExist:
|
||||
context.update({
|
||||
'invoice_name': '',
|
||||
'invoice_company': ''
|
||||
})
|
||||
renderer = ClassicMailRenderer(None, organizer)
|
||||
content_plain = body_plain = render_mail(template, context)
|
||||
subject = str(subject).format_map(TolerantDict(context))
|
||||
sender = (
|
||||
sender or
|
||||
(event.settings.get('mail_from') if event else None) or
|
||||
(organizer.settings.get('mail_from') if organizer else None) or
|
||||
settings.MAIL_FROM
|
||||
)
|
||||
if event:
|
||||
sender_name = clean_sender_name(event.settings.mail_from_name or str(event.name))
|
||||
sender = formataddr((sender_name, sender))
|
||||
elif organizer:
|
||||
sender_name = clean_sender_name(organizer.settings.mail_from_name or str(organizer.name))
|
||||
sender = formataddr((sender_name, sender))
|
||||
else:
|
||||
sender = formataddr((clean_sender_name(settings.PRETIX_INSTANCE_NAME), sender))
|
||||
|
||||
subject = raw_subject = str(subject).replace('\n', ' ').replace('\r', '')[:900]
|
||||
signature = ""
|
||||
|
||||
bcc = list(bcc or [])
|
||||
|
||||
settings_holder = event or organizer
|
||||
|
||||
if event:
|
||||
timezone = event.timezone
|
||||
elif user:
|
||||
timezone = ZoneInfo(user.timezone)
|
||||
elif organizer:
|
||||
timezone = organizer.timezone
|
||||
else:
|
||||
timezone = ZoneInfo(settings.TIME_ZONE)
|
||||
_autoextend_context(context, order)
|
||||
|
||||
# Build raw content
|
||||
content_plain = render_mail(template, context)
|
||||
if settings_holder:
|
||||
if settings_holder.settings.mail_bcc:
|
||||
for bcc_mail in settings_holder.settings.mail_bcc.split(','):
|
||||
bcc.append(bcc_mail.strip())
|
||||
|
||||
if settings_holder.settings.mail_from in (settings.DEFAULT_FROM_EMAIL, settings.MAIL_FROM_ORGANIZERS) \
|
||||
and settings_holder.settings.contact_mail and not headers.get('Reply-To'):
|
||||
headers['Reply-To'] = settings_holder.settings.contact_mail
|
||||
|
||||
subject = prefix_subject(settings_holder, subject)
|
||||
|
||||
body_plain += "\r\n\r\n-- \r\n"
|
||||
|
||||
signature = str(settings_holder.settings.get('mail_text_signature'))
|
||||
if signature:
|
||||
signature = signature.format(event=event.name if event else '')
|
||||
body_plain += signature
|
||||
body_plain += "\r\n\r\n-- \r\n"
|
||||
|
||||
if event:
|
||||
renderer = event.get_html_mail_renderer()
|
||||
|
||||
if order and order.testmode:
|
||||
subject = "[TESTMODE] " + subject
|
||||
|
||||
if order and position and not no_order_links:
|
||||
body_plain += _(
|
||||
"You are receiving this email because someone placed an order for {event} for you."
|
||||
).format(event=event.name)
|
||||
body_plain += "\r\n"
|
||||
body_plain += _(
|
||||
"You can view your order details at the following URL:\n{orderurl}."
|
||||
).replace("\n", "\r\n").format(
|
||||
event=event.name, orderurl=build_absolute_uri(
|
||||
order.event, 'presale:event.order.position', kwargs={
|
||||
'order': order.code,
|
||||
'secret': position.web_secret,
|
||||
'position': position.positionid,
|
||||
}
|
||||
)
|
||||
)
|
||||
elif order and not no_order_links:
|
||||
body_plain += _(
|
||||
"You are receiving this email because you placed an order for {event}."
|
||||
).format(event=event.name)
|
||||
body_plain += "\r\n"
|
||||
body_plain += _(
|
||||
"You can view your order details at the following URL:\n{orderurl}."
|
||||
).replace("\n", "\r\n").format(
|
||||
event=event.name, orderurl=build_absolute_uri(
|
||||
order.event, 'presale:event.order.open', kwargs={
|
||||
'order': order.code,
|
||||
'secret': order.secret,
|
||||
'hash': order.email_confirm_secret()
|
||||
}
|
||||
)
|
||||
)
|
||||
body_plain += "\r\n"
|
||||
|
||||
with override(timezone):
|
||||
try:
|
||||
if plain_text_only:
|
||||
body_html = None
|
||||
elif 'context' in inspect.signature(renderer.render).parameters:
|
||||
body_html = renderer.render(content_plain, signature, raw_subject, order, position, context)
|
||||
elif 'position' in inspect.signature(renderer.render).parameters:
|
||||
# Backwards compatibility
|
||||
warnings.warn('Email renderer called without context argument because context argument is not '
|
||||
'supported.',
|
||||
DeprecationWarning)
|
||||
body_html = renderer.render(content_plain, signature, raw_subject, order, position)
|
||||
else:
|
||||
# Backwards compatibility
|
||||
warnings.warn('Email renderer called without position argument because position argument is not '
|
||||
'supported.',
|
||||
DeprecationWarning)
|
||||
body_html = renderer.render(content_plain, signature, raw_subject, order)
|
||||
except:
|
||||
logger.exception('Could not render HTML body')
|
||||
body_html = None
|
||||
else:
|
||||
signature = ""
|
||||
|
||||
# Build full plain-text body
|
||||
body_plain = _wrap_plain_body(content_plain, signature, event, order, position, no_order_links)
|
||||
body_plain = format_map(body_plain, context, mode=SafeFormatter.MODE_RICH_TO_PLAIN)
|
||||
|
||||
send_task = mail_send_task.si(
|
||||
# Build subject
|
||||
subject = str(subject).format_map(TolerantDict(context))
|
||||
subject = raw_subject = subject.replace('\n', ' ').replace('\r', '')[:900]
|
||||
if settings_holder:
|
||||
subject = prefix_subject(settings_holder, subject)
|
||||
if (order and order.testmode) or (not order and event and event.testmode):
|
||||
subject = "[TESTMODE] " + subject
|
||||
|
||||
# Build sender
|
||||
sender = _full_sender(sender, event, organizer)
|
||||
|
||||
# Build HTML body
|
||||
if plain_text_only:
|
||||
body_html = None
|
||||
else:
|
||||
if event:
|
||||
renderer = event.get_html_mail_renderer()
|
||||
else:
|
||||
renderer = ClassicMailRenderer(None, organizer)
|
||||
|
||||
with override(timezone):
|
||||
try:
|
||||
if 'context' in inspect.signature(renderer.render).parameters:
|
||||
body_html = renderer.render(content_plain, signature, raw_subject, order, position, context)
|
||||
elif 'position' in inspect.signature(renderer.render).parameters:
|
||||
# Backwards compatibility
|
||||
warnings.warn('Email renderer called without context argument because context argument is not '
|
||||
'supported.',
|
||||
DeprecationWarning)
|
||||
body_html = renderer.render(content_plain, signature, raw_subject, order, position)
|
||||
else:
|
||||
# Backwards compatibility
|
||||
warnings.warn('Email renderer called without position argument because position argument is not '
|
||||
'supported.',
|
||||
DeprecationWarning)
|
||||
body_html = renderer.render(content_plain, signature, raw_subject, order)
|
||||
except:
|
||||
logger.exception('Could not render HTML body')
|
||||
body_html = None
|
||||
|
||||
m = OutgoingMail.objects.create(
|
||||
organizer=organizer,
|
||||
event=event,
|
||||
order=order,
|
||||
orderposition=position,
|
||||
customer=customer,
|
||||
user=user,
|
||||
to=[email] if isinstance(email, str) else list(email),
|
||||
cc=cc,
|
||||
bcc=bcc,
|
||||
cc=cc or [],
|
||||
bcc=bcc or [],
|
||||
subject=subject,
|
||||
body=body_plain,
|
||||
html=body_html,
|
||||
body_plain=body_plain,
|
||||
body_html=body_html,
|
||||
sender=sender,
|
||||
event=event.id if event else None,
|
||||
headers=headers,
|
||||
invoices=[i.pk for i in invoices] if invoices and not position else [],
|
||||
order=order.pk if order else None,
|
||||
position=position.pk if position else None,
|
||||
attach_tickets=attach_tickets,
|
||||
attach_ical=attach_ical,
|
||||
user=user.pk if user else None,
|
||||
organizer=organizer.pk if organizer else None,
|
||||
customer=customer.pk if customer else None,
|
||||
attach_cached_files=[(cf.id if isinstance(cf, CachedFile) else cf) for cf in attach_cached_files] if attach_cached_files else [],
|
||||
attach_other_files=attach_other_files,
|
||||
should_attach_tickets=attach_tickets,
|
||||
should_attach_ical=attach_ical,
|
||||
should_attach_other_files=attach_other_files or [],
|
||||
)
|
||||
if invoices and not position:
|
||||
m.should_attach_invoices.add(*invoices)
|
||||
if attach_cached_files:
|
||||
for cf in attach_cached_files:
|
||||
if not isinstance(cf, CachedFile):
|
||||
m.should_attach_cached_files.add(CachedFile.objects.get(pk=cf))
|
||||
else:
|
||||
m.should_attach_cached_files.add(cf)
|
||||
|
||||
send_task = mail_send_task.si(
|
||||
outgoing_mail=m.id
|
||||
)
|
||||
|
||||
if invoices:
|
||||
@@ -392,169 +352,177 @@ class CustomEmail(EmailMultiAlternatives):
|
||||
|
||||
|
||||
@app.task(base=TransactionAwareTask, bind=True, acks_late=True)
|
||||
def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: str, sender: str,
|
||||
event: int = None, position: int = None, headers: dict = None, cc: List[str] = None, bcc: List[str] = None,
|
||||
invoices: List[int] = None, order: int = None, attach_tickets=False, user=None,
|
||||
organizer=None, customer=None, attach_ical=False, attach_cached_files: List[int] = None,
|
||||
attach_other_files: List[str] = None) -> bool:
|
||||
email = CustomEmail(subject, body, sender, to=to, cc=cc, bcc=bcc, headers=headers)
|
||||
if html is not None:
|
||||
def mail_send_task(self, *args, outgoing_mail: int) -> bool:
|
||||
with transaction.atomic():
|
||||
outgoing_mail = OutgoingMail.objects.select_for_update(of=OF_SELF).get(pk=outgoing_mail)
|
||||
if outgoing_mail.status == OutgoingMail.STATUS_INFLIGHT:
|
||||
logger.info("Ignoring job for inflight email")
|
||||
return False
|
||||
elif outgoing_mail.status in (OutgoingMail.STATUS_SENT, OutgoingMail.STATUS_FAILED):
|
||||
logger.info(f"Ignoring job for email in final state {outgoing_mail.status}")
|
||||
return False
|
||||
outgoing_mail.status = OutgoingMail.STATUS_INFLIGHT
|
||||
outgoing_mail.save(update_fields=["status"])
|
||||
|
||||
email = CustomEmail(
|
||||
subject=outgoing_mail.subject,
|
||||
body=outgoing_mail.body_plain,
|
||||
from_email=outgoing_mail.sender,
|
||||
to=outgoing_mail.to,
|
||||
cc=outgoing_mail.cc,
|
||||
bcc=outgoing_mail.bcc,
|
||||
headers=outgoing_mail.headers,
|
||||
)
|
||||
|
||||
# Rewrite all <img> tags from real URLs or data URLs to inline attachments refered to by content ID
|
||||
if outgoing_mail.body_html is not None:
|
||||
html_message = SafeMIMEMultipart(_subtype='related', encoding=settings.DEFAULT_CHARSET)
|
||||
html_with_cid, cid_images = replace_images_with_cid_paths(html)
|
||||
html_with_cid, cid_images = replace_images_with_cid_paths(outgoing_mail.body_html)
|
||||
html_message.attach(SafeMIMEText(html_with_cid, 'html', settings.DEFAULT_CHARSET))
|
||||
attach_cid_images(html_message, cid_images, verify_ssl=True)
|
||||
email.attach_alternative(html_message, "multipart/related")
|
||||
|
||||
if user:
|
||||
user = User.objects.get(pk=user)
|
||||
log_target, error_log_action_type = outgoing_mail.log_parameters()
|
||||
invoices_attached = []
|
||||
actual_attachments = []
|
||||
|
||||
if event:
|
||||
with scopes_disabled():
|
||||
event = Event.objects.get(id=event)
|
||||
organizer = event.organizer
|
||||
backend = event.get_mail_backend()
|
||||
cm = lambda: scope(organizer=event.organizer) # noqa
|
||||
elif organizer:
|
||||
with scopes_disabled():
|
||||
organizer = Organizer.objects.get(id=organizer)
|
||||
backend = organizer.get_mail_backend()
|
||||
cm = lambda: scope(organizer=organizer) # noqa
|
||||
else:
|
||||
backend = get_connection(fail_silently=False)
|
||||
cm = lambda: scopes_disabled() # noqa
|
||||
|
||||
with cm():
|
||||
if customer:
|
||||
customer = Customer.objects.get(pk=customer)
|
||||
log_target = user or customer
|
||||
|
||||
if event:
|
||||
if order:
|
||||
try:
|
||||
order = event.orders.get(pk=order)
|
||||
log_target = order
|
||||
except Order.DoesNotExist:
|
||||
order = None
|
||||
else:
|
||||
with language(order.locale, event.settings.region):
|
||||
if not event.settings.mail_attach_tickets:
|
||||
attach_tickets = False
|
||||
if position:
|
||||
try:
|
||||
position = order.positions.get(pk=position)
|
||||
except OrderPosition.DoesNotExist:
|
||||
attach_tickets = False
|
||||
if attach_tickets:
|
||||
args = []
|
||||
attach_size = 0
|
||||
for name, ct in get_tickets_for_order(order, base_position=position):
|
||||
try:
|
||||
content = ct.file.read()
|
||||
args.append((name, content, ct.type))
|
||||
attach_size += len(content)
|
||||
except:
|
||||
# This sometimes fails e.g. with FileNotFoundError. We haven't been able to figure out
|
||||
# why (probably some race condition with ticket cache invalidation?), so retry later.
|
||||
try:
|
||||
self.retry(max_retries=5, countdown=60)
|
||||
except MaxRetriesExceededError:
|
||||
# Well then, something is really wrong, let's send it without attachment before we
|
||||
# don't sent at all
|
||||
logger.exception('Could not attach invoice to email')
|
||||
pass
|
||||
|
||||
if attach_size < settings.FILE_UPLOAD_MAX_SIZE_EMAIL_ATTACHMENT - 1:
|
||||
# Do not attach more than (limit - 1 MB) in tickets (1MB space for invoice, email itself, …),
|
||||
# it will bounce way to often.
|
||||
for a in args:
|
||||
try:
|
||||
email.attach(*a)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
order.log_action(
|
||||
'pretix.event.order.email.attachments.skipped',
|
||||
data={
|
||||
'subject': 'Attachments skipped',
|
||||
'message': 'Attachment have not been send because {} bytes are likely too large to arrive.'.format(attach_size),
|
||||
'recipient': '',
|
||||
'invoices': [],
|
||||
}
|
||||
)
|
||||
if attach_ical:
|
||||
fname = re.sub('[^a-zA-Z0-9 ]', '-', unidecode(pgettext('attachment_filename', 'Calendar invite')))
|
||||
for i, cal in enumerate(get_private_icals(event, [position] if position else order.positions.all())):
|
||||
email.attach('{}{}.ics'.format(fname, f'-{i + 1}' if i > 0 else ''), cal.serialize(), 'text/calendar')
|
||||
|
||||
email = email_filter.send_chained(event, 'message', message=email, order=order, user=user)
|
||||
|
||||
invoices_sent = []
|
||||
if invoices:
|
||||
invoices = Invoice.objects.filter(pk__in=invoices)
|
||||
for inv in invoices:
|
||||
if inv.file:
|
||||
with outgoing_mail.scope_manager():
|
||||
# Attach tickets
|
||||
if outgoing_mail.should_attach_tickets and outgoing_mail.order:
|
||||
with language(outgoing_mail.order.locale, outgoing_mail.event.settings.region):
|
||||
args = []
|
||||
attach_size = 0
|
||||
for name, ct in get_tickets_for_order(outgoing_mail.order, base_position=outgoing_mail.orderposition):
|
||||
try:
|
||||
# We try to give the invoice a more human-readable name, e.g. "Invoice_ABC-123.pdf" instead of
|
||||
# just "ABC-123.pdf", but we only do so if our currently selected language allows to do this
|
||||
# as ASCII text. For example, we would not want a "فاتورة_" prefix for our filename since this
|
||||
# has shown to cause deliverability problems of the email and deliverability wins.
|
||||
with language(order.locale if order else inv.locale, event.settings.region if event else None):
|
||||
filename = pgettext('invoice', 'Invoice {num}').format(num=inv.number).replace(' ', '_') + '.pdf'
|
||||
if not re.match("^[a-zA-Z0-9-_%./,&:# ]+$", filename):
|
||||
filename = inv.number.replace(' ', '_') + '.pdf'
|
||||
filename = re.sub("[^a-zA-Z0-9-_.]+", "_", filename)
|
||||
with language(inv.order.locale):
|
||||
email.attach(
|
||||
filename,
|
||||
inv.file.file.read(),
|
||||
'application/pdf'
|
||||
)
|
||||
invoices_sent.append(inv)
|
||||
content = ct.file.read()
|
||||
args.append((name, content, ct.type))
|
||||
attach_size += len(content)
|
||||
except:
|
||||
logger.exception('Could not attach invoice to email')
|
||||
pass
|
||||
# This sometimes fails e.g. with FileNotFoundError. We haven't been able to figure out
|
||||
# why (probably some race condition with ticket cache invalidation?), so retry later.
|
||||
try:
|
||||
self.retry(max_retries=5, countdown=60)
|
||||
except MaxRetriesExceededError:
|
||||
# Well then, something is really wrong, let's send it without attachment before we
|
||||
# don't sent at all
|
||||
logger.exception('Could not attach invoice to email')
|
||||
pass
|
||||
|
||||
if attach_other_files:
|
||||
for fname in attach_other_files:
|
||||
ftype, _ = mimetypes.guess_type(fname)
|
||||
data = default_storage.open(fname).read()
|
||||
if attach_size < settings.FILE_UPLOAD_MAX_SIZE_EMAIL_ATTACHMENT - 1:
|
||||
# Do not attach more than (limit - 1 MB) in tickets (1MB space for invoice, email itself, …),
|
||||
# it will bounce way to often.
|
||||
for a in args:
|
||||
try:
|
||||
email.attach(*a)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
outgoing_mail.order.log_action(
|
||||
'pretix.event.order.email.attachments.skipped',
|
||||
data={
|
||||
'subject': 'Attachments skipped',
|
||||
'message': 'Attachment have not been send because {} bytes are likely too large to arrive.'.format(attach_size),
|
||||
'recipient': '',
|
||||
'invoices': [],
|
||||
}
|
||||
)
|
||||
|
||||
# Attach calendar files
|
||||
if outgoing_mail.should_attach_ical and outgoing_mail.order:
|
||||
fname = re.sub('[^a-zA-Z0-9 ]', '-', unidecode(pgettext('attachment_filename', 'Calendar invite')))
|
||||
icals = get_private_icals(
|
||||
outgoing_mail.event,
|
||||
[outgoing_mail.orderposition] if outgoing_mail.orderposition else outgoing_mail.order.positions.all()
|
||||
)
|
||||
for i, cal in enumerate(icals):
|
||||
name = '{}{}.ics'.format(fname, f'-{i + 1}' if i > 0 else '')
|
||||
content = cal.serialize()
|
||||
mimetype = 'text/calendar'
|
||||
email.attach(name, content, mimetype)
|
||||
|
||||
for inv in outgoing_mail.should_attach_invoices.all():
|
||||
if inv.file:
|
||||
try:
|
||||
# We try to give the invoice a more human-readable name, e.g. "Invoice_ABC-123.pdf" instead of
|
||||
# just "ABC-123.pdf", but we only do so if our currently selected language allows to do this
|
||||
# as ASCII text. For example, we would not want a "فاتورة_" prefix for our filename since this
|
||||
# has shown to cause deliverability problems of the email and deliverability wins.
|
||||
with language(outgoing_mail.order.locale if outgoing_mail.order else inv.locale, outgoing_mail.event.settings.region):
|
||||
filename = pgettext('invoice', 'Invoice {num}').format(num=inv.number).replace(' ', '_') + '.pdf'
|
||||
if not re.match("^[a-zA-Z0-9-_%./,&:# ]+$", filename):
|
||||
filename = inv.number.replace(' ', '_') + '.pdf'
|
||||
filename = re.sub("[^a-zA-Z0-9-_.]+", "_", filename)
|
||||
content = inv.file.file.read()
|
||||
with language(inv.order.locale):
|
||||
email.attach(
|
||||
filename,
|
||||
content,
|
||||
'application/pdf'
|
||||
)
|
||||
invoices_attached.append(inv)
|
||||
except:
|
||||
logger.exception('Could not attach invoice to email')
|
||||
pass
|
||||
|
||||
for fname in outgoing_mail.should_attach_other_files:
|
||||
ftype, _ = mimetypes.guess_type(fname)
|
||||
data = default_storage.open(fname).read()
|
||||
try:
|
||||
email.attach(
|
||||
clean_filename(os.path.basename(fname)),
|
||||
data,
|
||||
ftype
|
||||
)
|
||||
except:
|
||||
logger.exception('Could not attach file to email')
|
||||
pass
|
||||
|
||||
for cf in outgoing_mail.should_attach_cached_files.all():
|
||||
if cf.file:
|
||||
try:
|
||||
email.attach(
|
||||
clean_filename(os.path.basename(fname)),
|
||||
data,
|
||||
ftype
|
||||
cf.filename,
|
||||
cf.file.file.read(),
|
||||
cf.type,
|
||||
)
|
||||
except:
|
||||
logger.exception('Could not attach file to email')
|
||||
pass
|
||||
|
||||
if attach_cached_files:
|
||||
for cf in CachedFile.objects.filter(id__in=attach_cached_files):
|
||||
if cf.file:
|
||||
try:
|
||||
email.attach(
|
||||
cf.filename,
|
||||
cf.file.file.read(),
|
||||
cf.type,
|
||||
)
|
||||
except:
|
||||
logger.exception('Could not attach file to email')
|
||||
pass
|
||||
if outgoing_mail.event:
|
||||
with outgoing_mail.scope_manager():
|
||||
email = email_filter.send_chained(
|
||||
outgoing_mail.event, 'message', message=email, order=outgoing_mail.order, user=outgoing_mail.user
|
||||
)
|
||||
|
||||
email = global_email_filter.send_chained(event, 'message', message=email, user=user, order=order,
|
||||
organizer=organizer, customer=customer)
|
||||
email = global_email_filter.send_chained(
|
||||
outgoing_mail.event, 'message', message=email, user=outgoing_mail.user, order=outgoing_mail.order,
|
||||
organizer=outgoing_mail.organizer, customer=outgoing_mail.customer
|
||||
)
|
||||
|
||||
outgoing_mail.actual_attachments = [
|
||||
{
|
||||
"name": a[0],
|
||||
"size": len(a[1]),
|
||||
"type": a[2],
|
||||
} for a in email.attachments
|
||||
]
|
||||
|
||||
backend = outgoing_mail.get_mail_backend()
|
||||
try:
|
||||
backend.send_messages([email])
|
||||
except (smtplib.SMTPResponseException, smtplib.SMTPSenderRefused) as e:
|
||||
if e.smtp_code in (101, 111, 421, 422, 431, 432, 442, 447, 452):
|
||||
if e.smtp_code == 432 and settings.HAS_REDIS:
|
||||
# This is likely Microsoft Exchange Online which has a pretty bad rate limit of max. 3 concurrent
|
||||
# SMTP connections which is *easily* exceeded with many celery threads. Just retrying with exponential
|
||||
# backoff won't be good enough if we have a lot of emails, instead we'll need to make sure our retry
|
||||
# intervals scatter such that the email won't all be retried at the same time again and cause the
|
||||
# same problem.
|
||||
# See also https://docs.microsoft.com/en-us/exchange/troubleshoot/send-emails/smtp-submission-improvements
|
||||
except Exception as e:
|
||||
logger.exception('Error sending email')
|
||||
retry_strategy = _retry_strategy(e)
|
||||
err, err_detail = _format_error(e)
|
||||
|
||||
outgoing_mail.error = err
|
||||
outgoing_mail.error_detail = err_detail
|
||||
outgoing_mail.sent = now()
|
||||
|
||||
# Run retries
|
||||
try:
|
||||
if retry_strategy == "microsoft_concurrency" and settings.HAS_REDIS:
|
||||
from django_redis import get_redis_connection
|
||||
|
||||
redis_key = "pretix_mail_retry_" + hashlib.sha1(f"{getattr(backend, 'username', '_')}@{getattr(backend, 'host', '_')}".encode()).hexdigest()
|
||||
@@ -564,100 +532,59 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
|
||||
|
||||
max_retries = 10
|
||||
retry_after = min(30 + cnt * 10, 1800)
|
||||
else:
|
||||
# Most likely some other kind of temporary failure, retry again (but pretty soon)
|
||||
|
||||
outgoing_mail.status = OutgoingMail.STATUS_AWAWITING_RETRY
|
||||
outgoing_mail.save(upate_fields=["status", "error", "error_detail", "sent"])
|
||||
self.retry(max_retries=max_retries, countdown=retry_after) # throws RetryException, ends function flow
|
||||
elif retry_strategy in ("microsoft_concurrency", "quick"):
|
||||
max_retries = 5
|
||||
retry_after = [10, 30, 60, 300, 900, 900][self.request.retries]
|
||||
outgoing_mail.status = OutgoingMail.STATUS_AWAWITING_RETRY
|
||||
outgoing_mail.save(upate_fields=["status", "error", "error_detail", "sent"])
|
||||
self.retry(max_retries=max_retries, countdown=retry_after) # throws RetryException, ends function flow
|
||||
|
||||
try:
|
||||
self.retry(max_retries=max_retries, countdown=retry_after)
|
||||
except MaxRetriesExceededError:
|
||||
if log_target:
|
||||
log_target.log_action(
|
||||
'pretix.email.error',
|
||||
data={
|
||||
'subject': 'SMTP code {}, max retries exceeded'.format(e.smtp_code),
|
||||
'message': e.smtp_error.decode() if isinstance(e.smtp_error, bytes) else str(e.smtp_error),
|
||||
'recipient': '',
|
||||
'invoices': [],
|
||||
}
|
||||
)
|
||||
raise e
|
||||
elif retry_strategy == "slow":
|
||||
outgoing_mail.status = OutgoingMail.STATUS_AWAWITING_RETRY
|
||||
outgoing_mail.save(upate_fields=["status", "error", "error_detail", "sent"])
|
||||
self.retry(max_retries=5, countdown=[60, 300, 600, 1200, 1800, 1800][self.request.retries]) # throws RetryException, ends function flow
|
||||
|
||||
logger.exception('Error sending email')
|
||||
except MaxRetriesExceededError:
|
||||
if log_target:
|
||||
log_target.log_action(
|
||||
error_log_action_type,
|
||||
data={
|
||||
'subject': f'{err} (max retries exceeded)',
|
||||
'message': err_detail,
|
||||
'recipient': '',
|
||||
'invoices': [],
|
||||
}
|
||||
)
|
||||
return False
|
||||
|
||||
# If we reach this, it's a non-retryable error
|
||||
outgoing_mail.status = OutgoingMail.STATUS_FAILED
|
||||
outgoing_mail.sent = now()
|
||||
outgoing_mail.save(upate_fields=["status", "error", "error_detail", "sent"])
|
||||
if log_target:
|
||||
log_target.log_action(
|
||||
'pretix.email.error',
|
||||
error_log_action_type,
|
||||
data={
|
||||
'subject': 'SMTP code {}'.format(e.smtp_code),
|
||||
'message': e.smtp_error.decode() if isinstance(e.smtp_error, bytes) else str(e.smtp_error),
|
||||
'subject': err,
|
||||
'message': err_detail,
|
||||
'recipient': '',
|
||||
'invoices': [],
|
||||
}
|
||||
)
|
||||
|
||||
raise SendMailException('Failed to send an email to {}.'.format(to))
|
||||
except smtplib.SMTPRecipientsRefused as e:
|
||||
smtp_codes = [a[0] for a in e.recipients.values()]
|
||||
|
||||
if not any(c >= 500 for c in smtp_codes) or any(b'Message is too large' in a[1] for a in e.recipients.values()):
|
||||
# This is not a permanent failure (mailbox full, service unavailable), retry later, but with large
|
||||
# intervals. One would think that "Message is too lage" is a permanent failure, but apparently it is not.
|
||||
# We have documented cases of emails to Microsoft returning the error occasionally and then later
|
||||
# allowing the very same email.
|
||||
try:
|
||||
self.retry(max_retries=5, countdown=[60, 300, 600, 1200, 1800, 1800][self.request.retries])
|
||||
except MaxRetriesExceededError:
|
||||
# ignore and go on with logging the error
|
||||
pass
|
||||
|
||||
logger.exception('Error sending email')
|
||||
if log_target:
|
||||
message = []
|
||||
for e, val in e.recipients.items():
|
||||
message.append(f'{e}: {val[0]} {val[1].decode()}')
|
||||
|
||||
log_target.log_action(
|
||||
'pretix.email.error',
|
||||
data={
|
||||
'subject': 'SMTP error',
|
||||
'message': '\n'.join(message),
|
||||
'recipient': '',
|
||||
'invoices': [],
|
||||
}
|
||||
)
|
||||
|
||||
raise SendMailException('Failed to send an email to {}.'.format(to))
|
||||
except Exception as e:
|
||||
if isinstance(e, OSError) and not isinstance(e, smtplib.SMTPNotSupportedError):
|
||||
try:
|
||||
self.retry(max_retries=5, countdown=[10, 30, 60, 300, 900, 900][self.request.retries])
|
||||
except MaxRetriesExceededError:
|
||||
if log_target:
|
||||
log_target.log_action(
|
||||
'pretix.email.error',
|
||||
data={
|
||||
'subject': 'Internal error',
|
||||
'message': f'Max retries exceeded after error "{str(e)}"',
|
||||
'recipient': '',
|
||||
'invoices': [],
|
||||
}
|
||||
)
|
||||
raise e
|
||||
if log_target:
|
||||
log_target.log_action(
|
||||
'pretix.email.error',
|
||||
data={
|
||||
'subject': 'Internal error',
|
||||
'message': str(e),
|
||||
'recipient': '',
|
||||
'invoices': [],
|
||||
}
|
||||
)
|
||||
logger.exception('Error sending email')
|
||||
raise SendMailException('Failed to send an email to {}.'.format(to))
|
||||
return False
|
||||
else:
|
||||
for i in invoices_sent:
|
||||
outgoing_mail.status = OutgoingMail.STATUS_SENT
|
||||
outgoing_mail.error = None
|
||||
outgoing_mail.error_detail = None
|
||||
outgoing_mail.actual_attachments = actual_attachments
|
||||
outgoing_mail.sent = now()
|
||||
outgoing_mail.save(upate_fields=["status", "error", "error_detail", "sent", "actual_attachments"])
|
||||
|
||||
for i in invoices_attached:
|
||||
if i.transmission_type == "email":
|
||||
# Mark invoice as sent when it was sent to the requested address *either* at the time of invoice
|
||||
# creation *or* as of right now.
|
||||
@@ -668,7 +595,7 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
|
||||
expected_recipients.append((i.order.invoice_address.transmission_info or {}).get("transmission_email_address") or i.order.email)
|
||||
except InvoiceAddress.DoesNotExist:
|
||||
pass
|
||||
if not any(t in expected_recipients for t in to):
|
||||
if not any(t in expected_recipients for t in outgoing_mail.to):
|
||||
continue
|
||||
if i.transmission_status != Invoice.TRANSMISSION_STATUS_COMPLETED:
|
||||
i.transmission_date = now()
|
||||
@@ -677,7 +604,7 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
|
||||
i.transmission_info = {
|
||||
"sent": [
|
||||
{
|
||||
"recipients": to,
|
||||
"recipients": outgoing_mail.to,
|
||||
"datetime": now().isoformat(),
|
||||
}
|
||||
]
|
||||
@@ -689,7 +616,7 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
|
||||
elif i.transmission_provider == "email_pdf":
|
||||
i.transmission_info["sent"].append(
|
||||
{
|
||||
"recipients": to,
|
||||
"recipients": outgoing_mail.to,
|
||||
"datetime": now().isoformat(),
|
||||
}
|
||||
)
|
||||
@@ -703,7 +630,7 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
|
||||
"transmission_provider": "email_pdf",
|
||||
"transmission_type": "email",
|
||||
"data": {
|
||||
"recipients": [to],
|
||||
"recipients": outgoing_mail.to,
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -826,3 +753,121 @@ def normalize_image_url(url):
|
||||
else:
|
||||
url = urljoin(settings.MEDIA_URL, url)
|
||||
return url
|
||||
|
||||
|
||||
def _autoextend_context(context, order):
|
||||
try:
|
||||
context.update({
|
||||
'invoice_name': order.invoice_address.name,
|
||||
'invoice_company': order.invoice_address.company
|
||||
})
|
||||
except InvoiceAddress.DoesNotExist:
|
||||
context.update({
|
||||
'invoice_name': '',
|
||||
'invoice_company': ''
|
||||
})
|
||||
|
||||
|
||||
def _full_sender(sender_address, event, organizer):
|
||||
sender_address = (
|
||||
sender_address or
|
||||
(event.settings.get('mail_from') if event else None) or
|
||||
(organizer.settings.get('mail_from') if organizer else None) or
|
||||
settings.MAIL_FROM
|
||||
)
|
||||
if event:
|
||||
sender_name = event.settings.mail_from_name or str(event.name)
|
||||
elif organizer:
|
||||
sender_name = organizer.settings.mail_from_name or str(organizer.name)
|
||||
else:
|
||||
sender_name = settings.PRETIX_INSTANCE_NAME
|
||||
|
||||
sender = formataddr((clean_sender_name(sender_name), sender_address))
|
||||
return sender
|
||||
|
||||
|
||||
def _wrap_plain_body(content_plain, signature, event, order, position, no_order_links):
|
||||
body_plain = content_plain
|
||||
body_plain += "\r\n\r\n-- \r\n"
|
||||
|
||||
if signature:
|
||||
signature = signature.format(event=event.name if event else '')
|
||||
body_plain += signature
|
||||
body_plain += "\r\n\r\n-- \r\n"
|
||||
|
||||
if event and order and position and not no_order_links:
|
||||
body_plain += _(
|
||||
"You are receiving this email because someone placed an order for {event} for you."
|
||||
).format(event=event.name)
|
||||
body_plain += "\r\n"
|
||||
body_plain += _(
|
||||
"You can view your order details at the following URL:\n{orderurl}."
|
||||
).replace("\n", "\r\n").format(
|
||||
event=event.name, orderurl=build_absolute_uri(
|
||||
order.event, 'presale:event.order.position', kwargs={
|
||||
'order': order.code,
|
||||
'secret': position.web_secret,
|
||||
'position': position.positionid,
|
||||
}
|
||||
)
|
||||
)
|
||||
elif event and order and not no_order_links:
|
||||
body_plain += _(
|
||||
"You are receiving this email because you placed an order for {event}."
|
||||
).format(event=event.name)
|
||||
body_plain += "\r\n"
|
||||
body_plain += _(
|
||||
"You can view your order details at the following URL:\n{orderurl}."
|
||||
).replace("\n", "\r\n").format(
|
||||
event=event.name, orderurl=build_absolute_uri(
|
||||
order.event, 'presale:event.order.open', kwargs={
|
||||
'order': order.code,
|
||||
'secret': order.secret,
|
||||
'hash': order.email_confirm_secret()
|
||||
}
|
||||
)
|
||||
)
|
||||
body_plain += "\r\n"
|
||||
|
||||
return body_plain
|
||||
|
||||
|
||||
def _retry_strategy(e: Exception):
|
||||
if isinstance(e, (smtplib.SMTPResponseException, smtplib.SMTPSenderRefused)):
|
||||
if e.smtp_code == 432:
|
||||
# This is likely Microsoft Exchange Online which has a pretty bad rate limit of max. 3 concurrent
|
||||
# SMTP connections which is *easily* exceeded with many celery threads. Just retrying with exponential
|
||||
# backoff won't be good enough if we have a lot of emails, instead we'll need to make sure our retry
|
||||
# intervals scatter such that the email won't all be retried at the same time again and cause the
|
||||
# same problem.
|
||||
# See also https://docs.microsoft.com/en-us/exchange/troubleshoot/send-emails/smtp-submission-improvements
|
||||
return "microsoft_concurrncy"
|
||||
|
||||
if e.smtp_code in (101, 111, 421, 422, 431, 432, 442, 447, 452):
|
||||
return "quick"
|
||||
|
||||
elif isinstance(e, smtplib.SMTPRecipientsRefused):
|
||||
smtp_codes = [a[0] for a in e.recipients.values()]
|
||||
|
||||
if not any(c >= 500 for c in smtp_codes) or any(b'Message is too large' in a[1] for a in e.recipients.values()):
|
||||
# This is not a permanent failure (mailbox full, service unavailable), retry later, but with large
|
||||
# intervals. One would think that "Message is too lage" is a permanent failure, but apparently it is not.
|
||||
# We have documented cases of emails to Microsoft returning the error occasionally and then later
|
||||
# allowing the very same email.
|
||||
return "slow"
|
||||
|
||||
elif isinstance(e, OSError) and not isinstance(e, smtplib.SMTPNotSupportedError):
|
||||
# Most likely some other kind of temporary failure, retry again (but pretty soon)
|
||||
return "quick"
|
||||
|
||||
|
||||
def _format_error(e: Exception):
|
||||
if isinstance(e, (smtplib.SMTPResponseException, smtplib.SMTPSenderRefused)):
|
||||
return 'SMTP code {}'.format(e.smtp_code), e.smtp_error.decode() if isinstance(e.smtp_error, bytes) else str(e.smtp_error)
|
||||
elif isinstance(e, smtplib.SMTPRecipientsRefused):
|
||||
message = []
|
||||
for e, val in e.recipients.items():
|
||||
message.append(f'{e}: {val[0]} {val[1].decode()}')
|
||||
return 'SMTP recipients refudes', '\n'.join(message)
|
||||
else:
|
||||
return 'Internal error', str(e)
|
||||
|
||||
@@ -90,7 +90,6 @@ from pretix.base.services.invoices import (
|
||||
from pretix.base.services.locking import (
|
||||
LOCK_TRUST_WINDOW, LockTimeoutException, lock_objects,
|
||||
)
|
||||
from pretix.base.services.mail import SendMailException
|
||||
from pretix.base.services.memberships import (
|
||||
create_membership, validate_memberships_in_order,
|
||||
)
|
||||
@@ -421,33 +420,27 @@ def approve_order(order, user=None, send_mail: bool=True, auth=None, force=False
|
||||
email_attendee_subject = order.event.settings.mail_subject_order_approved_attendee
|
||||
|
||||
email_context = get_email_context(event=order.event, order=order)
|
||||
try:
|
||||
order.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.order_approved', user,
|
||||
attach_tickets=True,
|
||||
attach_ical=order.event.settings.mail_attach_ical and (
|
||||
not order.event.settings.mail_attach_ical_paid_only or
|
||||
order.total == Decimal('0.00') or
|
||||
order.valid_if_pending
|
||||
),
|
||||
invoices=[invoice] if invoice and transmit_invoice_mail else []
|
||||
)
|
||||
except SendMailException:
|
||||
logger.exception('Order approved email could not be sent')
|
||||
order.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.order_approved', user,
|
||||
attach_tickets=True,
|
||||
attach_ical=order.event.settings.mail_attach_ical and (
|
||||
not order.event.settings.mail_attach_ical_paid_only or
|
||||
order.total == Decimal('0.00') or
|
||||
order.valid_if_pending
|
||||
),
|
||||
invoices=[invoice] if invoice and transmit_invoice_mail else []
|
||||
)
|
||||
|
||||
if email_attendees:
|
||||
for p in order.positions.all():
|
||||
if p.addon_to_id is None and p.attendee_email and p.attendee_email != order.email:
|
||||
email_attendee_context = get_email_context(event=order.event, order=order, position=p)
|
||||
try:
|
||||
p.send_mail(
|
||||
email_attendee_subject, email_attendee_template, email_attendee_context,
|
||||
'pretix.event.order.email.order_approved', user,
|
||||
attach_tickets=True,
|
||||
)
|
||||
except SendMailException:
|
||||
logger.exception('Order approved email could not be sent to attendee')
|
||||
p.send_mail(
|
||||
email_attendee_subject, email_attendee_template, email_attendee_context,
|
||||
'pretix.event.order.email.order_approved', user,
|
||||
attach_tickets=True,
|
||||
)
|
||||
|
||||
return order.pk
|
||||
|
||||
@@ -484,13 +477,10 @@ def deny_order(order, comment='', user=None, send_mail: bool=True, auth=None):
|
||||
email_template = order.event.settings.mail_text_order_denied
|
||||
email_subject = order.event.settings.mail_subject_order_denied
|
||||
email_context = get_email_context(event=order.event, order=order, comment=comment)
|
||||
try:
|
||||
order.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.order_denied', user
|
||||
)
|
||||
except SendMailException:
|
||||
logger.exception('Order denied email could not be sent')
|
||||
order.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.order_denied', user
|
||||
)
|
||||
|
||||
return order.pk
|
||||
|
||||
@@ -637,14 +627,11 @@ def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device
|
||||
email_template = order.event.settings.mail_text_order_canceled
|
||||
email_subject = order.event.settings.mail_subject_order_canceled
|
||||
email_context = get_email_context(event=order.event, order=order, comment=comment or "")
|
||||
try:
|
||||
order.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.order_canceled', user,
|
||||
invoices=transmit_invoices_mail,
|
||||
)
|
||||
except SendMailException:
|
||||
logger.exception('Order canceled email could not be sent')
|
||||
order.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.order_canceled', user,
|
||||
invoices=transmit_invoices_mail,
|
||||
)
|
||||
|
||||
for p in order.payments.filter(state__in=(OrderPayment.PAYMENT_STATE_CREATED, OrderPayment.PAYMENT_STATE_PENDING)):
|
||||
try:
|
||||
@@ -1099,46 +1086,40 @@ def _order_placed_email(event: Event, order: Order, email_template, subject_temp
|
||||
log_entry: str, invoice, payments: List[OrderPayment], is_free=False):
|
||||
email_context = get_email_context(event=event, order=order, payments=payments)
|
||||
|
||||
try:
|
||||
order.send_mail(
|
||||
subject_template, email_template, email_context,
|
||||
log_entry,
|
||||
invoices=[invoice] if invoice else [],
|
||||
attach_tickets=True,
|
||||
attach_ical=event.settings.mail_attach_ical and (
|
||||
not event.settings.mail_attach_ical_paid_only or
|
||||
is_free or
|
||||
order.valid_if_pending
|
||||
),
|
||||
attach_other_files=[a for a in [
|
||||
event.settings.get('mail_attachment_new_order', as_type=str, default='')[len('file://'):]
|
||||
] if a],
|
||||
)
|
||||
except SendMailException:
|
||||
logger.exception('Order received email could not be sent')
|
||||
order.send_mail(
|
||||
subject_template, email_template, email_context,
|
||||
log_entry,
|
||||
invoices=[invoice] if invoice else [],
|
||||
attach_tickets=True,
|
||||
attach_ical=event.settings.mail_attach_ical and (
|
||||
not event.settings.mail_attach_ical_paid_only or
|
||||
is_free or
|
||||
order.valid_if_pending
|
||||
),
|
||||
attach_other_files=[a for a in [
|
||||
event.settings.get('mail_attachment_new_order', as_type=str, default='')[len('file://'):]
|
||||
] if a],
|
||||
)
|
||||
|
||||
|
||||
def _order_placed_email_attendee(event: Event, order: Order, position: OrderPosition, email_template, subject_template,
|
||||
log_entry: str, is_free=False):
|
||||
email_context = get_email_context(event=event, order=order, position=position)
|
||||
|
||||
try:
|
||||
position.send_mail(
|
||||
subject_template, email_template, email_context,
|
||||
log_entry,
|
||||
invoices=[],
|
||||
attach_tickets=True,
|
||||
attach_ical=event.settings.mail_attach_ical and (
|
||||
not event.settings.mail_attach_ical_paid_only or
|
||||
is_free or
|
||||
order.valid_if_pending
|
||||
),
|
||||
attach_other_files=[a for a in [
|
||||
event.settings.get('mail_attachment_new_order', as_type=str, default='')[len('file://'):]
|
||||
] if a],
|
||||
)
|
||||
except SendMailException:
|
||||
logger.exception('Order received email could not be sent to attendee')
|
||||
position.send_mail(
|
||||
subject_template, email_template, email_context,
|
||||
log_entry,
|
||||
invoices=[],
|
||||
attach_tickets=True,
|
||||
attach_ical=event.settings.mail_attach_ical and (
|
||||
not event.settings.mail_attach_ical_paid_only or
|
||||
is_free or
|
||||
order.valid_if_pending
|
||||
),
|
||||
attach_other_files=[a for a in [
|
||||
event.settings.get('mail_attachment_new_order', as_type=str, default='')[len('file://'):]
|
||||
] if a],
|
||||
)
|
||||
|
||||
|
||||
def _perform_order(event: Event, payment_requests: List[dict], position_ids: List[str],
|
||||
@@ -1460,13 +1441,10 @@ def send_expiry_warnings(sender, **kwargs):
|
||||
email_template = settings.mail_text_order_pending_warning
|
||||
email_subject = settings.mail_subject_order_pending_warning
|
||||
|
||||
try:
|
||||
o.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.expire_warning_sent'
|
||||
)
|
||||
except SendMailException:
|
||||
logger.exception('Reminder email could not be sent')
|
||||
o.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.expire_warning_sent'
|
||||
)
|
||||
|
||||
|
||||
@receiver(signal=periodic_task)
|
||||
@@ -1527,14 +1505,11 @@ def send_download_reminders(sender, **kwargs):
|
||||
email_template = event.settings.mail_text_download_reminder
|
||||
email_subject = event.settings.mail_subject_download_reminder
|
||||
email_context = get_email_context(event=event, order=o)
|
||||
try:
|
||||
o.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.download_reminder_sent',
|
||||
attach_tickets=True
|
||||
)
|
||||
except SendMailException:
|
||||
logger.exception('Reminder email could not be sent')
|
||||
o.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.download_reminder_sent',
|
||||
attach_tickets=True
|
||||
)
|
||||
|
||||
if event.settings.mail_send_download_reminder_attendee:
|
||||
for p in positions:
|
||||
@@ -1548,14 +1523,11 @@ def send_download_reminders(sender, **kwargs):
|
||||
email_template = event.settings.mail_text_download_reminder_attendee
|
||||
email_subject = event.settings.mail_subject_download_reminder_attendee
|
||||
email_context = get_email_context(event=event, order=o, position=p)
|
||||
try:
|
||||
o.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.download_reminder_sent',
|
||||
attach_tickets=True, position=p
|
||||
)
|
||||
except SendMailException:
|
||||
logger.exception('Reminder email could not be sent to attendee')
|
||||
o.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.download_reminder_sent',
|
||||
attach_tickets=True, position=p
|
||||
)
|
||||
|
||||
|
||||
def notify_user_changed_order(order, user=None, auth=None, invoices=[]):
|
||||
@@ -1563,13 +1535,10 @@ def notify_user_changed_order(order, user=None, auth=None, invoices=[]):
|
||||
email_template = order.event.settings.mail_text_order_changed
|
||||
email_context = get_email_context(event=order.event, order=order)
|
||||
email_subject = order.event.settings.mail_subject_order_changed
|
||||
try:
|
||||
order.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.order_changed', user, auth=auth, invoices=invoices, attach_tickets=True,
|
||||
)
|
||||
except SendMailException:
|
||||
logger.exception('Order changed email could not be sent')
|
||||
order.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.order_changed', user, auth=auth, invoices=invoices, attach_tickets=True,
|
||||
)
|
||||
|
||||
|
||||
class OrderChangeManager:
|
||||
|
||||
@@ -48,7 +48,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import CachedFile, Event, User, cachedfile_name
|
||||
from pretix.base.services.mail import SendMailException, mail
|
||||
from pretix.base.services.mail import mail
|
||||
from pretix.base.services.tasks import ProfiledEventTask
|
||||
from pretix.base.shredder import ShredError
|
||||
from pretix.celery_app import app
|
||||
@@ -171,21 +171,18 @@ def shred(self, event: Event, fileid: str, confirm_code: str, user: int=None, lo
|
||||
|
||||
if user:
|
||||
with language(user.locale):
|
||||
try:
|
||||
mail(
|
||||
user.email,
|
||||
_('Data shredding completed'),
|
||||
'pretixbase/email/shred_completed.txt',
|
||||
{
|
||||
'user': user,
|
||||
'organizer': event.organizer.name,
|
||||
'event': str(event.name),
|
||||
'start_time': date_format(parse(indexdata['time']).astimezone(event.timezone), 'SHORT_DATETIME_FORMAT'),
|
||||
'shredders': ', '.join([str(s.verbose_name) for s in shredders])
|
||||
},
|
||||
event=None,
|
||||
user=user,
|
||||
locale=user.locale,
|
||||
)
|
||||
except SendMailException:
|
||||
pass # Already logged
|
||||
mail(
|
||||
user.email,
|
||||
_('Data shredding completed'),
|
||||
'pretixbase/email/shred_completed.txt',
|
||||
{
|
||||
'user': user,
|
||||
'organizer': event.organizer.name,
|
||||
'event': str(event.name),
|
||||
'start_time': date_format(parse(indexdata['time']).astimezone(event.timezone), 'SHORT_DATETIME_FORMAT'),
|
||||
'shredders': ', '.join([str(s.verbose_name) for s in shredders])
|
||||
},
|
||||
event=None,
|
||||
user=user,
|
||||
locale=user.locale,
|
||||
)
|
||||
|
||||
@@ -719,6 +719,7 @@ class CoreUserImpersonatedLogEntryType(UserImpersonatedLogEntryType):
|
||||
'pretix.customer.anonymized': _('The account has been disabled and anonymized.'),
|
||||
'pretix.customer.password.resetrequested': _('A new password has been requested.'),
|
||||
'pretix.customer.password.set': _('A new password has been set.'),
|
||||
'pretix.customer.email.error': _('Sending of an email has failed.'),
|
||||
'pretix.reusable_medium.created': _('The reusable medium has been created.'),
|
||||
'pretix.reusable_medium.created.auto': _('The reusable medium has been created automatically.'),
|
||||
'pretix.reusable_medium.changed': _('The reusable medium has been changed.'),
|
||||
@@ -752,6 +753,7 @@ class CoreUserImpersonatedLogEntryType(UserImpersonatedLogEntryType):
|
||||
'pretix.user.anonymized': _('This user has been anonymized.'),
|
||||
'pretix.user.oauth.authorized': _('The application "{application_name}" has been authorized to access your '
|
||||
'account.'),
|
||||
'pretix.user.email.error': _('Sending of an email has failed.'),
|
||||
'pretix.control.auth.user.forgot_password.mail_sent': _('Password reset mail sent.'),
|
||||
'pretix.control.auth.user.forgot_password.recovered': _('The password has been reset.'),
|
||||
'pretix.control.auth.user.forgot_password.denied.repeated': _('A repeated password reset has been denied, as '
|
||||
|
||||
@@ -65,7 +65,6 @@ from pretix.base.forms.auth import (
|
||||
)
|
||||
from pretix.base.metrics import pretix_failed_logins, pretix_successful_logins
|
||||
from pretix.base.models import TeamInvite, U2FDevice, User, WebAuthnDevice
|
||||
from pretix.base.services.mail import SendMailException
|
||||
from pretix.helpers.http import get_client_ip, redirect_to_url
|
||||
from pretix.helpers.security import handle_login_source
|
||||
|
||||
@@ -342,9 +341,6 @@ class Forgot(TemplateView):
|
||||
except User.DoesNotExist:
|
||||
logger.warning('Backend password reset for unregistered e-mail \"' + email + '\" requested.')
|
||||
|
||||
except SendMailException:
|
||||
logger.exception('Sending password reset email to \"' + email + '\" failed.')
|
||||
|
||||
except RepeatedResetDenied:
|
||||
pass
|
||||
|
||||
|
||||
@@ -82,27 +82,35 @@ class ControlSyncJob(OrderView):
|
||||
messages.success(self.request, _('The sync job has been enqueued and will run in the next minutes.'))
|
||||
elif self.request.POST.get("cancel_job"):
|
||||
with transaction.atomic():
|
||||
job = self.order.queued_sync_jobs.select_for_update(of=OF_SELF).get(
|
||||
pk=self.request.POST.get("cancel_job")
|
||||
)
|
||||
if job.in_flight:
|
||||
messages.warning(self.request, _('The sync job is already in progress.'))
|
||||
try:
|
||||
job = self.order.queued_sync_jobs.select_for_update(of=OF_SELF).get(
|
||||
pk=self.request.POST.get("cancel_job")
|
||||
)
|
||||
except OrderSyncQueue.DoesNotExist:
|
||||
messages.info(self.request, _('The sync job could not be found. It may have been processed in the meantime.'))
|
||||
else:
|
||||
job.delete()
|
||||
messages.success(self.request, _('The sync job has been canceled.'))
|
||||
if job.in_flight:
|
||||
messages.warning(self.request, _('The sync job is already in progress.'))
|
||||
else:
|
||||
job.delete()
|
||||
messages.success(self.request, _('The sync job has been canceled.'))
|
||||
elif self.request.POST.get("run_job_now"):
|
||||
with transaction.atomic():
|
||||
job = self.order.queued_sync_jobs.select_for_update(of=OF_SELF).get(
|
||||
pk=self.request.POST.get("run_job_now")
|
||||
)
|
||||
if job.in_flight:
|
||||
messages.success(self.request, _('The sync job is already in progress.'))
|
||||
try:
|
||||
job = self.order.queued_sync_jobs.select_for_update(of=OF_SELF).get(
|
||||
pk=self.request.POST.get("run_job_now")
|
||||
)
|
||||
except OrderSyncQueue.DoesNotExist:
|
||||
messages.info(self.request, _('The sync job could not be found. It may have been processed in the meantime.'))
|
||||
else:
|
||||
job.not_before = now()
|
||||
job.need_manual_retry = None
|
||||
job.save()
|
||||
sync_single.apply_async(args=(job.pk,))
|
||||
messages.success(self.request, _('The sync job has been set to run as soon as possible.'))
|
||||
if job.in_flight:
|
||||
messages.success(self.request, _('The sync job is already in progress.'))
|
||||
else:
|
||||
job.not_before = now()
|
||||
job.need_manual_retry = None
|
||||
job.save()
|
||||
sync_single.apply_async(args=(job.pk,))
|
||||
messages.success(self.request, _('The sync job has been set to run as soon as possible.'))
|
||||
|
||||
return redirect(self.get_order_url())
|
||||
|
||||
|
||||
@@ -96,9 +96,7 @@ from pretix.base.services.invoices import (
|
||||
invoice_qualified, regenerate_invoice, transmit_invoice,
|
||||
)
|
||||
from pretix.base.services.locking import LockTimeoutException
|
||||
from pretix.base.services.mail import (
|
||||
SendMailException, prefix_subject, render_mail,
|
||||
)
|
||||
from pretix.base.services.mail import prefix_subject, render_mail
|
||||
from pretix.base.services.orders import (
|
||||
OrderChangeManager, OrderError, approve_order, cancel_order, deny_order,
|
||||
extend_order, mark_order_expired, mark_order_refunded,
|
||||
@@ -1054,10 +1052,6 @@ class OrderPaymentConfirm(OrderView):
|
||||
messages.error(self.request, str(e))
|
||||
except PaymentException as e:
|
||||
messages.error(self.request, str(e))
|
||||
except SendMailException:
|
||||
messages.warning(self.request,
|
||||
_('The payment has been marked as complete, but we were unable to send a '
|
||||
'confirmation mail.'))
|
||||
else:
|
||||
messages.success(self.request, _('The payment has been marked as complete.'))
|
||||
else:
|
||||
@@ -1527,9 +1521,6 @@ class OrderTransition(OrderView):
|
||||
'message': str(e)
|
||||
})
|
||||
messages.error(self.request, str(e))
|
||||
except SendMailException:
|
||||
messages.warning(self.request, _('The order has been marked as paid, but we were unable to send a '
|
||||
'confirmation mail.'))
|
||||
else:
|
||||
messages.success(self.request, _('The payment has been created successfully.'))
|
||||
elif self.order.cancel_allowed() and to == 'c':
|
||||
@@ -1748,15 +1739,11 @@ class OrderResendLink(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
try:
|
||||
if 'position' in kwargs:
|
||||
p = get_object_or_404(self.order.positions, pk=kwargs['position'])
|
||||
p.resend_link(user=self.request.user)
|
||||
else:
|
||||
self.order.resend_link(user=self.request.user)
|
||||
except SendMailException:
|
||||
messages.error(self.request, _('There was an error sending the mail. Please try again later.'))
|
||||
return redirect(self.get_order_url())
|
||||
if 'position' in kwargs:
|
||||
p = get_object_or_404(self.order.positions, pk=kwargs['position'])
|
||||
p.resend_link(user=self.request.user)
|
||||
else:
|
||||
self.order.resend_link(user=self.request.user)
|
||||
|
||||
messages.success(self.request, _('The email has been queued to be sent.'))
|
||||
return redirect(self.get_order_url())
|
||||
@@ -2399,24 +2386,18 @@ class OrderSendMail(EventPermissionRequiredMixin, OrderViewMixin, FormView):
|
||||
}
|
||||
return self.get(self.request, *self.args, **self.kwargs)
|
||||
else:
|
||||
try:
|
||||
order.send_mail(
|
||||
form.cleaned_data['subject'], email_template,
|
||||
email_context, 'pretix.event.order.email.custom_sent',
|
||||
self.request.user, auto_email=False,
|
||||
attach_tickets=form.cleaned_data.get('attach_tickets', False),
|
||||
invoices=form.cleaned_data.get('attach_invoices', []),
|
||||
attach_other_files=[a for a in [
|
||||
self.request.event.settings.get('mail_attachment_new_order', as_type=str, default='')[len('file://'):]
|
||||
] if a] if form.cleaned_data.get('attach_new_order', False) else [],
|
||||
)
|
||||
messages.success(self.request,
|
||||
_('Your message has been queued and will be sent to {}.'.format(order.email)))
|
||||
except SendMailException:
|
||||
messages.error(
|
||||
self.request,
|
||||
_('Failed to send mail to the following user: {}'.format(order.email))
|
||||
)
|
||||
order.send_mail(
|
||||
form.cleaned_data['subject'], email_template,
|
||||
email_context, 'pretix.event.order.email.custom_sent',
|
||||
self.request.user, auto_email=False,
|
||||
attach_tickets=form.cleaned_data.get('attach_tickets', False),
|
||||
invoices=form.cleaned_data.get('attach_invoices', []),
|
||||
attach_other_files=[a for a in [
|
||||
self.request.event.settings.get('mail_attachment_new_order', as_type=str, default='')[len('file://'):]
|
||||
] if a] if form.cleaned_data.get('attach_new_order', False) else [],
|
||||
)
|
||||
messages.success(self.request,
|
||||
_('Your message has been queued and will be sent to {}.'.format(order.email)))
|
||||
return super(OrderSendMail, self).form_valid(form)
|
||||
|
||||
def get_success_url(self):
|
||||
@@ -2469,23 +2450,19 @@ class OrderPositionSendMail(OrderSendMail):
|
||||
}
|
||||
return self.get(self.request, *self.args, **self.kwargs)
|
||||
else:
|
||||
try:
|
||||
position.send_mail(
|
||||
form.cleaned_data['subject'],
|
||||
email_template,
|
||||
email_context,
|
||||
'pretix.event.order.position.email.custom_sent',
|
||||
self.request.user,
|
||||
attach_tickets=form.cleaned_data.get('attach_tickets', False),
|
||||
attach_other_files=[a for a in [
|
||||
self.request.event.settings.get('mail_attachment_new_order', as_type=str, default='')[len('file://'):]
|
||||
] if a] if form.cleaned_data.get('attach_new_order', False) else [],
|
||||
)
|
||||
messages.success(self.request,
|
||||
_('Your message has been queued and will be sent to {}.'.format(position.attendee_email)))
|
||||
except SendMailException:
|
||||
messages.error(self.request,
|
||||
_('Failed to send mail to the following user: {}'.format(position.attendee_email)))
|
||||
position.send_mail(
|
||||
form.cleaned_data['subject'],
|
||||
email_template,
|
||||
email_context,
|
||||
'pretix.event.order.position.email.custom_sent',
|
||||
self.request.user,
|
||||
attach_tickets=form.cleaned_data.get('attach_tickets', False),
|
||||
attach_other_files=[a for a in [
|
||||
self.request.event.settings.get('mail_attachment_new_order', as_type=str, default='')[len('file://'):]
|
||||
] if a] if form.cleaned_data.get('attach_new_order', False) else [],
|
||||
)
|
||||
messages.success(self.request,
|
||||
_('Your message has been queued and will be sent to {}.'.format(position.attendee_email)))
|
||||
return super(OrderSendMail, self).form_valid(form)
|
||||
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ from pretix.base.plugins import (
|
||||
PLUGIN_LEVEL_ORGANIZER,
|
||||
)
|
||||
from pretix.base.services.export import multiexport, scheduled_organizer_export
|
||||
from pretix.base.services.mail import SendMailException, mail, prefix_subject
|
||||
from pretix.base.services.mail import mail, prefix_subject
|
||||
from pretix.base.signals import register_multievent_data_exporters
|
||||
from pretix.base.templatetags.rich_text import markdown_compile_email
|
||||
from pretix.base.views.tasks import AsyncAction
|
||||
@@ -1036,24 +1036,21 @@ class TeamMemberView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
|
||||
return ctx
|
||||
|
||||
def _send_invite(self, instance):
|
||||
try:
|
||||
mail(
|
||||
instance.email,
|
||||
_('pretix account invitation'),
|
||||
'pretixcontrol/email/invitation.txt',
|
||||
{
|
||||
'user': self,
|
||||
'organizer': self.request.organizer.name,
|
||||
'team': instance.team.name,
|
||||
'url': build_global_uri('control:auth.invite', kwargs={
|
||||
'token': instance.token
|
||||
})
|
||||
},
|
||||
event=None,
|
||||
locale=self.request.LANGUAGE_CODE
|
||||
)
|
||||
except SendMailException:
|
||||
pass # Already logged
|
||||
mail(
|
||||
instance.email,
|
||||
_('pretix account invitation'),
|
||||
'pretixcontrol/email/invitation.txt',
|
||||
{
|
||||
'user': self,
|
||||
'organizer': self.request.organizer.name,
|
||||
'team': instance.team.name,
|
||||
'url': build_global_uri('control:auth.invite', kwargs={
|
||||
'token': instance.token
|
||||
})
|
||||
},
|
||||
event=None,
|
||||
locale=self.request.LANGUAGE_CODE
|
||||
)
|
||||
|
||||
@transaction.atomic
|
||||
def post(self, request, *args, **kwargs):
|
||||
|
||||
@@ -41,7 +41,6 @@ from hijack import signals
|
||||
|
||||
from pretix.base.auth import get_auth_backends
|
||||
from pretix.base.models import User
|
||||
from pretix.base.services.mail import SendMailException
|
||||
from pretix.control.forms.filter import UserFilterForm
|
||||
from pretix.control.forms.users import UserEditForm
|
||||
from pretix.control.permissions import AdministratorPermissionRequiredMixin
|
||||
@@ -139,11 +138,7 @@ class UserResetView(AdministratorPermissionRequiredMixin, RecentAuthenticationRe
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = get_object_or_404(User, pk=self.kwargs.get("id"))
|
||||
try:
|
||||
self.object.send_password_reset()
|
||||
except SendMailException:
|
||||
messages.error(request, _('There was an error sending the mail. Please try again later.'))
|
||||
return redirect(self.get_success_url())
|
||||
self.object.send_password_reset()
|
||||
|
||||
self.object.log_action('pretix.control.auth.user.forgot_password.mail_sent',
|
||||
user=request.user)
|
||||
|
||||
@@ -34,9 +34,7 @@
|
||||
|
||||
from django.utils.html import format_html
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import (
|
||||
get_language, gettext_lazy as _, pgettext_lazy,
|
||||
)
|
||||
from django.utils.translation import get_language, pgettext_lazy
|
||||
|
||||
from pretix.helpers.templatetags.date_fast import date_fast as _date
|
||||
|
||||
@@ -103,7 +101,7 @@ def daterange(df, dt, as_html=False):
|
||||
until=until,
|
||||
)
|
||||
|
||||
return _("{date_from}{until}{date_to}").format(
|
||||
return "{date_from}{until}{date_to}".format(
|
||||
date_from=_date(df, "DATE_FORMAT"),
|
||||
date_to=_date(dt, "DATE_FORMAT"),
|
||||
until=until,
|
||||
|
||||
@@ -32,7 +32,7 @@ from django_countries.fields import Country
|
||||
from geoip2.errors import AddressNotFoundError
|
||||
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.services.mail import SendMailException, mail
|
||||
from pretix.base.services.mail import mail
|
||||
from pretix.helpers.http import get_client_ip
|
||||
from pretix.helpers.urls import build_absolute_uri
|
||||
|
||||
@@ -159,21 +159,18 @@ def handle_login_source(user, request):
|
||||
})
|
||||
if user.known_login_sources.count() > 1:
|
||||
# Do not send on first login or first login after introduction of this feature:
|
||||
try:
|
||||
with language(user.locale):
|
||||
mail(
|
||||
user.email,
|
||||
_('Login from new source detected'),
|
||||
'pretixcontrol/email/login_notice.txt',
|
||||
{
|
||||
'source': src,
|
||||
'country': Country(str(country)).name if country else _('Unknown country'),
|
||||
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||
'url': build_absolute_uri('control:user.settings')
|
||||
},
|
||||
event=None,
|
||||
user=user,
|
||||
locale=user.locale
|
||||
)
|
||||
except SendMailException:
|
||||
pass # Not much we can do
|
||||
with language(user.locale):
|
||||
mail(
|
||||
user.email,
|
||||
_('Login from new source detected'),
|
||||
'pretixcontrol/email/login_notice.txt',
|
||||
{
|
||||
'source': src,
|
||||
'country': Country(str(country)).name if country else _('Unknown country'),
|
||||
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||
'url': build_absolute_uri('control:user.settings')
|
||||
},
|
||||
event=None,
|
||||
user=user,
|
||||
locale=user.locale
|
||||
)
|
||||
|
||||
@@ -4,8 +4,8 @@ msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-08-19 16:35+0000\n"
|
||||
"PO-Revision-Date: 2025-07-21 21:00+0000\n"
|
||||
"Last-Translator: Nikolai <nikolai@lengefeldt.de>\n"
|
||||
"PO-Revision-Date: 2025-08-27 22:00+0000\n"
|
||||
"Last-Translator: Mie Frydensbjerg <mif@aarhus.dk>\n"
|
||||
"Language-Team: Danish <https://translate.pretix.eu/projects/pretix/pretix/da/"
|
||||
">\n"
|
||||
"Language: da\n"
|
||||
@@ -13,7 +13,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.12.2\n"
|
||||
"X-Generator: Weblate 5.13\n"
|
||||
|
||||
#: pretix/_base_settings.py:87
|
||||
msgid "English"
|
||||
@@ -33244,14 +33244,12 @@ msgstr "Du skal have en gyldig voucherkode for at bestille dette produkt."
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_availability.html:10
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_availability.html:14
|
||||
#, fuzzy
|
||||
msgid "Not available yet."
|
||||
msgstr "Ikke tilgængelig"
|
||||
msgstr "Ikke tilgængelig endnu."
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_availability.html:18
|
||||
#, fuzzy
|
||||
msgid "Not available any more."
|
||||
msgstr "Ikke tilgængelig"
|
||||
msgstr "Ikke tilgængelig længere."
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_availability.html:23
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:89
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-08-19 16:35+0000\n"
|
||||
"PO-Revision-Date: 2025-08-19 17:33+0000\n"
|
||||
"PO-Revision-Date: 2025-08-22 16:00+0000\n"
|
||||
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
||||
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
|
||||
"pretix/pretix/de_Informal/>\n"
|
||||
@@ -34886,7 +34886,7 @@ msgstr ""
|
||||
#: pretix/presale/templates/pretixpresale/event/order.html:19
|
||||
#: pretix/presale/templates/pretixpresale/event/order.html:50
|
||||
msgid "We successfully received your payment. See below for details."
|
||||
msgstr "Wir haben deine Zahlung erfolgreich erhalten."
|
||||
msgstr "Wir haben deine Zahlung erfolgreich erhalten. Details siehe unten."
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/order.html:35
|
||||
msgid ""
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgstr ""
|
||||
"Project-Id-Version: French\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-08-19 16:02+0000\n"
|
||||
"PO-Revision-Date: 2025-05-30 11:06+0000\n"
|
||||
"PO-Revision-Date: 2025-08-28 23:00+0000\n"
|
||||
"Last-Translator: CVZ-es <damien.bremont@casadevelazquez.org>\n"
|
||||
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
"fr/>\n"
|
||||
@@ -16,7 +16,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.11.4\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
|
||||
@@ -551,7 +551,7 @@ msgstr "minutes"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:192
|
||||
msgid "Duplicate"
|
||||
msgstr "Doublon"
|
||||
msgstr "Dupliquer"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:193
|
||||
msgctxt "entry_status"
|
||||
|
||||
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-08-19 16:35+0000\n"
|
||||
"PO-Revision-Date: 2025-08-19 17:46+0000\n"
|
||||
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
||||
"PO-Revision-Date: 2025-08-23 21:00+0000\n"
|
||||
"Last-Translator: \"Luca Sorace \\\"Stranck\\\"\" <strdjn@gmail.com>\n"
|
||||
"Language-Team: Italian <https://translate.pretix.eu/projects/pretix/pretix/"
|
||||
"it/>\n"
|
||||
"Language: it\n"
|
||||
@@ -89,7 +89,7 @@ msgstr "Greco"
|
||||
|
||||
#: pretix/_base_settings.py:104
|
||||
msgid "Hebrew"
|
||||
msgstr ""
|
||||
msgstr "Ebraico"
|
||||
|
||||
#: pretix/_base_settings.py:105
|
||||
msgid "Indonesian"
|
||||
@@ -145,7 +145,7 @@ msgstr "Spagnolo"
|
||||
|
||||
#: pretix/_base_settings.py:118
|
||||
msgid "Spanish (Latin America)"
|
||||
msgstr ""
|
||||
msgstr "Spagnolo (America Latina)"
|
||||
|
||||
#: pretix/_base_settings.py:119
|
||||
msgid "Turkish"
|
||||
@@ -632,6 +632,8 @@ msgid ""
|
||||
"Only includes explicit changes to the voucher, not e.g. an increase of the "
|
||||
"number of redemptions."
|
||||
msgstr ""
|
||||
"Include solamente cambiamenti espliciti al voucher, non, ad esempio, "
|
||||
"l'aumento del numero di riscatti."
|
||||
|
||||
#: pretix/api/webhooks.py:421
|
||||
#, fuzzy
|
||||
@@ -825,6 +827,8 @@ msgid ""
|
||||
"Field \"{field_name}\" is not valid for {available_inputs}. Please check "
|
||||
"your {provider_name} settings."
|
||||
msgstr ""
|
||||
"Il campo {field_name} non è valido per {available_inputs}. Per favore, "
|
||||
"controlla le impostazioni di {provider_name}."
|
||||
|
||||
#: pretix/base/datasync/datasync.py:267
|
||||
#, python-brace-format
|
||||
@@ -863,7 +867,7 @@ msgstr "Dati del prodotto"
|
||||
#: pretix/control/templates/pretixcontrol/order/index.html:176
|
||||
#: pretix/presale/templates/pretixpresale/event/order.html:22
|
||||
msgid "Order details"
|
||||
msgstr ""
|
||||
msgstr "Dettagli dell'ordine"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:133
|
||||
#: pretix/base/datasync/sourcefields.py:299
|
||||
@@ -2910,7 +2914,7 @@ msgstr "Ordini pagati"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1152 pretix/control/views/item.py:975
|
||||
msgid "Pending orders"
|
||||
msgstr "Ordini pendenti"
|
||||
msgstr "Ordini in attesa"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1152
|
||||
msgid "Blocking vouchers"
|
||||
@@ -3379,13 +3383,12 @@ msgstr ""
|
||||
"venditore."
|
||||
|
||||
#: pretix/base/forms/questions.py:1177
|
||||
#, fuzzy
|
||||
msgid "No invoice requested"
|
||||
msgstr "Cancellazione"
|
||||
msgstr "Fattura non richiesta"
|
||||
|
||||
#: pretix/base/forms/questions.py:1179
|
||||
msgid "Invoice transmission method"
|
||||
msgstr ""
|
||||
msgstr "Metodo per le trasmettere le fatture"
|
||||
|
||||
#: pretix/base/forms/questions.py:1324
|
||||
msgid "You need to provide a company name."
|
||||
@@ -3400,26 +3403,28 @@ msgid ""
|
||||
"If you enter an invoice address, you also need to select an invoice "
|
||||
"transmission method."
|
||||
msgstr ""
|
||||
"Se inserisci l'indirizzo della fattura, devi anche specificare come "
|
||||
"trasmetterla."
|
||||
|
||||
#: pretix/base/forms/questions.py:1380
|
||||
#, 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 ""
|
||||
"Il tipo di supporto selezionato non è abilitato nelle tue impostazioni da "
|
||||
"organizzatore."
|
||||
"Il metodo selezionato per trasmettere la fattura non è disponibile nella "
|
||||
"nazione specificata nel tuo indirizzo."
|
||||
|
||||
#: pretix/base/forms/questions.py:1389
|
||||
msgid ""
|
||||
"The selected type of invoice transmission requires a field that is currently "
|
||||
"not available, please reach out to the organizer."
|
||||
msgstr ""
|
||||
"Il metodo selezionato per trasmettere la fattura richiede un campo non "
|
||||
"disponibile, per favore contatta l'organizzatore."
|
||||
|
||||
#: pretix/base/forms/questions.py:1393
|
||||
msgid "This field is required for the selected type of invoice transmission."
|
||||
msgstr ""
|
||||
msgstr "Questo campo è necessario per trasmettere la fattura come selezionato."
|
||||
|
||||
#: pretix/base/forms/user.py:51 pretix/control/forms/users.py:43
|
||||
msgid ""
|
||||
@@ -3533,7 +3538,7 @@ msgstr "Cliente individuale"
|
||||
|
||||
#: pretix/base/invoicing/email.py:50
|
||||
msgid "Email invoice directly to accounting department"
|
||||
msgstr ""
|
||||
msgstr "Invia la fattura alla contabilità"
|
||||
|
||||
#: pretix/base/invoicing/email.py:51
|
||||
#, fuzzy
|
||||
@@ -3550,7 +3555,7 @@ msgstr "Indirizzo email verificato"
|
||||
|
||||
#: pretix/base/invoicing/email.py:91
|
||||
msgid "PDF via email"
|
||||
msgstr ""
|
||||
msgstr "Invia PDF via email"
|
||||
|
||||
#: pretix/base/invoicing/national.py:37
|
||||
msgctxt "italian_invoice"
|
||||
@@ -3950,7 +3955,7 @@ msgstr "Sono state trovate più date corrispondenti."
|
||||
|
||||
#: pretix/base/modelimport_orders.py:73
|
||||
msgid "Grouping"
|
||||
msgstr ""
|
||||
msgstr "Raggruppa"
|
||||
|
||||
#: pretix/base/modelimport_orders.py:75
|
||||
msgid ""
|
||||
@@ -3958,6 +3963,10 @@ msgid ""
|
||||
"together...\". Lines with the same grouping value will be put in the same "
|
||||
"order, but MUST be consecutive lines of the input file."
|
||||
msgstr ""
|
||||
"Usabile solamente quando \"Modo di importazione\" è impostato su "
|
||||
"\"Raggruppa più righe insieme...\". Righe con lo stesso valore di "
|
||||
"raggruppamento manterranno lo stesso ordine, ma DEVONO essere consecutive "
|
||||
"nel file in input."
|
||||
|
||||
#: pretix/base/modelimport_orders.py:101
|
||||
msgid "Enter a valid phone number."
|
||||
@@ -3979,6 +3988,8 @@ msgstr "Devi selezionare una data."
|
||||
msgid ""
|
||||
"The product can be specified by its internal ID, full name or internal name."
|
||||
msgstr ""
|
||||
"Il prodotto può essere identificato usando il ID interno, nome completo o "
|
||||
"nome interno."
|
||||
|
||||
#: pretix/base/modelimport_orders.py:149
|
||||
#: pretix/base/modelimport_vouchers.py:194
|
||||
@@ -3999,6 +4010,7 @@ msgstr "Variante prodotto"
|
||||
#: pretix/base/modelimport_orders.py:161
|
||||
msgid "The variation can be specified by its internal ID or full name."
|
||||
msgstr ""
|
||||
"La variante può essere identificata usando il suo ID interno o il suo nome."
|
||||
|
||||
#: pretix/base/modelimport_orders.py:181
|
||||
#: pretix/base/modelimport_vouchers.py:225
|
||||
@@ -4028,7 +4040,7 @@ msgstr "Inserire un codice paese valido."
|
||||
|
||||
#: pretix/base/modelimport_orders.py:290 pretix/base/modelimport_orders.py:441
|
||||
msgid "The state can be specified by its short form or full name."
|
||||
msgstr ""
|
||||
msgstr "Lo stato può essere indicato usando il suo nome completo o abbreviato."
|
||||
|
||||
#: pretix/base/modelimport_orders.py:300 pretix/base/modelimport_orders.py:450
|
||||
msgid "States are not supported for this country."
|
||||
@@ -4085,6 +4097,8 @@ msgid ""
|
||||
"The sales channel can be specified by it's internal identifier or its full "
|
||||
"name."
|
||||
msgstr ""
|
||||
"Il canale di vendita può essere indicato usando il suo identificatore "
|
||||
"interno o il suo nome completo."
|
||||
|
||||
#: pretix/base/modelimport_orders.py:599 pretix/base/modelimport_orders.py:601
|
||||
msgid "Please enter a valid sales channel."
|
||||
@@ -4092,7 +4106,7 @@ msgstr "Inserire un canale di vendita valido."
|
||||
|
||||
#: pretix/base/modelimport_orders.py:611
|
||||
msgid "The seat needs to be specified by its internal ID."
|
||||
msgstr ""
|
||||
msgstr "Il posto deve essere indicato usando il suo ID interno."
|
||||
|
||||
#: pretix/base/modelimport_orders.py:626
|
||||
#: pretix/base/modelimport_vouchers.py:291
|
||||
@@ -4567,19 +4581,20 @@ msgstr "Separa valori multipli con spazi"
|
||||
|
||||
#: pretix/base/models/datasync.py:53
|
||||
msgid "Temporary error, auto-retry limit exceeded"
|
||||
msgstr ""
|
||||
msgstr "Errore momentaneo, limite di tentativi automatici superato"
|
||||
|
||||
#: pretix/base/models/datasync.py:54
|
||||
msgid "Provider reported a permanent error"
|
||||
msgstr ""
|
||||
msgstr "Il provider ha riportato un errore permanente"
|
||||
|
||||
#: pretix/base/models/datasync.py:55
|
||||
msgid "Misconfiguration, please check provider settings"
|
||||
msgstr ""
|
||||
"Configurazione errata, per favore controlla le impostazioni del provider"
|
||||
|
||||
#: pretix/base/models/datasync.py:56 pretix/base/models/datasync.py:57
|
||||
msgid "System error, needs manual intervention"
|
||||
msgstr ""
|
||||
msgstr "Errore del sistema, richiesto intervento manuale"
|
||||
|
||||
#: pretix/base/models/devices.py:70 pretix/base/models/items.py:1675
|
||||
msgid "Internal identifier"
|
||||
@@ -4841,13 +4856,15 @@ msgstr "Opzionale. Nessun prodotto verrà venduto prima di questa data."
|
||||
|
||||
#: pretix/base/models/event.py:620
|
||||
msgid "This event is remote or partially remote."
|
||||
msgstr ""
|
||||
msgstr "Questo evento è totalmente o parzialmente da remoto."
|
||||
|
||||
#: pretix/base/models/event.py:621
|
||||
msgid ""
|
||||
"This will be used to let users know if the event is in a different timezone "
|
||||
"and let’s us calculate users’ local times."
|
||||
msgstr ""
|
||||
"Verrà usato per informare gli utenti se l'evento si trova in un fuso orario "
|
||||
"differente e permette di calcolare l'ora locale dei vari utenti."
|
||||
|
||||
#: pretix/base/models/event.py:641 pretix/base/models/organizer.py:97
|
||||
#: pretix/control/navigation.py:65 pretix/control/navigation.py:499
|
||||
@@ -5131,14 +5148,12 @@ msgid "Manual transaction"
|
||||
msgstr "Transazione manuale"
|
||||
|
||||
#: pretix/base/models/invoices.py:120
|
||||
#, fuzzy
|
||||
#| msgid "Pending amount"
|
||||
msgid "pending transmission"
|
||||
msgstr "Ammontare rimanente"
|
||||
msgstr "trasmissione in attesa"
|
||||
|
||||
#: pretix/base/models/invoices.py:121
|
||||
msgid "currently being transmitted"
|
||||
msgstr ""
|
||||
msgstr "trasmissione in corso"
|
||||
|
||||
#: pretix/base/models/invoices.py:122
|
||||
#, fuzzy
|
||||
@@ -5155,11 +5170,11 @@ msgstr "fallito"
|
||||
#: pretix/base/models/invoices.py:124
|
||||
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:56
|
||||
msgid "unknown"
|
||||
msgstr ""
|
||||
msgstr "sconosciuto"
|
||||
|
||||
#: pretix/base/models/invoices.py:125
|
||||
msgid "not transmitted due to test mode"
|
||||
msgstr ""
|
||||
msgstr "non trasmesso perché in modalità di test"
|
||||
|
||||
#: pretix/base/models/invoices.py:217
|
||||
#, python-format
|
||||
@@ -6113,6 +6128,9 @@ msgid ""
|
||||
"with changing the type of question without data loss. Consider hiding this "
|
||||
"question and creating a new one instead."
|
||||
msgstr ""
|
||||
"Sono già presenti delle risposte a questa domanda di tipo incompatibile "
|
||||
"rispetto al nuovo. Il cambio avverrebbe perdendo informazioni. È possibile "
|
||||
"invece procedere nascondendo questa domanda e creandone un'altra."
|
||||
|
||||
#: pretix/base/models/items.py:1961
|
||||
#: pretix/control/templates/pretixcontrol/items/question.html:90
|
||||
@@ -8243,7 +8261,7 @@ msgstr "Il tuo file di layout non è un layout valido. Messaggio di errore: {}"
|
||||
#: pretix/base/plugins.py:136
|
||||
#: pretix/control/templates/pretixcontrol/event/quick_setup.html:132
|
||||
msgid "Features"
|
||||
msgstr ""
|
||||
msgstr "Funzionalità"
|
||||
|
||||
#: pretix/base/plugins.py:138
|
||||
msgid "Integrations"
|
||||
@@ -8261,7 +8279,7 @@ msgstr "Formato di esportazione"
|
||||
|
||||
#: pretix/base/plugins.py:141
|
||||
msgid "API features"
|
||||
msgstr ""
|
||||
msgstr "Funzionalità dell'API"
|
||||
|
||||
#: pretix/base/reldate.py:38
|
||||
msgid "Event start"
|
||||
@@ -9147,6 +9165,8 @@ msgid ""
|
||||
"The grouping \"%(value)s\" occurs on non-consecutive lines (seen again on "
|
||||
"line %(row)s)."
|
||||
msgstr ""
|
||||
"Il raggruppamento \"%(value)s\" è stato trovato su righe non consecutive ("
|
||||
"visto già su linea %(row)s)."
|
||||
|
||||
#: pretix/base/services/modelimport.py:151
|
||||
#, python-brace-format
|
||||
@@ -9154,6 +9174,9 @@ msgid ""
|
||||
"Inconsistent data in row {row}: Column {col} contains value \"{val_line}\", "
|
||||
"but for this order, the value has already been set to \"{val_order}\"."
|
||||
msgstr ""
|
||||
"Dati inconsistenti su riga {row}: La colonna {col} contiene il valore "
|
||||
"\"{val_line}\", ma sullo stesso ordine è già presente il valore "
|
||||
"\"{val_order}\"."
|
||||
|
||||
#: pretix/base/services/modelimport.py:165
|
||||
#: pretix/base/services/modelimport.py:277
|
||||
@@ -9752,19 +9775,19 @@ msgstr "Chiedi il nome dei partecipanti"
|
||||
|
||||
#: pretix/base/settings.py:359
|
||||
msgid "Ask for a name for all personalized tickets."
|
||||
msgstr ""
|
||||
msgstr "Richiedi un nome per tutti i biglietti personalizzati."
|
||||
|
||||
#: pretix/base/settings.py:368
|
||||
msgid "Require attendee names"
|
||||
msgstr ""
|
||||
msgstr "Richiedi i nomi dei partecipanti"
|
||||
|
||||
#: pretix/base/settings.py:369
|
||||
msgid "Require customers to fill in the names of all attendees."
|
||||
msgstr ""
|
||||
msgstr "Obbliga i clienti a riempire i nomi di tutti i partecipanti."
|
||||
|
||||
#: pretix/base/settings.py:379
|
||||
msgid "Ask for email addresses per ticket"
|
||||
msgstr ""
|
||||
msgstr "Chiedi l'indirizzo email per i biglietti"
|
||||
|
||||
#: pretix/base/settings.py:380
|
||||
msgid ""
|
||||
@@ -9777,10 +9800,18 @@ msgid ""
|
||||
"primary email address, not to the per-attendee addresses. You can however "
|
||||
"enable this in the email settings."
|
||||
msgstr ""
|
||||
"Normalmente Pretix chiede un indirizzo email per ordine, che verrà usato per "
|
||||
"inviare la conferma dello stesso. Se attivi questa opzione, il sistema "
|
||||
"chiederà in aggiunta un indirizzo email per ogni biglietto personalizzato. "
|
||||
"Questo può essere utile per ottenere un indirizzo email per ogni "
|
||||
"partecipante, anche in caso di ordini di gruppo. In ogni caso, di default "
|
||||
"Pretix invierà la conferma dell'ordine solamente all'email principale, non "
|
||||
"ad ogni partecipante. Puoi comunque cambiare questa opzione nelle "
|
||||
"impostazioni della email."
|
||||
|
||||
#: pretix/base/settings.py:394
|
||||
msgid "Require email addresses per ticket"
|
||||
msgstr ""
|
||||
msgstr "Richiedi un indirizzo email per ogni biglietto"
|
||||
|
||||
#: pretix/base/settings.py:395
|
||||
msgid ""
|
||||
@@ -9788,60 +9819,63 @@ msgid ""
|
||||
"tickets. See the above option for more details. One email address for the "
|
||||
"order confirmation will always be required regardless of this setting."
|
||||
msgstr ""
|
||||
"Obbliga i clienti ad indicare un indirizzo email per ogni biglietto "
|
||||
"personalizzato. Guarda l'opzione sopra per maggiori informazioni. Verrà "
|
||||
"comunque richiesto un indirizzo email per la conferma dell'ordine, a "
|
||||
"prescindere da questa opzione."
|
||||
|
||||
#: pretix/base/settings.py:407
|
||||
msgid "Ask for company per ticket"
|
||||
msgstr ""
|
||||
msgstr "Chiedi l'azienda su ogni biglietto"
|
||||
|
||||
#: pretix/base/settings.py:416
|
||||
msgid "Require company per ticket"
|
||||
msgstr ""
|
||||
msgstr "Obbliga a inserire l'azienda su ogni biglietto"
|
||||
|
||||
#: pretix/base/settings.py:426
|
||||
msgid "Ask for postal addresses per ticket"
|
||||
msgstr ""
|
||||
msgstr "Chiedi l'indirizzo postale su ogni biglietto"
|
||||
|
||||
#: pretix/base/settings.py:435
|
||||
msgid "Require postal addresses per ticket"
|
||||
msgstr ""
|
||||
msgstr "Obbliga ad inserire l'indirizzo postale su ogni biglietto"
|
||||
|
||||
#: pretix/base/settings.py:445
|
||||
msgid "Ask for the order email address twice"
|
||||
msgstr ""
|
||||
msgstr "Chiedi di inserire due volte l'indirizzo email"
|
||||
|
||||
#: pretix/base/settings.py:446
|
||||
msgid ""
|
||||
"Require customers to fill in the primary email address twice to avoid errors."
|
||||
msgstr ""
|
||||
msgstr "Obbliga i clienti di inserire due volte l'indirizzo email per conferma."
|
||||
|
||||
#: pretix/base/settings.py:455
|
||||
msgid "Ask for a phone number per order"
|
||||
msgstr ""
|
||||
msgstr "Chiedi il numero di telefono"
|
||||
|
||||
#: pretix/base/settings.py:464
|
||||
msgid "Require a phone number per order"
|
||||
msgstr ""
|
||||
msgstr "Obbliga l'inserimento del numero di telefono"
|
||||
|
||||
#: pretix/base/settings.py:474
|
||||
msgid "Ask for invoice address"
|
||||
msgstr ""
|
||||
msgstr "Chiedi l'indirizzo di fattura"
|
||||
|
||||
#: pretix/base/settings.py:483
|
||||
msgid "Do not ask for invoice address if an order is free"
|
||||
msgstr ""
|
||||
msgstr "Non chiedere l'indirizzo di fattura se un ordine è gratuito"
|
||||
|
||||
#: pretix/base/settings.py:492
|
||||
msgid "Require customer name"
|
||||
msgstr ""
|
||||
msgstr "Obbliga l'inserimento del nome del cliente"
|
||||
|
||||
#: pretix/base/settings.py:501
|
||||
msgid "Show attendee names on invoices"
|
||||
msgstr ""
|
||||
msgstr "Mostra il nome del partecipante sulla fattura"
|
||||
|
||||
#: pretix/base/settings.py:510
|
||||
#, fuzzy
|
||||
msgid "Show event location on invoices"
|
||||
msgstr "Prevendita non ancora attiva"
|
||||
msgstr "Mostra l'indirizzo dell'evento sulla fattura"
|
||||
|
||||
#: pretix/base/settings.py:511
|
||||
msgid ""
|
||||
@@ -9849,22 +9883,27 @@ msgid ""
|
||||
"same for all lines. It will be shown on every line if there are different "
|
||||
"locations."
|
||||
msgstr ""
|
||||
"L'indirizzo dell'evento verrà mostrato sotto la lista dei prodotti se è lo "
|
||||
"stesso per tutti. Se l'evento si svolgerà in luoghi differenti, verrà "
|
||||
"mostrato sotto ciascuno."
|
||||
|
||||
#: pretix/base/settings.py:521
|
||||
#, fuzzy
|
||||
msgid "Show exchange rates"
|
||||
msgstr "Mostra il valore a"
|
||||
msgstr "Mostra i tassi di cambio"
|
||||
|
||||
#: pretix/base/settings.py:524 pretix/base/settings.py:532
|
||||
#: pretix/control/forms/item.py:626
|
||||
msgid "Never"
|
||||
msgstr ""
|
||||
msgstr "Mai"
|
||||
|
||||
#: pretix/base/settings.py:525 pretix/base/settings.py:533
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"Based on European Central Bank daily rates, whenever the invoice recipient "
|
||||
"is in an EU country that uses a different currency."
|
||||
msgstr ""
|
||||
"In base ai cambio della Banca Centrale Europea, se il destinatario della "
|
||||
"fattura è in una nazione dell'EU che usa una valuta differente."
|
||||
|
||||
#: pretix/base/settings.py:527 pretix/base/settings.py:535
|
||||
msgid ""
|
||||
@@ -9874,25 +9913,24 @@ msgstr ""
|
||||
|
||||
#: pretix/base/settings.py:545
|
||||
msgid "Require invoice address"
|
||||
msgstr ""
|
||||
msgstr "Obbliga l'inserimento dell'indirizzo di fatturazione"
|
||||
|
||||
#: pretix/base/settings.py:555
|
||||
#, fuzzy
|
||||
msgid "Require a business address"
|
||||
msgstr "Crea un nuovo organizzatore"
|
||||
msgstr "Richiedi un indirizzo aziendale"
|
||||
|
||||
#: pretix/base/settings.py:556
|
||||
msgid "This will require users to enter a company name."
|
||||
msgstr ""
|
||||
msgstr "Obbligherà gli utenti ad inserire il nome della propria azienda."
|
||||
|
||||
#: pretix/base/settings.py:566
|
||||
msgid "Ask for beneficiary"
|
||||
msgstr ""
|
||||
msgstr "Chiedi il beneficiario"
|
||||
|
||||
#: pretix/base/settings.py:576
|
||||
#, fuzzy
|
||||
msgid "Custom recipient field label"
|
||||
msgstr "Campo indirizzo personalizzato"
|
||||
msgstr "Campo indirizzo del destinatario personalizzato"
|
||||
|
||||
#: pretix/base/settings.py:578
|
||||
msgid ""
|
||||
@@ -9902,11 +9940,15 @@ msgid ""
|
||||
"details as well as for displaying the value on the invoice. It will be shown "
|
||||
"on the invoice below the headline. The field will not be required."
|
||||
msgstr ""
|
||||
"Se vuoi aggiungere un campo di testo personalizzato, ad esempio un numero di "
|
||||
"registrazione specifico per ogni nazione, al tuo modulo per la fattura, "
|
||||
"inserisci il nome qui. Questo nome verrà usato sia per chiedere agli utenti "
|
||||
"di inserire i propri dettagli, che per mostrare il valore sulla fattura, "
|
||||
"sotto il titolo. Questo campo non è obbligatorio."
|
||||
|
||||
#: pretix/base/settings.py:591
|
||||
#, fuzzy
|
||||
msgid "Custom recipient field help text"
|
||||
msgstr "Campo indirizzo personalizzato"
|
||||
msgstr "Testo di aiuto per il campo indirizzo del destinatario personalizzato"
|
||||
|
||||
#: pretix/base/settings.py:593
|
||||
msgid ""
|
||||
@@ -9914,10 +9956,13 @@ msgid ""
|
||||
"will be displayed underneath the field. It will not be displayed on the "
|
||||
"invoice."
|
||||
msgstr ""
|
||||
"Se utilizzi il campo indirizzo del destinatario personalizzato, puoi "
|
||||
"specificare un testo di aiuto che verrà mostrato al di sotto del campo "
|
||||
"stesso. Non verrà, invece, mostrato sulla fattura."
|
||||
|
||||
#: pretix/base/settings.py:603
|
||||
msgid "Ask for VAT ID"
|
||||
msgstr ""
|
||||
msgstr "Chiedi la partita IVA"
|
||||
|
||||
#: pretix/base/settings.py:605
|
||||
#, python-brace-format
|
||||
@@ -9926,10 +9971,14 @@ msgid ""
|
||||
"only requested from business customers in the following countries: "
|
||||
"{countries}"
|
||||
msgstr ""
|
||||
"Funzionerà solamente se viene richiesto anche l'indirizzo per la "
|
||||
"fatturazione. La partita IVA non è mai un campo obbligatorio e verrà "
|
||||
"richiesta solamente per i clienti aziendali dalle seguenti nazioni: "
|
||||
"{countries}"
|
||||
|
||||
#: pretix/base/settings.py:618
|
||||
msgid "Invoice address explanation"
|
||||
msgstr ""
|
||||
msgstr "Spiegazione dell'indirizzo di fattura"
|
||||
|
||||
#: pretix/base/settings.py:621
|
||||
msgid "This text will be shown above the invoice address form during checkout."
|
||||
@@ -9940,54 +9989,64 @@ msgstr ""
|
||||
#: pretix/base/settings.py:630
|
||||
msgid "Show paid amount on partially paid invoices"
|
||||
msgstr ""
|
||||
"Mostra quanto è stato già versato sugli ordini pagati solamente parzialmente"
|
||||
|
||||
#: pretix/base/settings.py:631
|
||||
msgid ""
|
||||
"If an invoice has already been paid partially, this option will add the paid "
|
||||
"and pending amount to the invoice."
|
||||
msgstr ""
|
||||
"Se una fattura è già stata pagata parzialmente, questa opzione aggiungerà "
|
||||
"quanto è stato pagato e quanto rimane da pagare alla stessa."
|
||||
|
||||
#: pretix/base/settings.py:641
|
||||
msgid "Show free products on invoices"
|
||||
msgstr ""
|
||||
msgstr "Mostra i prodotti gratuiti sulla fattura"
|
||||
|
||||
#: pretix/base/settings.py:642
|
||||
msgid ""
|
||||
"Note that invoices will never be generated for orders that contain only free "
|
||||
"products."
|
||||
msgstr ""
|
||||
"Nota che non verrà emessa alcuna fattura per gli ordini che contengono "
|
||||
"solamente prodotti gratuiti."
|
||||
|
||||
#: pretix/base/settings.py:652
|
||||
msgid "Show expiration date of order"
|
||||
msgstr ""
|
||||
msgstr "Mostra la data di scadenza degli ordini"
|
||||
|
||||
#: pretix/base/settings.py:653
|
||||
msgid ""
|
||||
"The expiration date will not be shown if the invoice is generated after the "
|
||||
"order is paid."
|
||||
msgstr ""
|
||||
"La data di scadenza non verrà mostrata se la fattura verrà emessa dopo che "
|
||||
"l'ordine è stato pagato."
|
||||
|
||||
#: pretix/base/settings.py:663
|
||||
msgid "Minimum length of invoice number after prefix"
|
||||
msgstr ""
|
||||
msgstr "Lunghezza minima del numero di fattura, escluso il prefisso"
|
||||
|
||||
#: pretix/base/settings.py:664
|
||||
msgid ""
|
||||
"The part of your invoice number after your prefix will be filled up with "
|
||||
"leading zeros up to this length, e.g. INV-001 or INV-00001."
|
||||
msgstr ""
|
||||
"La parte del tuo numero di fattura dopo il prefisso verrà riempita con zeri "
|
||||
"iniziali per raggiungere la lunghezza impostata, ad esempio: INV-001 oppure "
|
||||
"INV-00001."
|
||||
|
||||
#: pretix/base/settings.py:675
|
||||
msgid "Generate invoices with consecutive numbers"
|
||||
msgstr ""
|
||||
msgstr "Genera le fatture seguendo una numerazione consecutiva"
|
||||
|
||||
#: pretix/base/settings.py:676
|
||||
msgid "If deactivated, the order code will be used in the invoice number."
|
||||
msgstr ""
|
||||
msgstr "Se disattivata, il codice d'ordine verrà usato come numero di fattura."
|
||||
|
||||
#: pretix/base/settings.py:685
|
||||
msgid "Invoice number prefix"
|
||||
msgstr ""
|
||||
msgstr "Prefisso del numero di fattura"
|
||||
|
||||
#: pretix/base/settings.py:686
|
||||
msgid ""
|
||||
@@ -9999,6 +10058,14 @@ msgid ""
|
||||
"can use %Y (with century) %y (without century) to insert the year of the "
|
||||
"invoice, or %m and %d for the day of month."
|
||||
msgstr ""
|
||||
"Verrà anteposto ai numeri di fattura. Se non riempi questo campo, verrà "
|
||||
"usato lo slug dell'evento, seguito da -. Attenzione: se più eventi della "
|
||||
"stessa organizzazione usano lo stesso valore in questo campo, condivideranno "
|
||||
"la stessa numerazione: Un singolo numero di fattura verrà usato una ed una "
|
||||
"sola volta per ogni evento che condivide lo stesso prefisso. Le modifiche a "
|
||||
"questo campo si applicheranno solamente alle fatture future. Puoi usare %Y ("
|
||||
"con secolo) %y (senza secolo) per inserire l'anno della fattura, oppure %m e "
|
||||
"%d per il mese ed il giorno."
|
||||
|
||||
#: pretix/base/settings.py:698 pretix/base/settings.py:720
|
||||
#, python-brace-format
|
||||
@@ -10007,7 +10074,7 @@ msgstr "Per favore, utilizza soltanto i caratteri {allowed} in questo campo."
|
||||
|
||||
#: pretix/base/settings.py:711
|
||||
msgid "Invoice number prefix for cancellations"
|
||||
msgstr ""
|
||||
msgstr "Prefisso per i numeri di fattura per le cancellazioni"
|
||||
|
||||
#: pretix/base/settings.py:712
|
||||
msgid ""
|
||||
@@ -10015,42 +10082,48 @@ msgid ""
|
||||
"this field empty, the same numbering scheme will be used that you configured "
|
||||
"for regular invoices."
|
||||
msgstr ""
|
||||
"Verrà anteposto al numero di fatturazione per le cancellazioni. Se lasci "
|
||||
"questo campo vuoto, verrà utilizzato lo stesso schema di numerazione "
|
||||
"configurato per le fatture normali."
|
||||
|
||||
#: pretix/base/settings.py:733
|
||||
msgid "Highlight order code to make it stand out visibly"
|
||||
msgstr ""
|
||||
msgstr "Evidenzia il codice d'ordine per renderlo più visibile"
|
||||
|
||||
#: pretix/base/settings.py:734 pretix/base/settings.py:745
|
||||
msgid "Only respected by some invoice renderers."
|
||||
msgstr ""
|
||||
msgstr "Funziona solamente con alcuni renderizzatori di fatture."
|
||||
|
||||
#: pretix/base/settings.py:744 pretix/base/settings.py:2959
|
||||
#: pretix/control/templates/pretixcontrol/pdf/index.html:436
|
||||
msgid "Font"
|
||||
msgstr ""
|
||||
msgstr "Font"
|
||||
|
||||
#: pretix/base/settings.py:770
|
||||
#, fuzzy
|
||||
msgid "Length of ticket codes"
|
||||
msgstr "Codice biglietto"
|
||||
msgstr "Lunghezza dei codici dei biglietti"
|
||||
|
||||
#: pretix/base/settings.py:797
|
||||
msgid "Reservation period"
|
||||
msgstr ""
|
||||
msgstr "Durata del carrello"
|
||||
|
||||
#: pretix/base/settings.py:799
|
||||
msgid ""
|
||||
"The number of minutes the items in a user's cart are reserved for this user."
|
||||
msgstr ""
|
||||
"Numero di minuti per cui un prodotto rimane riservato nel carrello di un "
|
||||
"utente."
|
||||
|
||||
#: pretix/base/settings.py:808
|
||||
msgid ""
|
||||
"Directly redirect to check-out after a product has been added to the cart."
|
||||
msgstr ""
|
||||
"Reindirizza direttamente al check-out dopo che un prodotto viene aggiunto al "
|
||||
"carrello."
|
||||
|
||||
#: pretix/base/settings.py:817
|
||||
msgid "End of presale text"
|
||||
msgstr ""
|
||||
msgstr "Testo per la fine della prevendita"
|
||||
|
||||
#: pretix/base/settings.py:820
|
||||
msgid ""
|
||||
@@ -10058,16 +10131,21 @@ msgid ""
|
||||
"timeframe for this event is over. You can use it to describe other options "
|
||||
"to get a ticket, such as a box office."
|
||||
msgstr ""
|
||||
"Questo testo verrà mostrato sopra lo shop una volta che il termine per la "
|
||||
"vendita dei biglietti viene raggiunto. Puoi utilizzarlo per descrivere "
|
||||
"alternative per l'acquisto dei biglietti, come ad esempio un box office."
|
||||
|
||||
#: pretix/base/settings.py:834
|
||||
msgid "Guidance text"
|
||||
msgstr ""
|
||||
msgstr "Guida ai pagamenti"
|
||||
|
||||
#: pretix/base/settings.py:835
|
||||
msgid ""
|
||||
"This text will be shown above the payment options. You can explain the "
|
||||
"choices to the user here, if you want."
|
||||
msgstr ""
|
||||
"Questo testo apparirà sopra le opzioni di pagamento. Puoi usarlo per "
|
||||
"spiegare le varie opzioni agli utenti, se vuoi."
|
||||
|
||||
#: pretix/base/settings.py:846 pretix/base/settings.py:855
|
||||
msgid "in days"
|
||||
@@ -10078,19 +10156,21 @@ msgid "in minutes"
|
||||
msgstr "in minuti"
|
||||
|
||||
#: pretix/base/settings.py:851
|
||||
#, fuzzy
|
||||
msgid "Set payment term"
|
||||
msgstr "Nascondi metodo di pagamento"
|
||||
msgstr "Termine dei pagamenti"
|
||||
|
||||
#: pretix/base/settings.py:858
|
||||
msgid ""
|
||||
"If using days, the order will expire at the end of the last day. Using "
|
||||
"minutes is more exact, but should only be used for real-time payment methods."
|
||||
msgstr ""
|
||||
"Utilizzando i giorni, l'ordine scadrà alla fine dell'ultimo giorno. Usando "
|
||||
"invece i minuti, si può essere più precisi, ma questa opzione dovrebbe "
|
||||
"essere utilizzata solamente per i metodi di pagamento in tempo reale."
|
||||
|
||||
#: pretix/base/settings.py:868
|
||||
msgid "Payment term in days"
|
||||
msgstr ""
|
||||
msgstr "Termine di pagamento in giorni"
|
||||
|
||||
#: pretix/base/settings.py:875
|
||||
msgid ""
|
||||
@@ -10099,10 +10179,15 @@ msgid ""
|
||||
"recommend 14 days. If you only use real-time payment methods, we recommend "
|
||||
"still setting two or three days to allow people to retry failed payments."
|
||||
msgstr ""
|
||||
"Il numero di giorni in cui un utente può pagare un ordine già fatto per "
|
||||
"mantenere la propria prenotazione. Se utilizzi metodi di pagamento lenti "
|
||||
"come i bonifici, raccomandiamo un valore di 14 giorni. Se usi invece metodi "
|
||||
"di pagamento in tempo reale, raccomandiamo di lasciare agli utenti 2-3 "
|
||||
"giorni per permettergli di riprovare pagamenti falliti."
|
||||
|
||||
#: pretix/base/settings.py:893
|
||||
msgid "Only end payment terms on weekdays"
|
||||
msgstr ""
|
||||
msgstr "Fai cadere il termine dei pagamenti solamente nei giorni feriali"
|
||||
|
||||
#: pretix/base/settings.py:894
|
||||
msgid ""
|
||||
@@ -10111,11 +10196,14 @@ msgid ""
|
||||
"some countries by civil law. This will not effect the last date of payments "
|
||||
"configured below."
|
||||
msgstr ""
|
||||
"Se questa opzione è attiva e il termine di pagamento cade di Sabato o "
|
||||
"Domenica, verrà spostato al Lunedì successivo. Questo è legalmente richiesto "
|
||||
"in alcune nazioni. Questa opzione non avrà comunque effetto sull'ultima data "
|
||||
"di pagamento, configurabile sotto."
|
||||
|
||||
#: pretix/base/settings.py:910
|
||||
#, fuzzy
|
||||
msgid "Payment term in minutes"
|
||||
msgstr "ID Pagamento"
|
||||
msgstr "Termine di pagamento in minuti"
|
||||
|
||||
#: pretix/base/settings.py:911
|
||||
msgid ""
|
||||
@@ -10124,6 +10212,11 @@ msgid ""
|
||||
"methods. Please note that for technical reasons, the actual time frame might "
|
||||
"be a few minutes longer before the order is marked as expired."
|
||||
msgstr ""
|
||||
"Il numero di minuti in cui un utente può pagare un ordine già fatto per "
|
||||
"mantenere la propria prenotazione. Usa questa opzione solo se usi "
|
||||
"esclusivamente metodi di pagamento in tempo reale. Tieni in mente che per "
|
||||
"ragioni tecniche, il termine potrebbe essere esteso automaticamente di "
|
||||
"alcuni minuti."
|
||||
|
||||
#: pretix/base/settings.py:934
|
||||
msgid "Last date of payments"
|
||||
@@ -10135,10 +10228,14 @@ msgid ""
|
||||
"configured above. If you use the event series feature and an order contains "
|
||||
"tickets for multiple dates, the earliest date will be used."
|
||||
msgstr ""
|
||||
"L'ultimo giorno in cui i pagamenti vengono accettati. Questa opzione ha "
|
||||
"precedenza rispetto a tutte le altre configurate sopra. Se usi la "
|
||||
"funzionalità per avere una serie di eventi ed un ordine contiene biglietti "
|
||||
"per più date, verrà usata la prima."
|
||||
|
||||
#: pretix/base/settings.py:946
|
||||
msgid "Automatically expire unpaid orders"
|
||||
msgstr ""
|
||||
msgstr "Fai scadere automaticamente gli ordini non pagati"
|
||||
|
||||
#: pretix/base/settings.py:947
|
||||
msgid ""
|
||||
@@ -10146,11 +10243,14 @@ msgid ""
|
||||
"'expired' after the end of their payment deadline. This means that those "
|
||||
"tickets go back to the pool and can be ordered by other people."
|
||||
msgstr ""
|
||||
"Se selezionata, tutti gli ordini non pagati passeranno automaticamente da "
|
||||
"\"in attesa\" a \"scaduti\", dopo aver raggiunto il termine di pagamento. "
|
||||
"Questo implica che i biglietti torneranno indietro nella quota e che "
|
||||
"potranno essere riordinati da altre persone."
|
||||
|
||||
#: pretix/base/settings.py:958
|
||||
#, fuzzy
|
||||
msgid "Expiration delay"
|
||||
msgstr "Carrello scaduto"
|
||||
msgstr "Ritardo nella scadenza"
|
||||
|
||||
#: pretix/base/settings.py:959
|
||||
msgid ""
|
||||
@@ -10160,6 +10260,12 @@ msgid ""
|
||||
"beyond the \"last date of payments\" configured above, which is always "
|
||||
"enforced."
|
||||
msgstr ""
|
||||
"L'ordine scadrà davvero solamente dopo i giorni specificati a partire dalla "
|
||||
"data di scadenza comunicata al cliente. Se selezioni "
|
||||
"\"Fai cadere il termine dei pagamenti solamente nei giorni feriali\", verrà "
|
||||
"rispettato anche in questo caso. Tieni in considerazione che il ritardo non "
|
||||
"scavallerà l'\"ultima data per i pagamenti\" impostata sopra, che verrà "
|
||||
"sempre rispettata."
|
||||
|
||||
#: pretix/base/settings.py:980
|
||||
msgid "Hide \"payment pending\" state on customer-facing pages"
|
||||
@@ -14910,7 +15016,7 @@ msgstr ""
|
||||
|
||||
#: pretix/control/forms/modelimport.py:76
|
||||
msgid "Import mode"
|
||||
msgstr "modo importazione"
|
||||
msgstr "Modo di importazione"
|
||||
|
||||
#: pretix/control/forms/modelimport.py:78
|
||||
msgid "Create a separate order for each line"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,16 +8,16 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-08-19 16:02+0000\n"
|
||||
"PO-Revision-Date: 2025-07-23 01:00+0000\n"
|
||||
"Last-Translator: Hijiri Umemoto <hijiri@umemoto.org>\n"
|
||||
"Language-Team: Japanese <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
"js/ja/>\n"
|
||||
"PO-Revision-Date: 2025-08-24 16:00+0000\n"
|
||||
"Last-Translator: Yasunobu YesNo Kawaguchi <kawaguti@gmail.com>\n"
|
||||
"Language-Team: Japanese <https://translate.pretix.eu/projects/pretix/"
|
||||
"pretix-js/ja/>\n"
|
||||
"Language: ja\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: Weblate 5.12.2\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
|
||||
@@ -209,7 +209,7 @@ msgstr "方向転換"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:38
|
||||
msgid "Entry"
|
||||
msgstr "入口"
|
||||
msgstr "エントリー"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:39
|
||||
msgid "Exit"
|
||||
@@ -644,7 +644,8 @@ msgstr ""
|
||||
msgid ""
|
||||
"Your color has insufficient contrast to white. Accessibility of your site "
|
||||
"will be impacted."
|
||||
msgstr ""
|
||||
msgstr "あなたの色は白に対して十分なコントラストがありません。サイトのアクセシビリテ"
|
||||
"ィに影響します。"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:417
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:437
|
||||
@@ -719,7 +720,7 @@ msgstr "カートの有効期限が切れています"
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:58
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:84
|
||||
msgid "Your cart is about to expire."
|
||||
msgstr ""
|
||||
msgstr "カートの有効期限が近づいています。"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:62
|
||||
msgid "The items in your cart are reserved for you for one minute."
|
||||
@@ -784,12 +785,12 @@ msgstr "数量を増やす"
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:19
|
||||
msgctxt "widget"
|
||||
msgid "Filter events by"
|
||||
msgstr ""
|
||||
msgstr "イベントをフィルタ"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:20
|
||||
msgctxt "widget"
|
||||
msgid "Filter"
|
||||
msgstr ""
|
||||
msgstr "フィルタ"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:21
|
||||
msgctxt "widget"
|
||||
@@ -974,7 +975,7 @@ msgstr ""
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:57
|
||||
msgctxt "widget"
|
||||
msgid "Resume checkout"
|
||||
msgstr "購入を続行する"
|
||||
msgstr "チェックアウトを続行する"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:58
|
||||
msgctxt "widget"
|
||||
@@ -997,17 +998,14 @@ msgid "Close"
|
||||
msgstr "閉じる"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "Resume checkout"
|
||||
msgctxt "widget"
|
||||
msgid "Close checkout"
|
||||
msgstr "購入を続行する"
|
||||
msgstr "チェックアウトを閉じる"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||
msgctxt "widget"
|
||||
msgid "You cannot cancel this operation. Please wait for loading to finish."
|
||||
msgstr ""
|
||||
msgstr "この操作はキャンセルできません。読み込みが完了するまでお待ちください。"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
||||
msgctxt "widget"
|
||||
|
||||
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-08-19 16:35+0000\n"
|
||||
"PO-Revision-Date: 2025-06-25 06:56+0000\n"
|
||||
"Last-Translator: 조정화 <junghwa.jo@om.org>\n"
|
||||
"PO-Revision-Date: 2025-09-01 18:00+0000\n"
|
||||
"Last-Translator: z3rrry <z3rrry@gmail.com>\n"
|
||||
"Language-Team: Korean <https://translate.pretix.eu/projects/pretix/pretix/ko/"
|
||||
">\n"
|
||||
"Language: ko\n"
|
||||
@@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: Weblate 5.11.4\n"
|
||||
"X-Generator: Weblate 5.13\n"
|
||||
|
||||
#: pretix/_base_settings.py:87
|
||||
msgid "English"
|
||||
@@ -145,7 +145,7 @@ msgstr "스페인어"
|
||||
|
||||
#: pretix/_base_settings.py:118
|
||||
msgid "Spanish (Latin America)"
|
||||
msgstr ""
|
||||
msgstr "스페인어 (라틴 아메리카)"
|
||||
|
||||
#: pretix/_base_settings.py:119
|
||||
msgid "Turkish"
|
||||
@@ -617,7 +617,8 @@ msgstr "바우처 할당"
|
||||
msgid ""
|
||||
"Only includes explicit changes to the voucher, not e.g. an increase of the "
|
||||
"number of redemptions."
|
||||
msgstr ""
|
||||
msgstr "쿠폰에 대한 명시적인 변경 사항만 포함하며, 예를 들어 사용 횟수 증가 등은 "
|
||||
"포함하지 않습니다."
|
||||
|
||||
#: pretix/api/webhooks.py:421
|
||||
#, fuzzy
|
||||
@@ -729,18 +730,16 @@ msgstr ""
|
||||
"있습니다."
|
||||
|
||||
#: pretix/base/context.py:38
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid "powered by {name} based on <a {a_attr}>pretix</a>"
|
||||
#, python-brace-format
|
||||
msgid "<a {a_name_attr}>powered by {name}</a> <a {a_attr}>based on pretix</a>"
|
||||
msgstr ""
|
||||
"이 서비스는 {name}에 의해 제공되며 pretix(온라인 이벤트 티켓팅 및 등록 시스"
|
||||
"템) 시스템 기반으로 작동합니다"
|
||||
"이 서비스는 <a {a_name_attr}>{name}</a>에 의해 제공되며 <a {a_attr}>pretix</"
|
||||
"a> 기반으로 작동합니다"
|
||||
|
||||
#: pretix/base/context.py:48
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid "powered by {name} based on <a {a_attr}>pretix</a>"
|
||||
#, python-brace-format
|
||||
msgid "<a {a_attr}>powered by {name} based on pretix</a>"
|
||||
msgstr "이 서비스는 {name}에서 제공하며 pretix를 기반으로 합니다."
|
||||
msgstr "이 서비스는 <a {a_attr}>{name}</a>에서 제공하며 pretix를 기반으로 합니다."
|
||||
|
||||
#: pretix/base/context.py:55
|
||||
#, python-format
|
||||
@@ -812,13 +811,16 @@ msgid ""
|
||||
"Field \"{field_name}\" is not valid for {available_inputs}. Please check "
|
||||
"your {provider_name} settings."
|
||||
msgstr ""
|
||||
"필드 \"{field_name}\"은 {available_inputs}에 유효하지 않습니다. "
|
||||
"{provider_name} 설정을 확인해 주세요."
|
||||
|
||||
#: pretix/base/datasync/datasync.py:267
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Please update value mapping for field \"{field_name}\" - option \"{val}\" "
|
||||
"not assigned"
|
||||
msgstr ""
|
||||
msgstr "필드 \"{field_name}\"의 값 매핑을 업데이트해 주세요 - 옵션 \"{val}\"이 "
|
||||
"할당되지 않았습니다"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:128
|
||||
#, fuzzy
|
||||
@@ -850,7 +852,7 @@ msgstr "상품 데이터"
|
||||
#: pretix/control/templates/pretixcontrol/order/index.html:176
|
||||
#: pretix/presale/templates/pretixpresale/event/order.html:22
|
||||
msgid "Order details"
|
||||
msgstr ""
|
||||
msgstr "주문 내역"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:133
|
||||
#: pretix/base/datasync/sourcefields.py:299
|
||||
|
||||
@@ -7,16 +7,16 @@ msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-08-19 16:35+0000\n"
|
||||
"PO-Revision-Date: 2025-07-06 01:00+0000\n"
|
||||
"PO-Revision-Date: 2025-08-26 19: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-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.11.4\n"
|
||||
"X-Generator: Weblate 5.13\n"
|
||||
|
||||
#: pretix/_base_settings.py:87
|
||||
msgid "English"
|
||||
@@ -144,7 +144,7 @@ msgstr "Spaans"
|
||||
|
||||
#: pretix/_base_settings.py:118
|
||||
msgid "Spanish (Latin America)"
|
||||
msgstr ""
|
||||
msgstr "Spaans (Latijns-Amerika)"
|
||||
|
||||
#: pretix/_base_settings.py:119
|
||||
msgid "Turkish"
|
||||
@@ -559,22 +559,16 @@ msgid "Event series date deleted"
|
||||
msgstr "Evenementenreeks: datum verwijderd"
|
||||
|
||||
#: pretix/api/webhooks.py:374
|
||||
#, fuzzy
|
||||
#| msgid "Product name"
|
||||
msgid "Product changed"
|
||||
msgstr "Productnaam"
|
||||
msgstr "Product is veranderd"
|
||||
|
||||
#: 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 ""
|
||||
"Product veranderd (inclusief product toegevoegd of verwijderd en inclusief "
|
||||
"veranderingen aan geneste objecten zoals variaties of bundels)"
|
||||
"Dit omvat toegevoegd of verwijderde product en wijzigingen in geneste "
|
||||
"objecten zoals variaties of bundels."
|
||||
|
||||
#: pretix/api/webhooks.py:380
|
||||
msgid "Shop taken live"
|
||||
@@ -609,28 +603,24 @@ msgid "Waiting list entry received voucher"
|
||||
msgstr "Wachtlijstitem heeft voucher ontvangen"
|
||||
|
||||
#: pretix/api/webhooks.py:412
|
||||
#, fuzzy
|
||||
#| msgid "Voucher code"
|
||||
msgid "Voucher added"
|
||||
msgstr "Vouchercode"
|
||||
msgstr "Voucher toegevoegd"
|
||||
|
||||
#: pretix/api/webhooks.py:416
|
||||
#, fuzzy
|
||||
#| msgid "Voucher assigned"
|
||||
msgid "Voucher changed"
|
||||
msgstr "Voucher toegewezen"
|
||||
msgstr "Voucher gewijzigd"
|
||||
|
||||
#: pretix/api/webhooks.py:417
|
||||
msgid ""
|
||||
"Only includes explicit changes to the voucher, not e.g. an increase of the "
|
||||
"number of redemptions."
|
||||
msgstr ""
|
||||
"Omvat alleen expliciete wijzigingen aan de voucher, niet bijvoorbeeld een "
|
||||
"toename van het aantal inwisselingen."
|
||||
|
||||
#: pretix/api/webhooks.py:421
|
||||
#, fuzzy
|
||||
#| msgid "Voucher redeemed"
|
||||
msgid "Voucher deleted"
|
||||
msgstr "Voucher verzilverd"
|
||||
msgstr "Voucher verwijderd"
|
||||
|
||||
#: pretix/api/webhooks.py:425
|
||||
msgid "Customer account created"
|
||||
@@ -874,17 +864,13 @@ msgid "Invoice address"
|
||||
msgstr "Factuuradres"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:134
|
||||
#, fuzzy
|
||||
#| msgid "Meta information"
|
||||
msgid "Event information"
|
||||
msgstr "Meta-informatie"
|
||||
msgstr "Event-informatie"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:135
|
||||
#, fuzzy
|
||||
#| msgid "Send recovery information"
|
||||
msgctxt "subevent"
|
||||
msgid "Event or date information"
|
||||
msgstr "Stuur herstelinformatie"
|
||||
msgstr "Event- of datuminformatie"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:175
|
||||
#: pretix/base/exporters/orderlist.py:604
|
||||
@@ -909,10 +895,8 @@ msgstr "Naam van aanwezige"
|
||||
#: 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 "Naam van aanwezige"
|
||||
msgstr "Aanwezige"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:207
|
||||
#: pretix/base/exporters/orderlist.py:611 pretix/base/forms/questions.py:685
|
||||
@@ -926,10 +910,8 @@ msgid "Attendee email"
|
||||
msgstr "E-mailadres van aanwezige"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:219
|
||||
#, fuzzy
|
||||
#| msgid "Attendee email"
|
||||
msgid "Attendee or order email"
|
||||
msgstr "E-mailadres van aanwezige"
|
||||
msgstr "E-mailadres van de aanwezige of de bestelling"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:232 pretix/base/pdf.py:186
|
||||
#: pretix/control/templates/pretixcontrol/order/index.html:595
|
||||
@@ -941,28 +923,20 @@ msgid "Attendee company"
|
||||
msgstr "Bedrijf aanwezige"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:241
|
||||
#, fuzzy
|
||||
#| msgid "Attendee address"
|
||||
msgid "Attendee address street"
|
||||
msgstr "Adres van gast"
|
||||
msgstr "Adres aanwezige: straat"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:250
|
||||
#, fuzzy
|
||||
#| msgid "Attendee ZIP code"
|
||||
msgid "Attendee address ZIP code"
|
||||
msgstr "Adres gast: postcode"
|
||||
msgstr "Adres aanwezige: postcode"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:259
|
||||
#, fuzzy
|
||||
#| msgid "Attendee address"
|
||||
msgid "Attendee address city"
|
||||
msgstr "Adres van gast"
|
||||
msgstr "Adres aanwezige: plaats"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:268
|
||||
#, fuzzy
|
||||
#| msgid "Attendee address"
|
||||
msgid "Attendee address country"
|
||||
msgstr "Adres van gast"
|
||||
msgstr "Adres aanwezige: land"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:279 pretix/base/pdf.py:344
|
||||
msgid "Invoice address company"
|
||||
@@ -997,16 +971,12 @@ msgid "Invoice address country"
|
||||
msgstr "Factuuradres: land"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:353
|
||||
#, fuzzy
|
||||
#| msgid "Order details"
|
||||
msgid "Order email"
|
||||
msgstr "Bestellingsdetails"
|
||||
msgstr "Emailadres bestelling"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:362
|
||||
#, fuzzy
|
||||
#| msgid "Organizer domain"
|
||||
msgid "Order email domain"
|
||||
msgstr "Domein van de organisator"
|
||||
msgstr "E-maildomein van de bestelling"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:371
|
||||
#: pretix/base/exporters/invoices.py:201 pretix/base/exporters/invoices.py:328
|
||||
@@ -1037,10 +1007,8 @@ msgid "Order code"
|
||||
msgstr "Bestelcode"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:380
|
||||
#, fuzzy
|
||||
#| msgid "End order date"
|
||||
msgid "Event and order code"
|
||||
msgstr "Einddatum bestelling"
|
||||
msgstr "Code van event en bestelling"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:389
|
||||
#: pretix/base/exporters/orderlist.py:262 pretix/base/notifications.py:201
|
||||
@@ -1052,10 +1020,8 @@ msgid "Order total"
|
||||
msgstr "Totaalbedrag van bestelling"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:398
|
||||
#, fuzzy
|
||||
#| msgid "Product name and variation"
|
||||
msgid "Product and variation name"
|
||||
msgstr "Productnaam en variant"
|
||||
msgstr "Product- en variantnaam"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:410 pretix/base/exporters/items.py:57
|
||||
#: pretix/base/exporters/orderlist.py:597
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-08-19 16:02+0000\n"
|
||||
"PO-Revision-Date: 2025-02-27 18:00+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.10.1\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
|
||||
@@ -143,7 +143,7 @@ msgstr "Continuar"
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:317
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:341
|
||||
msgid "Confirming your payment …"
|
||||
msgstr "Confirmado o seu pagamento …"
|
||||
msgstr "Confirmando seu pagamento …"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:254
|
||||
msgid "Payment method unavailable"
|
||||
@@ -165,7 +165,7 @@ msgstr "Receita total"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:15
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr "Contactando Stripe …"
|
||||
msgstr "Contatando Stripe …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:72
|
||||
msgid "Total"
|
||||
@@ -173,7 +173,7 @@ msgstr "Total"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:291
|
||||
msgid "Contacting your bank …"
|
||||
msgstr "Contactando o seu banco …"
|
||||
msgstr "Contatando o seu banco …"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:30
|
||||
msgid "Select a check-in list"
|
||||
@@ -217,7 +217,7 @@ msgstr "Saída"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:40
|
||||
msgid "Scan a ticket or search and press return…"
|
||||
msgstr "Escanear um ingresso ou pesquisar e pressionar enter…"
|
||||
msgstr "Escanear um ingresso ou pesquisar e pressionar Enter…"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:41
|
||||
msgid "Load more"
|
||||
@@ -370,9 +370,9 @@ msgid ""
|
||||
"If this takes longer than two minutes, please contact us or go back in your "
|
||||
"browser and try again."
|
||||
msgstr ""
|
||||
"Sua solicitação chegou ao nosso servidor mas ainda precisamos esperar ela "
|
||||
"ser processada. Se isto tomar mais do que dois minutos, por favor entre em "
|
||||
"contato conosco ou retorne no seu navegador e tente novamente."
|
||||
"Sua solicitação chegou ao servidor, mas ainda esperamos que ela seja "
|
||||
"processada. Se isso demorar mais de dois minutos, entre em contato conosco "
|
||||
"ou volte ao seu navegador e tente novamente."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:119
|
||||
#: pretix/static/pretixbase/js/asynctask.js:176
|
||||
@@ -386,8 +386,8 @@ msgid ""
|
||||
"We currently cannot reach the server, but we keep trying. Last error code: "
|
||||
"{code}"
|
||||
msgstr ""
|
||||
"Não conseguimos acessar o servidor, mas continuaremos tentando. Último "
|
||||
"código do erro: {code}"
|
||||
"No momento, não podemos acessar o servidor, mas continuamos tentando. Último "
|
||||
"código de erro: {code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:156
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:21
|
||||
@@ -399,8 +399,8 @@ msgstr "A solicitação demorou muito. Por favor, tente novamente."
|
||||
msgid ""
|
||||
"We currently cannot reach the server. Please try again. Error code: {code}"
|
||||
msgstr ""
|
||||
"Não conseguimos acessar o servidor. Por favor, tente novamente. Código do "
|
||||
"erro: {code}"
|
||||
"No momento, não podemos acessar o servidor. Por favor, tente novamente. "
|
||||
"Código de erro: {code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:210
|
||||
msgid "We are processing your request …"
|
||||
@@ -418,7 +418,7 @@ msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:270
|
||||
msgid "If this takes longer than a few minutes, please contact us."
|
||||
msgstr ""
|
||||
msgstr "Se isso demorar mais do que alguns minutos, entre em contato conosco."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:325
|
||||
msgid "Close message"
|
||||
@@ -630,26 +630,24 @@ msgid "Unknown error."
|
||||
msgstr "Erro desconhecido."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:292
|
||||
#, 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 "Sua cor tem grande contraste e é muito fácil de ler!"
|
||||
msgstr "Sua cor tem ótimo contraste e proporcionará excelente acessibilidade."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:296
|
||||
#, 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 ""
|
||||
"Sua cor tem um contraste aceitável e provavelmente é boa o suficiente para "
|
||||
"ler!"
|
||||
"Sua cor tem contraste decente e é suficiente para requisitos mínimos de "
|
||||
"acessibilidade."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:300
|
||||
msgid ""
|
||||
"Your color has insufficient contrast to white. Accessibility of your site "
|
||||
"will be impacted."
|
||||
msgstr ""
|
||||
"Sua cor não tem contraste suficiente com o branco. A acessibilidade do seu "
|
||||
"site será afetada."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:417
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:437
|
||||
@@ -693,10 +691,8 @@ msgid "Calculating default price…"
|
||||
msgstr "Calculando o preço padrão…"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/plugins.js:69
|
||||
#, fuzzy
|
||||
#| msgid "Search results"
|
||||
msgid "No results"
|
||||
msgstr "Resultados da busca"
|
||||
msgstr "Sem resultados"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/question.js:41
|
||||
msgid "Others"
|
||||
@@ -727,7 +723,7 @@ msgstr "Carrinho expirado"
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:58
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:84
|
||||
msgid "Your cart is about to expire."
|
||||
msgstr ""
|
||||
msgstr "Seu carrinho está para expirar."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:62
|
||||
msgid "The items in your cart are reserved for you for one minute."
|
||||
@@ -737,16 +733,10 @@ msgstr[1] ""
|
||||
"Os itens em seu carrinho estão reservados para você por {num} minutos."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:83
|
||||
#, fuzzy
|
||||
#| msgid "Cart expired"
|
||||
msgid "Your cart has expired."
|
||||
msgstr "Carrinho expirado"
|
||||
msgstr "Seu carrinho expirou."
|
||||
|
||||
#: 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 they’re 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."
|
||||
@@ -756,11 +746,11 @@ msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:87
|
||||
msgid "Do you want to renew the reservation period?"
|
||||
msgstr ""
|
||||
msgstr "Você quer renovar o seu período de reserva?"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:90
|
||||
msgid "Renew reservation"
|
||||
msgstr ""
|
||||
msgstr "Renovar reserva"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:194
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
@@ -800,12 +790,12 @@ msgstr "Aumentar quantidade"
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:19
|
||||
msgctxt "widget"
|
||||
msgid "Filter events by"
|
||||
msgstr ""
|
||||
msgstr "Filtrar eventos por"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:20
|
||||
msgctxt "widget"
|
||||
msgid "Filter"
|
||||
msgstr ""
|
||||
msgstr "Filtro"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:21
|
||||
msgctxt "widget"
|
||||
@@ -833,7 +823,7 @@ msgstr "Selecione"
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Select %s"
|
||||
msgstr "Selecion %s"
|
||||
msgstr "Selecione %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:26
|
||||
#, javascript-format
|
||||
@@ -955,12 +945,9 @@ msgid "Open ticket shop"
|
||||
msgstr "Abrir loja de ingressos"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:50
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "Resume checkout"
|
||||
msgctxt "widget"
|
||||
msgid "Checkout"
|
||||
msgstr "Retomar checkout"
|
||||
msgstr "Checkout"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:51
|
||||
msgctxt "widget"
|
||||
@@ -1017,17 +1004,14 @@ msgid "Close"
|
||||
msgstr "Fechar"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "Resume checkout"
|
||||
msgctxt "widget"
|
||||
msgid "Close checkout"
|
||||
msgstr "Retomar checkout"
|
||||
msgstr "Fechar checkout"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||
msgctxt "widget"
|
||||
msgid "You cannot cancel this operation. Please wait for loading to finish."
|
||||
msgstr ""
|
||||
msgstr "Você não pode cancelar esta operação. Aguarde o carregamento terminar."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
||||
msgctxt "widget"
|
||||
@@ -1130,31 +1114,31 @@ msgstr "Dom"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:85
|
||||
msgid "Monday"
|
||||
msgstr ""
|
||||
msgstr "Segunda-feira"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:86
|
||||
msgid "Tuesday"
|
||||
msgstr ""
|
||||
msgstr "Terça-feira"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:87
|
||||
msgid "Wednesday"
|
||||
msgstr ""
|
||||
msgstr "Quarta-feira"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:88
|
||||
msgid "Thursday"
|
||||
msgstr ""
|
||||
msgstr "Quinta-feira"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:89
|
||||
msgid "Friday"
|
||||
msgstr ""
|
||||
msgstr "Sexta-feira"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:90
|
||||
msgid "Saturday"
|
||||
msgstr ""
|
||||
msgstr "Sábado"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:91
|
||||
msgid "Sunday"
|
||||
msgstr ""
|
||||
msgstr "Domingo"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:94
|
||||
msgid "January"
|
||||
|
||||
@@ -54,7 +54,6 @@ from pretix.base.models import (
|
||||
)
|
||||
from pretix.base.payment import PaymentException
|
||||
from pretix.base.services.locking import LockTimeoutException
|
||||
from pretix.base.services.mail import SendMailException
|
||||
from pretix.base.services.orders import change_payment_provider
|
||||
from pretix.base.services.tasks import TransactionAwareTask
|
||||
from pretix.celery_app import app
|
||||
@@ -70,13 +69,10 @@ def notify_incomplete_payment(o: Order):
|
||||
email_context = get_email_context(event=o.event, order=o, pending_sum=o.pending_sum)
|
||||
email_subject = o.event.settings.mail_subject_order_incomplete_payment
|
||||
|
||||
try:
|
||||
o.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.expire_warning_sent'
|
||||
)
|
||||
except SendMailException:
|
||||
logger.exception('Reminder email could not be sent')
|
||||
o.send_mail(
|
||||
email_subject, email_template, email_context,
|
||||
'pretix.event.order.email.expire_warning_sent'
|
||||
)
|
||||
|
||||
|
||||
def cancel_old_payments(order):
|
||||
@@ -279,9 +275,6 @@ def _handle_transaction(trans: BankTransaction, matches: tuple, event: Event = N
|
||||
except Quota.QuotaExceededException:
|
||||
# payment confirmed but order status could not be set, no longer problem of this plugin
|
||||
cancel_old_payments(order)
|
||||
except SendMailException:
|
||||
# payment confirmed but order status could not be set, no longer problem of this plugin
|
||||
cancel_old_payments(order)
|
||||
else:
|
||||
cancel_old_payments(order)
|
||||
|
||||
|
||||
@@ -58,7 +58,6 @@ from localflavor.generic.forms import BICFormField, IBANFormField
|
||||
|
||||
from pretix.base.forms.widgets import DatePickerWidget
|
||||
from pretix.base.models import Event, Order, OrderPayment, OrderRefund, Quota
|
||||
from pretix.base.services.mail import SendMailException
|
||||
from pretix.base.settings import SettingsSandbox
|
||||
from pretix.base.templatetags.money import money_filter
|
||||
from pretix.control.permissions import (
|
||||
@@ -160,11 +159,6 @@ class ActionView(View):
|
||||
p.confirm(user=self.request.user)
|
||||
except Quota.QuotaExceededException:
|
||||
pass
|
||||
except SendMailException:
|
||||
return JsonResponse({
|
||||
'status': 'error',
|
||||
'message': _('Problem sending email.')
|
||||
})
|
||||
trans.state = BankTransaction.STATE_VALID
|
||||
trans.save()
|
||||
trans.order.payments.filter(
|
||||
|
||||
@@ -57,7 +57,6 @@ from pretix.base.decimal import round_decimal
|
||||
from pretix.base.forms import SecretKeySettingsField
|
||||
from pretix.base.models import Event, Order, OrderPayment, OrderRefund, Quota
|
||||
from pretix.base.payment import BasePaymentProvider, PaymentException
|
||||
from pretix.base.services.mail import SendMailException
|
||||
from pretix.base.settings import SettingsSandbox
|
||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||
from pretix.plugins.paypal.api import Api
|
||||
@@ -468,9 +467,6 @@ class Paypal(BasePaymentProvider):
|
||||
payment_obj.confirm()
|
||||
except Quota.QuotaExceededException as e:
|
||||
raise PaymentException(str(e))
|
||||
|
||||
except SendMailException:
|
||||
messages.warning(request, _('There was an error sending the confirmation mail.'))
|
||||
return None
|
||||
|
||||
def payment_pending_render(self, request, payment) -> str:
|
||||
|
||||
@@ -54,7 +54,6 @@ from pretix.base.forms import SecretKeySettingsField
|
||||
from pretix.base.forms.questions import guess_country
|
||||
from pretix.base.models import Event, Order, OrderPayment, OrderRefund, Quota
|
||||
from pretix.base.payment import BasePaymentProvider, PaymentException
|
||||
from pretix.base.services.mail import SendMailException
|
||||
from pretix.base.settings import SettingsSandbox
|
||||
from pretix.helpers import OF_SELF
|
||||
from pretix.helpers.urls import build_absolute_uri as build_global_uri
|
||||
@@ -565,7 +564,7 @@ class PaypalMethod(BasePaymentProvider):
|
||||
)
|
||||
request.session['payment_paypal_payment'] = None
|
||||
else:
|
||||
pass
|
||||
return None
|
||||
|
||||
try:
|
||||
paymentreq = OrdersCreateRequest()
|
||||
@@ -821,9 +820,6 @@ class PaypalMethod(BasePaymentProvider):
|
||||
payment.confirm()
|
||||
except Quota.QuotaExceededException as e:
|
||||
raise PaymentException(str(e))
|
||||
|
||||
except SendMailException:
|
||||
messages.warning(request, _('There was an error sending the confirmation mail.'))
|
||||
finally:
|
||||
if 'payment_paypal_oid' in request.session:
|
||||
del request.session['payment_paypal_oid']
|
||||
|
||||
@@ -38,7 +38,6 @@ from pretix.base.models import (
|
||||
fields,
|
||||
)
|
||||
from pretix.base.models.base import LoggingMixin
|
||||
from pretix.base.services.mail import SendMailException
|
||||
|
||||
|
||||
class ScheduledMail(models.Model):
|
||||
@@ -180,13 +179,10 @@ class ScheduledMail(models.Model):
|
||||
invoice_address=ia,
|
||||
event_or_subevent=self.subevent or e,
|
||||
)
|
||||
try:
|
||||
o.send_mail(self.rule.subject, self.rule.template, email_ctx,
|
||||
attach_ical=self.rule.attach_ical,
|
||||
log_entry_type='pretix.plugins.sendmail.rule.order.email.sent')
|
||||
o_sent = True
|
||||
except SendMailException:
|
||||
... # ¯\_(ツ)_/¯
|
||||
o.send_mail(self.rule.subject, self.rule.template, email_ctx,
|
||||
attach_ical=self.rule.attach_ical,
|
||||
log_entry_type='pretix.plugins.sendmail.rule.order.email.sent')
|
||||
o_sent = True
|
||||
|
||||
if send_to_attendees:
|
||||
if not self.rule.all_products:
|
||||
@@ -195,31 +191,28 @@ class ScheduledMail(models.Model):
|
||||
positions = [p for p in positions if p.subevent_id == self.subevent_id]
|
||||
|
||||
for p in positions:
|
||||
try:
|
||||
if p.attendee_email and (p.attendee_email != o.email or not o_sent):
|
||||
email_ctx = get_email_context(
|
||||
event=e,
|
||||
order=o,
|
||||
invoice_address=ia,
|
||||
position=p,
|
||||
event_or_subevent=self.subevent or e,
|
||||
)
|
||||
p.send_mail(self.rule.subject, self.rule.template, email_ctx,
|
||||
attach_ical=self.rule.attach_ical,
|
||||
log_entry_type='pretix.plugins.sendmail.rule.order.position.email.sent')
|
||||
elif not o_sent and o.email:
|
||||
email_ctx = get_email_context(
|
||||
event=e,
|
||||
order=o,
|
||||
invoice_address=ia,
|
||||
event_or_subevent=self.subevent or e,
|
||||
)
|
||||
o.send_mail(self.rule.subject, self.rule.template, email_ctx,
|
||||
attach_ical=self.rule.attach_ical,
|
||||
log_entry_type='pretix.plugins.sendmail.rule.order.email.sent')
|
||||
o_sent = True
|
||||
except SendMailException:
|
||||
... # ¯\_(ツ)_/¯
|
||||
if p.attendee_email and (p.attendee_email != o.email or not o_sent):
|
||||
email_ctx = get_email_context(
|
||||
event=e,
|
||||
order=o,
|
||||
invoice_address=ia,
|
||||
position=p,
|
||||
event_or_subevent=self.subevent or e,
|
||||
)
|
||||
p.send_mail(self.rule.subject, self.rule.template, email_ctx,
|
||||
attach_ical=self.rule.attach_ical,
|
||||
log_entry_type='pretix.plugins.sendmail.rule.order.position.email.sent')
|
||||
elif not o_sent and o.email:
|
||||
email_ctx = get_email_context(
|
||||
event=e,
|
||||
order=o,
|
||||
invoice_address=ia,
|
||||
event_or_subevent=self.subevent or e,
|
||||
)
|
||||
o.send_mail(self.rule.subject, self.rule.template, email_ctx,
|
||||
attach_ical=self.rule.attach_ical,
|
||||
log_entry_type='pretix.plugins.sendmail.rule.order.email.sent')
|
||||
o_sent = True
|
||||
|
||||
self.last_successful_order_id = o.pk
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ from pretix.base.i18n import language
|
||||
from pretix.base.models import (
|
||||
CachedFile, Checkin, Event, InvoiceAddress, Order, User,
|
||||
)
|
||||
from pretix.base.services.mail import SendMailException, mail
|
||||
from pretix.base.services.mail import mail
|
||||
from pretix.base.services.tasks import ProfiledEventTask
|
||||
from pretix.celery_app import app
|
||||
from pretix.helpers.format import format_map
|
||||
@@ -53,7 +53,6 @@ def send_mails_to_orders(event: Event, user: int, subject: dict, message: dict,
|
||||
recipients: str, filter_checkins: bool, not_checked_in: bool, checkin_lists: list,
|
||||
attachments: list = None, attach_tickets: bool = False,
|
||||
attach_ical: bool = False) -> None:
|
||||
failures = []
|
||||
user = User.objects.get(pk=user) if user else None
|
||||
orders = Order.objects.filter(pk__in=objects, event=event)
|
||||
subject = LazyI18nString(subject)
|
||||
@@ -114,70 +113,64 @@ def send_mails_to_orders(event: Event, user: int, subject: dict, message: dict,
|
||||
if subevents_to and p.subevent.date_from >= subevents_to:
|
||||
continue
|
||||
|
||||
try:
|
||||
with language(o.locale, event.settings.region):
|
||||
email_context = get_email_context(event=event, order=o, invoice_address=ia, position=p)
|
||||
mail(
|
||||
p.attendee_email,
|
||||
subject,
|
||||
message,
|
||||
email_context,
|
||||
event,
|
||||
locale=o.locale,
|
||||
order=o,
|
||||
position=p,
|
||||
attach_tickets=attach_tickets,
|
||||
attach_ical=attach_ical,
|
||||
attach_cached_files=attachments
|
||||
)
|
||||
o.log_action(
|
||||
'pretix.plugins.sendmail.order.email.sent.attendee',
|
||||
user=user,
|
||||
data={
|
||||
'position': p.positionid,
|
||||
'subject': format_map(subject.localize(o.locale), email_context),
|
||||
'message': format_map(message.localize(o.locale), email_context),
|
||||
'recipient': p.attendee_email,
|
||||
'attach_tickets': attach_tickets,
|
||||
'attach_ical': attach_ical,
|
||||
'attach_other_files': [],
|
||||
'attach_cached_files': attachments_for_log,
|
||||
}
|
||||
)
|
||||
except SendMailException:
|
||||
failures.append(p.attendee_email)
|
||||
|
||||
if send_to_order and o.email:
|
||||
try:
|
||||
with language(o.locale, event.settings.region):
|
||||
email_context = get_email_context(event=event, order=o, invoice_address=ia)
|
||||
email_context = get_email_context(event=event, order=o, invoice_address=ia, position=p)
|
||||
mail(
|
||||
o.email,
|
||||
p.attendee_email,
|
||||
subject,
|
||||
message,
|
||||
email_context,
|
||||
event,
|
||||
locale=o.locale,
|
||||
order=o,
|
||||
position=p,
|
||||
attach_tickets=attach_tickets,
|
||||
attach_ical=attach_ical,
|
||||
attach_cached_files=attachments,
|
||||
attach_cached_files=attachments
|
||||
)
|
||||
o.log_action(
|
||||
'pretix.plugins.sendmail.order.email.sent',
|
||||
'pretix.plugins.sendmail.order.email.sent.attendee',
|
||||
user=user,
|
||||
data={
|
||||
'position': p.positionid,
|
||||
'subject': format_map(subject.localize(o.locale), email_context),
|
||||
'message': format_map(message.localize(o.locale), email_context),
|
||||
'recipient': o.email,
|
||||
'recipient': p.attendee_email,
|
||||
'attach_tickets': attach_tickets,
|
||||
'attach_ical': attach_ical,
|
||||
'attach_other_files': [],
|
||||
'attach_cached_files': attachments_for_log,
|
||||
}
|
||||
)
|
||||
except SendMailException:
|
||||
failures.append(o.email)
|
||||
|
||||
if send_to_order and o.email:
|
||||
with language(o.locale, event.settings.region):
|
||||
email_context = get_email_context(event=event, order=o, invoice_address=ia)
|
||||
mail(
|
||||
o.email,
|
||||
subject,
|
||||
message,
|
||||
email_context,
|
||||
event,
|
||||
locale=o.locale,
|
||||
order=o,
|
||||
attach_tickets=attach_tickets,
|
||||
attach_ical=attach_ical,
|
||||
attach_cached_files=attachments,
|
||||
)
|
||||
o.log_action(
|
||||
'pretix.plugins.sendmail.order.email.sent',
|
||||
user=user,
|
||||
data={
|
||||
'subject': format_map(subject.localize(o.locale), email_context),
|
||||
'message': format_map(message.localize(o.locale), email_context),
|
||||
'recipient': o.email,
|
||||
'attach_tickets': attach_tickets,
|
||||
'attach_ical': attach_ical,
|
||||
'attach_other_files': [],
|
||||
'attach_cached_files': attachments_for_log,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@app.task(base=ProfiledEventTask, acks_late=True)
|
||||
|
||||
@@ -71,7 +71,6 @@ from pretix.base.payment import (
|
||||
BasePaymentProvider, PaymentException, WalletQueries,
|
||||
)
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
from pretix.base.services.mail import SendMailException
|
||||
from pretix.base.settings import SettingsSandbox
|
||||
from pretix.helpers import OF_SELF
|
||||
from pretix.helpers.countries import CachedCountries
|
||||
@@ -980,9 +979,6 @@ class StripeMethod(BasePaymentProvider):
|
||||
payment.confirm()
|
||||
except Quota.QuotaExceededException as e:
|
||||
raise PaymentException(str(e))
|
||||
|
||||
except SendMailException:
|
||||
raise PaymentException(_('There was an error sending the confirmation mail.'))
|
||||
elif intent.status == 'processing':
|
||||
if request:
|
||||
messages.warning(request, _('Your payment is pending completion. We will inform you as soon as the '
|
||||
|
||||
@@ -1643,11 +1643,6 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
|
||||
order = Order.objects.get(id=value)
|
||||
return self.get_order_url(order)
|
||||
|
||||
def get_error_message(self, exception):
|
||||
if exception.__class__.__name__ == 'SendMailException':
|
||||
return _('There was an error sending the confirmation mail. Please try again later.')
|
||||
return super().get_error_message(exception)
|
||||
|
||||
def get_error_url(self):
|
||||
return self.get_step_url(self.request)
|
||||
|
||||
|
||||
@@ -75,7 +75,6 @@ from pretix.base.services.invoices import (
|
||||
generate_cancellation, generate_invoice, invoice_pdf, invoice_pdf_task,
|
||||
invoice_qualified,
|
||||
)
|
||||
from pretix.base.services.mail import SendMailException
|
||||
from pretix.base.services.orders import (
|
||||
OrderChangeManager, OrderError, _try_auto_refund, cancel_order,
|
||||
change_payment_provider, error_messages,
|
||||
@@ -595,10 +594,7 @@ class OrderPayChangeMethod(EventViewMixin, OrderDetailMixin, TemplateView):
|
||||
amount=Decimal('0.00'),
|
||||
fee=None
|
||||
)
|
||||
try:
|
||||
p.confirm()
|
||||
except SendMailException:
|
||||
pass
|
||||
p.confirm()
|
||||
else:
|
||||
p._mark_order_paid(
|
||||
payment_refund_sum=self.order.payment_refund_sum
|
||||
|
||||
@@ -976,7 +976,7 @@ class DayCalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
|
||||
return ctx
|
||||
|
||||
events = ebd[self.date]
|
||||
shortest_duration = self._get_shortest_duration(events).total_seconds() // 60
|
||||
shortest_duration = max(self._get_shortest_duration(events).total_seconds() // 60, 1)
|
||||
# pick the next biggest tick_duration based on shortest_duration, max. 180 minutes
|
||||
tick_duration = next((d for d in [5, 10, 15, 30, 60, 120, 180] if d >= shortest_duration), 180)
|
||||
|
||||
|
||||
@@ -32,8 +32,6 @@
|
||||
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations under the License.
|
||||
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.utils.functional import cached_property
|
||||
@@ -42,7 +40,7 @@ from django.views import View
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from pretix.base.email import get_email_context
|
||||
from pretix.base.services.mail import INVALID_ADDRESS, SendMailException, mail
|
||||
from pretix.base.services.mail import INVALID_ADDRESS, mail
|
||||
from pretix.helpers.http import redirect_to_url
|
||||
from pretix.multidomain.urlreverse import eventreverse
|
||||
from pretix.presale.forms.user import ResendLinkForm
|
||||
@@ -83,13 +81,7 @@ class ResendLinkView(EventViewMixin, TemplateView):
|
||||
subject = self.request.event.settings.mail_subject_resend_all_links
|
||||
template = self.request.event.settings.mail_text_resend_all_links
|
||||
context = get_email_context(event=self.request.event, orders=orders)
|
||||
try:
|
||||
mail(user, subject, template, context, event=self.request.event, locale=self.request.LANGUAGE_CODE)
|
||||
except SendMailException:
|
||||
logger = logging.getLogger('pretix.presale.user')
|
||||
logger.exception('A mail resending order links to {} could not be sent.'.format(user))
|
||||
messages.error(self.request, _('We have trouble sending emails right now, please check back later.'))
|
||||
return self.get(request, *args, **kwargs)
|
||||
mail(user, subject, template, context, event=self.request.event, locale=self.request.LANGUAGE_CODE)
|
||||
|
||||
messages.success(self.request, _('If there were any orders by this user, they will receive an email with their order codes.'))
|
||||
return redirect_to_url(eventreverse(self.request.event, 'presale:event.index'))
|
||||
|
||||
244
src/pretix/static/npm_dir/package-lock.json
generated
244
src/pretix/static/npm_dir/package-lock.json
generated
@@ -8,8 +8,8 @@
|
||||
"name": "pretix",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.28.0",
|
||||
"@babel/preset-env": "^7.28.0",
|
||||
"@babel/core": "^7.28.3",
|
||||
"@babel/preset-env": "^7.28.3",
|
||||
"@rollup/plugin-babel": "^6.0.4",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"rollup": "^2.79.1",
|
||||
@@ -53,20 +53,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/core": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz",
|
||||
"integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz",
|
||||
"integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==",
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.0",
|
||||
"@babel/generator": "^7.28.3",
|
||||
"@babel/helper-compilation-targets": "^7.27.2",
|
||||
"@babel/helper-module-transforms": "^7.27.3",
|
||||
"@babel/helpers": "^7.27.6",
|
||||
"@babel/parser": "^7.28.0",
|
||||
"@babel/helper-module-transforms": "^7.28.3",
|
||||
"@babel/helpers": "^7.28.3",
|
||||
"@babel/parser": "^7.28.3",
|
||||
"@babel/template": "^7.27.2",
|
||||
"@babel/traverse": "^7.28.0",
|
||||
"@babel/types": "^7.28.0",
|
||||
"@babel/traverse": "^7.28.3",
|
||||
"@babel/types": "^7.28.2",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"debug": "^4.1.0",
|
||||
"gensync": "^1.0.0-beta.2",
|
||||
@@ -101,12 +101,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/generator": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz",
|
||||
"integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
|
||||
"integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.28.0",
|
||||
"@babel/types": "^7.28.0",
|
||||
"@babel/parser": "^7.28.3",
|
||||
"@babel/types": "^7.28.2",
|
||||
"@jridgewell/gen-mapping": "^0.3.12",
|
||||
"@jridgewell/trace-mapping": "^0.3.28",
|
||||
"jsesc": "^3.0.2"
|
||||
@@ -173,17 +173,16 @@
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
||||
},
|
||||
"node_modules/@babel/helper-create-class-features-plugin": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz",
|
||||
"integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==",
|
||||
"license": "MIT",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz",
|
||||
"integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==",
|
||||
"dependencies": {
|
||||
"@babel/helper-annotate-as-pure": "^7.27.1",
|
||||
"@babel/helper-annotate-as-pure": "^7.27.3",
|
||||
"@babel/helper-member-expression-to-functions": "^7.27.1",
|
||||
"@babel/helper-optimise-call-expression": "^7.27.1",
|
||||
"@babel/helper-replace-supers": "^7.27.1",
|
||||
"@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
|
||||
"@babel/traverse": "^7.27.1",
|
||||
"@babel/traverse": "^7.28.3",
|
||||
"semver": "^6.3.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -277,14 +276,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-module-transforms": {
|
||||
"version": "7.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz",
|
||||
"integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==",
|
||||
"license": "MIT",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
|
||||
"integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-imports": "^7.27.1",
|
||||
"@babel/helper-validator-identifier": "^7.27.1",
|
||||
"@babel/traverse": "^7.27.3"
|
||||
"@babel/traverse": "^7.28.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -403,23 +401,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helpers": {
|
||||
"version": "7.27.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz",
|
||||
"integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz",
|
||||
"integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==",
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.27.2",
|
||||
"@babel/types": "^7.27.6"
|
||||
"@babel/types": "^7.28.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
|
||||
"integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz",
|
||||
"integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.28.0"
|
||||
"@babel/types": "^7.28.2"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@@ -492,13 +490,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.27.1.tgz",
|
||||
"integrity": "sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==",
|
||||
"license": "MIT",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz",
|
||||
"integrity": "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.27.1",
|
||||
"@babel/traverse": "^7.27.1"
|
||||
"@babel/traverse": "^7.28.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -657,12 +654,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-class-static-block": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.27.1.tgz",
|
||||
"integrity": "sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==",
|
||||
"license": "MIT",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz",
|
||||
"integrity": "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==",
|
||||
"dependencies": {
|
||||
"@babel/helper-create-class-features-plugin": "^7.27.1",
|
||||
"@babel/helper-create-class-features-plugin": "^7.28.3",
|
||||
"@babel/helper-plugin-utils": "^7.27.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -673,16 +669,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-classes": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz",
|
||||
"integrity": "sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.3.tgz",
|
||||
"integrity": "sha512-DoEWC5SuxuARF2KdKmGUq3ghfPMO6ZzR12Dnp5gubwbeWJo4dbNWXJPVlwvh4Zlq6Z7YVvL8VFxeSOJgjsx4Sg==",
|
||||
"dependencies": {
|
||||
"@babel/helper-annotate-as-pure": "^7.27.3",
|
||||
"@babel/helper-compilation-targets": "^7.27.2",
|
||||
"@babel/helper-globals": "^7.28.0",
|
||||
"@babel/helper-plugin-utils": "^7.27.1",
|
||||
"@babel/helper-replace-supers": "^7.27.1",
|
||||
"@babel/traverse": "^7.28.0"
|
||||
"@babel/traverse": "^7.28.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -1177,9 +1173,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-regenerator": {
|
||||
"version": "7.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.1.tgz",
|
||||
"integrity": "sha512-P0QiV/taaa3kXpLY+sXla5zec4E+4t4Aqc9ggHlfZ7a2cp8/x/Gv08jfwEtn9gnnYIMvHx6aoOZ8XJL8eU71Dg==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.3.tgz",
|
||||
"integrity": "sha512-K3/M/a4+ESb5LEldjQb+XSrpY0nF+ZBFlTCbSnKaYAMfD8v33O6PMs4uYnOk19HlcsI8WMu3McdFPTiQHF/1/A==",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.27.1"
|
||||
},
|
||||
@@ -1361,9 +1357,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/preset-env": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.0.tgz",
|
||||
"integrity": "sha512-VmaxeGOwuDqzLl5JUkIRM1X2Qu2uKGxHEQWh+cvvbl7JuJRgKGJSfsEF/bUaxFhJl/XAyxBe7q7qSuTbKFuCyg==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.3.tgz",
|
||||
"integrity": "sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg==",
|
||||
"dependencies": {
|
||||
"@babel/compat-data": "^7.28.0",
|
||||
"@babel/helper-compilation-targets": "^7.27.2",
|
||||
@@ -1373,7 +1369,7 @@
|
||||
"@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1",
|
||||
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1",
|
||||
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1",
|
||||
"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.27.1",
|
||||
"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3",
|
||||
"@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
|
||||
"@babel/plugin-syntax-import-assertions": "^7.27.1",
|
||||
"@babel/plugin-syntax-import-attributes": "^7.27.1",
|
||||
@@ -1384,8 +1380,8 @@
|
||||
"@babel/plugin-transform-block-scoped-functions": "^7.27.1",
|
||||
"@babel/plugin-transform-block-scoping": "^7.28.0",
|
||||
"@babel/plugin-transform-class-properties": "^7.27.1",
|
||||
"@babel/plugin-transform-class-static-block": "^7.27.1",
|
||||
"@babel/plugin-transform-classes": "^7.28.0",
|
||||
"@babel/plugin-transform-class-static-block": "^7.28.3",
|
||||
"@babel/plugin-transform-classes": "^7.28.3",
|
||||
"@babel/plugin-transform-computed-properties": "^7.27.1",
|
||||
"@babel/plugin-transform-destructuring": "^7.28.0",
|
||||
"@babel/plugin-transform-dotall-regex": "^7.27.1",
|
||||
@@ -1417,7 +1413,7 @@
|
||||
"@babel/plugin-transform-private-methods": "^7.27.1",
|
||||
"@babel/plugin-transform-private-property-in-object": "^7.27.1",
|
||||
"@babel/plugin-transform-property-literals": "^7.27.1",
|
||||
"@babel/plugin-transform-regenerator": "^7.28.0",
|
||||
"@babel/plugin-transform-regenerator": "^7.28.3",
|
||||
"@babel/plugin-transform-regexp-modifiers": "^7.27.1",
|
||||
"@babel/plugin-transform-reserved-words": "^7.27.1",
|
||||
"@babel/plugin-transform-shorthand-properties": "^7.27.1",
|
||||
@@ -1479,16 +1475,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz",
|
||||
"integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz",
|
||||
"integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.0",
|
||||
"@babel/generator": "^7.28.3",
|
||||
"@babel/helper-globals": "^7.28.0",
|
||||
"@babel/parser": "^7.28.0",
|
||||
"@babel/parser": "^7.28.3",
|
||||
"@babel/template": "^7.27.2",
|
||||
"@babel/types": "^7.28.0",
|
||||
"@babel/types": "^7.28.2",
|
||||
"debug": "^4.3.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3831,20 +3827,20 @@
|
||||
"integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw=="
|
||||
},
|
||||
"@babel/core": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz",
|
||||
"integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz",
|
||||
"integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==",
|
||||
"requires": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.0",
|
||||
"@babel/generator": "^7.28.3",
|
||||
"@babel/helper-compilation-targets": "^7.27.2",
|
||||
"@babel/helper-module-transforms": "^7.27.3",
|
||||
"@babel/helpers": "^7.27.6",
|
||||
"@babel/parser": "^7.28.0",
|
||||
"@babel/helper-module-transforms": "^7.28.3",
|
||||
"@babel/helpers": "^7.28.3",
|
||||
"@babel/parser": "^7.28.3",
|
||||
"@babel/template": "^7.27.2",
|
||||
"@babel/traverse": "^7.28.0",
|
||||
"@babel/types": "^7.28.0",
|
||||
"@babel/traverse": "^7.28.3",
|
||||
"@babel/types": "^7.28.2",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"debug": "^4.1.0",
|
||||
"gensync": "^1.0.0-beta.2",
|
||||
@@ -3865,12 +3861,12 @@
|
||||
}
|
||||
},
|
||||
"@babel/generator": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz",
|
||||
"integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
|
||||
"integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
|
||||
"requires": {
|
||||
"@babel/parser": "^7.28.0",
|
||||
"@babel/types": "^7.28.0",
|
||||
"@babel/parser": "^7.28.3",
|
||||
"@babel/types": "^7.28.2",
|
||||
"@jridgewell/gen-mapping": "^0.3.12",
|
||||
"@jridgewell/trace-mapping": "^0.3.28",
|
||||
"jsesc": "^3.0.2"
|
||||
@@ -3928,16 +3924,16 @@
|
||||
}
|
||||
},
|
||||
"@babel/helper-create-class-features-plugin": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz",
|
||||
"integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz",
|
||||
"integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==",
|
||||
"requires": {
|
||||
"@babel/helper-annotate-as-pure": "^7.27.1",
|
||||
"@babel/helper-annotate-as-pure": "^7.27.3",
|
||||
"@babel/helper-member-expression-to-functions": "^7.27.1",
|
||||
"@babel/helper-optimise-call-expression": "^7.27.1",
|
||||
"@babel/helper-replace-supers": "^7.27.1",
|
||||
"@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
|
||||
"@babel/traverse": "^7.27.1",
|
||||
"@babel/traverse": "^7.28.3",
|
||||
"semver": "^6.3.1"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -4001,13 +3997,13 @@
|
||||
}
|
||||
},
|
||||
"@babel/helper-module-transforms": {
|
||||
"version": "7.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz",
|
||||
"integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
|
||||
"integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
|
||||
"requires": {
|
||||
"@babel/helper-module-imports": "^7.27.1",
|
||||
"@babel/helper-validator-identifier": "^7.27.1",
|
||||
"@babel/traverse": "^7.27.3"
|
||||
"@babel/traverse": "^7.28.3"
|
||||
}
|
||||
},
|
||||
"@babel/helper-optimise-call-expression": {
|
||||
@@ -4078,20 +4074,20 @@
|
||||
}
|
||||
},
|
||||
"@babel/helpers": {
|
||||
"version": "7.27.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz",
|
||||
"integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz",
|
||||
"integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==",
|
||||
"requires": {
|
||||
"@babel/template": "^7.27.2",
|
||||
"@babel/types": "^7.27.6"
|
||||
"@babel/types": "^7.28.2"
|
||||
}
|
||||
},
|
||||
"@babel/parser": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
|
||||
"integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz",
|
||||
"integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==",
|
||||
"requires": {
|
||||
"@babel/types": "^7.28.0"
|
||||
"@babel/types": "^7.28.2"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
|
||||
@@ -4130,12 +4126,12 @@
|
||||
}
|
||||
},
|
||||
"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.27.1.tgz",
|
||||
"integrity": "sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz",
|
||||
"integrity": "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==",
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.27.1",
|
||||
"@babel/traverse": "^7.27.1"
|
||||
"@babel/traverse": "^7.28.3"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-proposal-private-property-in-object": {
|
||||
@@ -4223,25 +4219,25 @@
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-class-static-block": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.27.1.tgz",
|
||||
"integrity": "sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz",
|
||||
"integrity": "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==",
|
||||
"requires": {
|
||||
"@babel/helper-create-class-features-plugin": "^7.27.1",
|
||||
"@babel/helper-create-class-features-plugin": "^7.28.3",
|
||||
"@babel/helper-plugin-utils": "^7.27.1"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-classes": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz",
|
||||
"integrity": "sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.3.tgz",
|
||||
"integrity": "sha512-DoEWC5SuxuARF2KdKmGUq3ghfPMO6ZzR12Dnp5gubwbeWJo4dbNWXJPVlwvh4Zlq6Z7YVvL8VFxeSOJgjsx4Sg==",
|
||||
"requires": {
|
||||
"@babel/helper-annotate-as-pure": "^7.27.3",
|
||||
"@babel/helper-compilation-targets": "^7.27.2",
|
||||
"@babel/helper-globals": "^7.28.0",
|
||||
"@babel/helper-plugin-utils": "^7.27.1",
|
||||
"@babel/helper-replace-supers": "^7.27.1",
|
||||
"@babel/traverse": "^7.28.0"
|
||||
"@babel/traverse": "^7.28.3"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-computed-properties": {
|
||||
@@ -4517,9 +4513,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-regenerator": {
|
||||
"version": "7.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.1.tgz",
|
||||
"integrity": "sha512-P0QiV/taaa3kXpLY+sXla5zec4E+4t4Aqc9ggHlfZ7a2cp8/x/Gv08jfwEtn9gnnYIMvHx6aoOZ8XJL8eU71Dg==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.3.tgz",
|
||||
"integrity": "sha512-K3/M/a4+ESb5LEldjQb+XSrpY0nF+ZBFlTCbSnKaYAMfD8v33O6PMs4uYnOk19HlcsI8WMu3McdFPTiQHF/1/A==",
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.27.1"
|
||||
}
|
||||
@@ -4618,9 +4614,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/preset-env": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.0.tgz",
|
||||
"integrity": "sha512-VmaxeGOwuDqzLl5JUkIRM1X2Qu2uKGxHEQWh+cvvbl7JuJRgKGJSfsEF/bUaxFhJl/XAyxBe7q7qSuTbKFuCyg==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.3.tgz",
|
||||
"integrity": "sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg==",
|
||||
"requires": {
|
||||
"@babel/compat-data": "^7.28.0",
|
||||
"@babel/helper-compilation-targets": "^7.27.2",
|
||||
@@ -4630,7 +4626,7 @@
|
||||
"@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1",
|
||||
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1",
|
||||
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1",
|
||||
"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.27.1",
|
||||
"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3",
|
||||
"@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
|
||||
"@babel/plugin-syntax-import-assertions": "^7.27.1",
|
||||
"@babel/plugin-syntax-import-attributes": "^7.27.1",
|
||||
@@ -4641,8 +4637,8 @@
|
||||
"@babel/plugin-transform-block-scoped-functions": "^7.27.1",
|
||||
"@babel/plugin-transform-block-scoping": "^7.28.0",
|
||||
"@babel/plugin-transform-class-properties": "^7.27.1",
|
||||
"@babel/plugin-transform-class-static-block": "^7.27.1",
|
||||
"@babel/plugin-transform-classes": "^7.28.0",
|
||||
"@babel/plugin-transform-class-static-block": "^7.28.3",
|
||||
"@babel/plugin-transform-classes": "^7.28.3",
|
||||
"@babel/plugin-transform-computed-properties": "^7.27.1",
|
||||
"@babel/plugin-transform-destructuring": "^7.28.0",
|
||||
"@babel/plugin-transform-dotall-regex": "^7.27.1",
|
||||
@@ -4674,7 +4670,7 @@
|
||||
"@babel/plugin-transform-private-methods": "^7.27.1",
|
||||
"@babel/plugin-transform-private-property-in-object": "^7.27.1",
|
||||
"@babel/plugin-transform-property-literals": "^7.27.1",
|
||||
"@babel/plugin-transform-regenerator": "^7.28.0",
|
||||
"@babel/plugin-transform-regenerator": "^7.28.3",
|
||||
"@babel/plugin-transform-regexp-modifiers": "^7.27.1",
|
||||
"@babel/plugin-transform-reserved-words": "^7.27.1",
|
||||
"@babel/plugin-transform-shorthand-properties": "^7.27.1",
|
||||
@@ -4722,16 +4718,16 @@
|
||||
}
|
||||
},
|
||||
"@babel/traverse": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz",
|
||||
"integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz",
|
||||
"integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==",
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.0",
|
||||
"@babel/generator": "^7.28.3",
|
||||
"@babel/helper-globals": "^7.28.0",
|
||||
"@babel/parser": "^7.28.0",
|
||||
"@babel/parser": "^7.28.3",
|
||||
"@babel/template": "^7.27.2",
|
||||
"@babel/types": "^7.28.0",
|
||||
"@babel/types": "^7.28.2",
|
||||
"debug": "^4.3.1"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"private": true,
|
||||
"scripts": {},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.28.0",
|
||||
"@babel/preset-env": "^7.28.0",
|
||||
"@babel/core": "^7.28.3",
|
||||
"@babel/preset-env": "^7.28.3",
|
||||
"@rollup/plugin-babel": "^6.0.4",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"vue": "^2.7.16",
|
||||
|
||||
10
src/pretix/testutils/mail.py
Normal file
10
src/pretix/testutils/mail.py
Normal file
@@ -0,0 +1,10 @@
|
||||
import smtplib
|
||||
|
||||
from django.core.mail.backends.locmem import EmailBackend
|
||||
|
||||
|
||||
class FailingEmailBackend(EmailBackend):
|
||||
def send_messages(self, email_messages):
|
||||
raise smtplib.SMTPRecipientsRefused({
|
||||
'recipient@example.org': (450, b'Recipient unknown')
|
||||
})
|
||||
@@ -71,7 +71,7 @@ def env():
|
||||
t.members.add(user)
|
||||
t.limit_events.add(event)
|
||||
o = Order.objects.create(
|
||||
code='FOO', event=event, email='dummy@dummy.test',
|
||||
code='ABC32', event=event, email='dummy@dummy.test',
|
||||
status=Order.STATUS_PENDING,
|
||||
datetime=now(), expires=now() + timedelta(days=10),
|
||||
total=14, locale='en',
|
||||
@@ -105,47 +105,48 @@ def env():
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_order_list(client, env):
|
||||
o = env[2]
|
||||
with scopes_disabled():
|
||||
otherticket = Item.objects.create(event=env[0], name='Early-bird ticket',
|
||||
category=None, default_price=23,
|
||||
admission=True, personalized=True)
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.get('/control/event/dummy/dummy/orders/')
|
||||
assert 'FOO' in response.content.decode()
|
||||
assert o.code in response.content.decode()
|
||||
response = client.get('/control/event/dummy/dummy/orders/?query=peter')
|
||||
assert 'FOO' in response.content.decode()
|
||||
assert o.code in response.content.decode()
|
||||
response = client.get('/control/event/dummy/dummy/orders/?query=hans')
|
||||
assert 'FOO' not in response.content.decode()
|
||||
assert o.code not in response.content.decode()
|
||||
response = client.get('/control/event/dummy/dummy/orders/?query=dummy')
|
||||
assert 'FOO' in response.content.decode()
|
||||
assert o.code in response.content.decode()
|
||||
response = client.get('/control/event/dummy/dummy/orders/?status=p')
|
||||
assert 'FOO' not in response.content.decode()
|
||||
assert o.code not in response.content.decode()
|
||||
response = client.get('/control/event/dummy/dummy/orders/?status=n')
|
||||
assert 'FOO' in response.content.decode()
|
||||
assert o.code in response.content.decode()
|
||||
response = client.get('/control/event/dummy/dummy/orders/?status=ne')
|
||||
assert 'FOO' in response.content.decode()
|
||||
assert o.code in response.content.decode()
|
||||
response = client.get('/control/event/dummy/dummy/orders/?item=%s' % otherticket.id)
|
||||
assert 'FOO' not in response.content.decode()
|
||||
assert o.code not in response.content.decode()
|
||||
response = client.get('/control/event/dummy/dummy/orders/?item=%s' % env[3].id)
|
||||
assert 'FOO' in response.content.decode()
|
||||
assert o.code in response.content.decode()
|
||||
response = client.get('/control/event/dummy/dummy/orders/?provider=free')
|
||||
assert 'FOO' not in response.content.decode()
|
||||
assert o.code not in response.content.decode()
|
||||
response = client.get('/control/event/dummy/dummy/orders/?provider=banktransfer')
|
||||
assert 'FOO' in response.content.decode()
|
||||
assert o.code in response.content.decode()
|
||||
|
||||
response = client.get('/control/event/dummy/dummy/orders/?status=o')
|
||||
assert 'FOO' not in response.content.decode()
|
||||
assert o.code not in response.content.decode()
|
||||
env[2].expires = now() - timedelta(days=10)
|
||||
env[2].save()
|
||||
response = client.get('/control/event/dummy/dummy/orders/?status=o')
|
||||
assert 'FOO' in response.content.decode()
|
||||
assert o.code in response.content.decode()
|
||||
|
||||
response = client.get('/control/event/dummy/dummy/orders/?status=pa')
|
||||
assert 'FOO' not in response.content.decode()
|
||||
assert o.code not in response.content.decode()
|
||||
env[2].require_approval = True
|
||||
env[2].save()
|
||||
response = client.get('/control/event/dummy/dummy/orders/?status=pa')
|
||||
assert 'FOO' in response.content.decode()
|
||||
assert o.code in response.content.decode()
|
||||
|
||||
with scopes_disabled():
|
||||
q = Question.objects.create(event=env[0], question="Q", type="N", required=True)
|
||||
@@ -153,9 +154,9 @@ def test_order_list(client, env):
|
||||
op = env[2].positions.first()
|
||||
qa = QuestionAnswer.objects.create(question=q, orderposition=op, answer="12")
|
||||
response = client.get('/control/event/dummy/dummy/orders/?question=%d&answer=12' % q.pk)
|
||||
assert 'FOO' in response.content.decode()
|
||||
assert o.code in response.content.decode()
|
||||
response = client.get('/control/event/dummy/dummy/orders/?question=%d&answer=13' % q.pk)
|
||||
assert 'FOO' not in response.content.decode()
|
||||
assert o.code not in response.content.decode()
|
||||
|
||||
q.type = "C"
|
||||
q.save()
|
||||
@@ -164,24 +165,24 @@ def test_order_list(client, env):
|
||||
qo2 = q.options.create(answer="Bar")
|
||||
qa.options.add(qo1)
|
||||
response = client.get('/control/event/dummy/dummy/orders/?question=%d&answer=%d' % (q.pk, qo1.pk))
|
||||
assert 'FOO' in response.content.decode()
|
||||
assert o.code in response.content.decode()
|
||||
response = client.get('/control/event/dummy/dummy/orders/?question=%d&answer=%d' % (q.pk, qo2.pk))
|
||||
assert 'FOO' not in response.content.decode()
|
||||
assert o.code not in response.content.decode()
|
||||
|
||||
response = client.get('/control/event/dummy/dummy/orders/?status=testmode')
|
||||
assert 'FOO' not in response.content.decode()
|
||||
assert o.code not in response.content.decode()
|
||||
assert 'TEST MODE' not in response.content.decode()
|
||||
env[2].testmode = True
|
||||
env[2].save()
|
||||
response = client.get('/control/event/dummy/dummy/orders/?status=testmode')
|
||||
assert 'FOO' in response.content.decode()
|
||||
assert o.code in response.content.decode()
|
||||
assert 'TEST MODE' in response.content.decode()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_order_detail(client, env):
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.get('/control/event/dummy/dummy/orders/FOO/')
|
||||
response = client.get('/control/event/dummy/dummy/orders/ABC32/')
|
||||
assert 'Early-bird' in response.content.decode()
|
||||
assert 'Peter' in response.content.decode()
|
||||
assert 'Lukas Gelöscht' in response.content.decode()
|
||||
@@ -193,7 +194,7 @@ def test_order_detail_show_test_mode(client, env):
|
||||
env[2].testmode = True
|
||||
env[2].save()
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.get('/control/event/dummy/dummy/orders/FOO/')
|
||||
response = client.get('/control/event/dummy/dummy/orders/ABC32/')
|
||||
assert 'TEST MODE' in response.content.decode()
|
||||
|
||||
|
||||
@@ -203,7 +204,7 @@ def test_order_set_contact(client, env):
|
||||
q = Quota.objects.create(event=env[0], size=0)
|
||||
q.items.add(env[3])
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/contact', {
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/contact', {
|
||||
'email': 'admin@rami.io'
|
||||
})
|
||||
with scopes_disabled():
|
||||
@@ -218,7 +219,7 @@ def test_order_set_customer(client, env):
|
||||
c = org.customers.create(email='foo@example.org')
|
||||
org.settings.customer_accounts = True
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/contact', {
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/contact', {
|
||||
'email': 'admin@rami.io',
|
||||
'customer': c.pk
|
||||
}, follow=True)
|
||||
@@ -233,7 +234,7 @@ def test_order_set_locale(client, env):
|
||||
q = Quota.objects.create(event=env[0], size=0)
|
||||
q.items.add(env[3])
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/locale', {
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/locale', {
|
||||
'locale': 'de'
|
||||
})
|
||||
with scopes_disabled():
|
||||
@@ -247,7 +248,7 @@ def test_order_set_locale_with_invalid_locale_value(client, env):
|
||||
q = Quota.objects.create(event=env[0], size=0)
|
||||
q.items.add(env[3])
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/locale', {
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/locale', {
|
||||
'locale': 'fr'
|
||||
})
|
||||
with scopes_disabled():
|
||||
@@ -261,7 +262,7 @@ def test_order_set_comment(client, env):
|
||||
q = Quota.objects.create(event=env[0], size=0)
|
||||
q.items.add(env[3])
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/comment', {
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/comment', {
|
||||
'comment': 'Foo'
|
||||
})
|
||||
with scopes_disabled():
|
||||
@@ -275,7 +276,7 @@ def test_order_transition_to_expired_success(client, env):
|
||||
q = Quota.objects.create(event=env[0], size=0)
|
||||
q.items.add(env[3])
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/transition', {
|
||||
'status': 'e'
|
||||
})
|
||||
with scopes_disabled():
|
||||
@@ -289,7 +290,7 @@ def test_order_transition_to_paid_in_time_success(client, env):
|
||||
q = Quota.objects.create(event=env[0], size=0)
|
||||
q.items.add(env[3])
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/transition', {
|
||||
'amount': str(env[2].pending_sum),
|
||||
'payment_date': now().date().isoformat(),
|
||||
'status': 'p'
|
||||
@@ -308,7 +309,7 @@ def test_order_transition_to_paid_expired_quota_left(client, env):
|
||||
q = Quota.objects.create(event=env[0], size=10)
|
||||
q.items.add(env[3])
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
res = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||
res = client.post('/control/event/dummy/dummy/orders/ABC32/transition', {
|
||||
'status': 'p',
|
||||
'payment_date': now().date().isoformat(),
|
||||
'amount': str(o.pending_sum),
|
||||
@@ -329,7 +330,7 @@ def test_order_approve(client, env):
|
||||
q = Quota.objects.create(event=env[0], size=10)
|
||||
q.items.add(env[3])
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
res = client.post('/control/event/dummy/dummy/orders/FOO/approve', {
|
||||
res = client.post('/control/event/dummy/dummy/orders/ABC32/approve', {
|
||||
})
|
||||
with scopes_disabled():
|
||||
o = Order.objects.get(id=env[2].id)
|
||||
@@ -348,7 +349,7 @@ def test_order_deny(client, env):
|
||||
q = Quota.objects.create(event=env[0], size=10)
|
||||
q.items.add(env[3])
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
res = client.post('/control/event/dummy/dummy/orders/FOO/deny', {
|
||||
res = client.post('/control/event/dummy/dummy/orders/ABC32/deny', {
|
||||
})
|
||||
with scopes_disabled():
|
||||
o = Order.objects.get(id=env[2].id)
|
||||
@@ -360,10 +361,10 @@ def test_order_deny(client, env):
|
||||
@pytest.mark.django_db
|
||||
def test_order_delete_require_testmode(client, env):
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
res = client.get('/control/event/dummy/dummy/orders/FOO/delete', {}, follow=True)
|
||||
res = client.get('/control/event/dummy/dummy/orders/ABC32/delete', {}, follow=True)
|
||||
assert 'alert-danger' in res.content.decode()
|
||||
assert 'Only orders created in test mode can be deleted' in res.content.decode()
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/delete', {}, follow=True)
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/delete', {}, follow=True)
|
||||
with scopes_disabled():
|
||||
assert Order.objects.get(id=env[2].id)
|
||||
|
||||
@@ -375,7 +376,7 @@ def test_order_delete(client, env):
|
||||
o.testmode = True
|
||||
o.save()
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/delete', {}, follow=True)
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/delete', {}, follow=True)
|
||||
with scopes_disabled():
|
||||
assert not Order.objects.filter(id=env[2].id).exists()
|
||||
|
||||
@@ -401,8 +402,8 @@ def test_order_transition(client, env, process):
|
||||
o.status = process[0]
|
||||
o.save()
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
client.get('/control/event/dummy/dummy/orders/FOO/transition?status=' + process[1])
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||
client.get('/control/event/dummy/dummy/orders/ABC32/transition?status=' + process[1])
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/transition', {
|
||||
'amount': str(o.pending_sum),
|
||||
'payment_date': now().date().isoformat(),
|
||||
'status': process[1]
|
||||
@@ -423,8 +424,8 @@ def test_order_cancel_free(client, env):
|
||||
o.total = Decimal('0.00')
|
||||
o.save()
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
client.get('/control/event/dummy/dummy/orders/FOO/transition?status=c')
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||
client.get('/control/event/dummy/dummy/orders/ABC32/transition?status=c')
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/transition', {
|
||||
'status': 'c'
|
||||
})
|
||||
with scopes_disabled():
|
||||
@@ -441,8 +442,8 @@ def test_order_cancel_paid_keep_fee(client, env):
|
||||
o.save()
|
||||
o.event.tax_rules.create(rate=Decimal('7.00'), default=True)
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
client.get('/control/event/dummy/dummy/orders/FOO/transition?status=c')
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||
client.get('/control/event/dummy/dummy/orders/ABC32/transition?status=c')
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/transition', {
|
||||
'status': 'c',
|
||||
'cancellation_fee': '6.00'
|
||||
})
|
||||
@@ -471,8 +472,8 @@ def test_order_cancel_paid_keep_fee_taxed(client, env):
|
||||
o.save()
|
||||
tr7 = o.event.tax_rules.create(rate=Decimal('7.00'), default=True)
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
client.get('/control/event/dummy/dummy/orders/FOO/transition?status=c')
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||
client.get('/control/event/dummy/dummy/orders/ABC32/transition?status=c')
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/transition', {
|
||||
'status': 'c',
|
||||
'cancellation_fee': '6.00'
|
||||
})
|
||||
@@ -509,8 +510,8 @@ def test_order_cancel_paid_keep_fee_tax_split(client, env):
|
||||
op2._calculate_tax(tax_rule=tr19)
|
||||
op2.save()
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
client.get('/control/event/dummy/dummy/orders/FOO/transition?status=c')
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||
client.get('/control/event/dummy/dummy/orders/ABC32/transition?status=c')
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/transition', {
|
||||
'status': 'c',
|
||||
'cancellation_fee': '6.00'
|
||||
})
|
||||
@@ -543,8 +544,8 @@ def test_order_cancel_pending_keep_fee(client, env):
|
||||
o.status = Order.STATUS_PENDING
|
||||
o.save()
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
client.get('/control/event/dummy/dummy/orders/FOO/transition?status=c')
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||
client.get('/control/event/dummy/dummy/orders/ABC32/transition?status=c')
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/transition', {
|
||||
'status': 'c',
|
||||
'cancellation_fee': '6.00'
|
||||
})
|
||||
@@ -568,8 +569,8 @@ def test_order_cancel_pending_fee_too_high(client, env):
|
||||
o.status = Order.STATUS_PENDING
|
||||
o.save()
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
client.get('/control/event/dummy/dummy/orders/FOO/transition?status=c')
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||
client.get('/control/event/dummy/dummy/orders/ABC32/transition?status=c')
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/transition', {
|
||||
'status': 'c',
|
||||
'cancellation_fee': '26.00'
|
||||
})
|
||||
@@ -584,8 +585,8 @@ def test_order_cancel_pending_fee_too_high(client, env):
|
||||
@pytest.mark.django_db
|
||||
def test_order_cancel_unpaid_fees_allowed(client, env):
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
client.get('/control/event/dummy/dummy/orders/FOO/transition?status=c')
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||
client.get('/control/event/dummy/dummy/orders/ABC32/transition?status=c')
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/transition', {
|
||||
'status': 'c',
|
||||
'cancellation_fee': '6.00'
|
||||
})
|
||||
@@ -601,7 +602,7 @@ def test_order_cancel_unpaid_fees_allowed(client, env):
|
||||
def test_order_invoice_create_forbidden(client, env):
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
env[0].settings.set('invoice_generate', 'no')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/invoice', {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/invoice', {}, follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
|
||||
|
||||
@@ -611,7 +612,7 @@ def test_order_invoice_create_duplicate(client, env):
|
||||
with scopes_disabled():
|
||||
generate_invoice(env[2])
|
||||
env[0].settings.set('invoice_generate', 'admin')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/invoice', {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/invoice', {}, follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
|
||||
|
||||
@@ -619,7 +620,7 @@ def test_order_invoice_create_duplicate(client, env):
|
||||
def test_order_invoice_create_ok(client, env):
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
env[0].settings.set('invoice_generate', 'admin')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/invoice', {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/invoice', {}, follow=True)
|
||||
assert 'alert-success' in response.content.decode()
|
||||
with scopes_disabled():
|
||||
assert env[2].invoices.exists()
|
||||
@@ -632,7 +633,7 @@ def test_order_invoice_retransmit(client, env):
|
||||
i = generate_invoice(env[2])
|
||||
i.transmission_status = Invoice.TRANSMISSION_STATUS_FAILED
|
||||
i.save()
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/invoices/%d/retransmit' % i.pk, {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/invoices/%d/retransmit' % i.pk, {}, follow=True)
|
||||
assert 'alert-success' in response.content.decode()
|
||||
i.refresh_from_db()
|
||||
assert i.transmission_status == Invoice.TRANSMISSION_STATUS_PENDING
|
||||
@@ -645,7 +646,7 @@ def test_order_invoice_regenerate(client, env):
|
||||
i = generate_invoice(env[2])
|
||||
InvoiceAddress.objects.create(name_parts={'full_name': 'Foo', "_scheme": "full"}, order=env[2])
|
||||
env[0].settings.set('invoice_generate', 'admin')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/invoices/%d/regenerate' % i.pk, {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/invoices/%d/regenerate' % i.pk, {}, follow=True)
|
||||
assert 'alert-success' in response.content.decode()
|
||||
i.refresh_from_db()
|
||||
assert 'Foo' in i.invoice_to
|
||||
@@ -659,14 +660,14 @@ def test_order_invoice_regenerate_canceled(client, env):
|
||||
with scopes_disabled():
|
||||
i = generate_invoice(env[2])
|
||||
generate_cancellation(i)
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/invoices/%d/regenerate' % i.pk, {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/invoices/%d/regenerate' % i.pk, {}, follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_order_invoice_regenerate_unknown(client, env):
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/invoices/%d/regenerate' % 3, {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/invoices/%d/regenerate' % 3, {}, follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
|
||||
|
||||
@@ -677,7 +678,7 @@ def test_order_invoice_reissue(client, env):
|
||||
i = generate_invoice(env[2])
|
||||
InvoiceAddress.objects.create(name_parts={'full_name': 'Foo', "_scheme": "full"}, order=env[2])
|
||||
env[0].settings.set('invoice_generate', 'admin')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/invoices/%d/reissue' % i.pk, {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/invoices/%d/reissue' % i.pk, {}, follow=True)
|
||||
assert 'alert-success' in response.content.decode()
|
||||
i.refresh_from_db()
|
||||
with scopes_disabled():
|
||||
@@ -693,14 +694,14 @@ def test_order_invoice_reissue_canceled(client, env):
|
||||
with scopes_disabled():
|
||||
i = generate_invoice(env[2])
|
||||
generate_cancellation(i)
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/invoices/%d/reissue' % i.pk, {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/invoices/%d/reissue' % i.pk, {}, follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_order_invoice_reissue_unknown(client, env):
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/invoices/%d/reissue' % 3, {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/invoices/%d/reissue' % 3, {}, follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
|
||||
|
||||
@@ -708,9 +709,9 @@ def test_order_invoice_reissue_unknown(client, env):
|
||||
def test_order_resend_link(client, env):
|
||||
mail.outbox = []
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/resend', {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/resend', {}, follow=True)
|
||||
assert 'alert-success' in response.content.decode()
|
||||
assert 'FOO' in mail.outbox[0].body
|
||||
assert 'ABC32' in mail.outbox[0].body
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -720,9 +721,9 @@ def test_order_reactivate_not_canceled(client, env):
|
||||
o.status = Order.STATUS_PAID
|
||||
o.save()
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.get('/control/event/dummy/dummy/orders/FOO/reactivate', follow=True)
|
||||
response = client.get('/control/event/dummy/dummy/orders/ABC32/reactivate', follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/reactivate', follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/reactivate', follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
|
||||
|
||||
@@ -735,7 +736,7 @@ def test_order_reactivate(client, env):
|
||||
o.status = Order.STATUS_CANCELED
|
||||
o.save()
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/reactivate', {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/reactivate', {
|
||||
}, follow=True)
|
||||
assert 'alert-success' in response.content.decode()
|
||||
with scopes_disabled():
|
||||
@@ -750,9 +751,9 @@ def test_order_extend_not_pending(client, env):
|
||||
o.status = Order.STATUS_PAID
|
||||
o.save()
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.get('/control/event/dummy/dummy/orders/FOO/extend', follow=True)
|
||||
response = client.get('/control/event/dummy/dummy/orders/ABC32/extend', follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/extend', follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
|
||||
|
||||
@@ -765,7 +766,7 @@ def test_order_extend_not_expired(client, env):
|
||||
generate_invoice(o)
|
||||
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/extend', {
|
||||
'expires': newdate
|
||||
}, follow=True)
|
||||
assert 'alert-success' in response.content.decode()
|
||||
@@ -785,7 +786,7 @@ def test_order_extend_overdue_quota_empty(client, env):
|
||||
q.items.add(env[3])
|
||||
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/extend', {
|
||||
'expires': newdate
|
||||
}, follow=True)
|
||||
assert 'alert-success' in response.content.decode()
|
||||
@@ -808,7 +809,7 @@ def test_order_extend_overdue_quota_blocked_by_waiting_list(client, env):
|
||||
|
||||
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/extend', {
|
||||
'expires': newdate
|
||||
}, follow=True)
|
||||
assert 'alert-success' in response.content.decode()
|
||||
@@ -833,7 +834,7 @@ def test_order_extend_expired_quota_left(client, env):
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
with scopes_disabled():
|
||||
assert o.invoices.count() == 2
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/extend', {
|
||||
'expires': newdate
|
||||
}, follow=True)
|
||||
assert b'alert-success' in response.content
|
||||
@@ -857,7 +858,7 @@ def test_order_extend_expired_quota_empty(client, env):
|
||||
q.items.add(env[3])
|
||||
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/extend', {
|
||||
'expires': newdate
|
||||
}, follow=True)
|
||||
assert b'alert-danger' in response.content
|
||||
@@ -878,7 +879,7 @@ def test_order_extend_expired_quota_empty_ignore(client, env):
|
||||
q.items.add(env[3])
|
||||
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/extend', {
|
||||
'expires': newdate,
|
||||
'quota_ignore': 'on'
|
||||
}, follow=True)
|
||||
@@ -905,7 +906,7 @@ def test_order_extend_expired_seat_free(client, env):
|
||||
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
assert o.invoices.count() == 2
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/extend', {
|
||||
'expires': newdate
|
||||
}, follow=True)
|
||||
assert b'alert-success' in response.content
|
||||
@@ -933,7 +934,7 @@ def test_order_extend_expired_seat_blocked(client, env):
|
||||
q.items.add(env[3])
|
||||
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/extend', {
|
||||
'expires': newdate
|
||||
}, follow=True)
|
||||
assert b'alert-danger' in response.content
|
||||
@@ -976,7 +977,7 @@ def test_order_extend_expired_seat_taken(client, env):
|
||||
q.items.add(env[3])
|
||||
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/extend', {
|
||||
'expires': newdate
|
||||
}, follow=True)
|
||||
assert b'alert-danger' in response.content
|
||||
@@ -1005,7 +1006,7 @@ def test_order_extend_expired_quota_partial(client, env):
|
||||
q.items.add(env[3])
|
||||
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/extend', {
|
||||
'expires': newdate
|
||||
}, follow=True)
|
||||
assert b'alert-danger' in response.content
|
||||
@@ -1035,7 +1036,7 @@ def test_order_extend_expired_voucher_budget_ok(client, env):
|
||||
q.items.add(env[3])
|
||||
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/extend', {
|
||||
'expires': newdate
|
||||
}, follow=True)
|
||||
assert b'alert-success' in response.content
|
||||
@@ -1066,7 +1067,7 @@ def test_order_extend_expired_voucher_budget_fail(client, env):
|
||||
q.items.add(env[3])
|
||||
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/extend', {
|
||||
'expires': newdate
|
||||
}, follow=True)
|
||||
assert b'alert-danger' in response.content
|
||||
@@ -1090,7 +1091,7 @@ def test_order_mark_paid_overdue_quota_blocked_by_waiting_list(client, env):
|
||||
env[0].waitinglistentries.create(item=env[3], email='foo@bar.com')
|
||||
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/transition', {
|
||||
'status': 'p',
|
||||
'payment_date': now().date().isoformat(),
|
||||
'amount': str(o.pending_sum),
|
||||
@@ -1112,7 +1113,7 @@ def test_order_mark_paid_blocked(client, env):
|
||||
q.items.add(env[3])
|
||||
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/transition', {
|
||||
'amount': str(o.pending_sum),
|
||||
'payment_date': now().date().isoformat(),
|
||||
'status': 'p'
|
||||
@@ -1137,7 +1138,7 @@ def test_order_mark_paid_overpaid_expired(client, env):
|
||||
q.items.add(env[3])
|
||||
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/transition', {
|
||||
'status': 'p',
|
||||
'payment_date': now().date().isoformat(),
|
||||
'amount': '0.00',
|
||||
@@ -1162,7 +1163,7 @@ def test_order_mark_paid_forced(client, env):
|
||||
q.items.add(env[3])
|
||||
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/transition', {
|
||||
'status': 'p',
|
||||
'payment_date': now().date().isoformat(),
|
||||
'amount': str(o.pending_sum),
|
||||
@@ -1206,7 +1207,7 @@ def test_order_mark_paid_expired_seat_taken(client, env):
|
||||
q = Quota.objects.create(event=env[0], size=100)
|
||||
q.items.add(env[3])
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/transition', {
|
||||
'status': 'p',
|
||||
'payment_date': now().date().isoformat(),
|
||||
'amount': str(o.pending_sum),
|
||||
@@ -1236,7 +1237,7 @@ def test_order_mark_paid_expired_blocked(client, env):
|
||||
q = Quota.objects.create(event=env[0], size=100)
|
||||
q.items.add(env[3])
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/transition', {
|
||||
'status': 'p',
|
||||
'payment_date': now().date().isoformat(),
|
||||
'amount': str(o.pending_sum),
|
||||
@@ -1250,7 +1251,7 @@ def test_order_mark_paid_expired_blocked(client, env):
|
||||
|
||||
env[0].settings.seating_allow_blocked_seats_for_channel = ["bar"]
|
||||
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/transition', {
|
||||
'status': 'p',
|
||||
'payment_date': now().date().isoformat(),
|
||||
'amount': str(o.pending_sum),
|
||||
@@ -1265,22 +1266,22 @@ def test_order_mark_paid_expired_blocked(client, env):
|
||||
@pytest.mark.django_db
|
||||
def test_order_go_lowercase(client, env):
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.get('/control/event/dummy/dummy/orders/go?code=DuMmyfoO')
|
||||
assert response['Location'].endswith('/control/event/dummy/dummy/orders/FOO/')
|
||||
response = client.get('/control/event/dummy/dummy/orders/go?code=DuMmyabC32')
|
||||
assert response['Location'].endswith('/control/event/dummy/dummy/orders/ABC32/')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_order_go_with_slug(client, env):
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.get('/control/event/dummy/dummy/orders/go?code=DUMMYFOO')
|
||||
assert response['Location'].endswith('/control/event/dummy/dummy/orders/FOO/')
|
||||
response = client.get('/control/event/dummy/dummy/orders/go?code=DUMMYABC32')
|
||||
assert response['Location'].endswith('/control/event/dummy/dummy/orders/ABC32/')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_order_go_found(client, env):
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.get('/control/event/dummy/dummy/orders/go?code=FOO')
|
||||
assert response['Location'].endswith('/control/event/dummy/dummy/orders/FOO/')
|
||||
response = client.get('/control/event/dummy/dummy/orders/go?code=ABC32')
|
||||
assert response['Location'].endswith('/control/event/dummy/dummy/orders/ABC32/')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -1768,7 +1769,7 @@ def test_check_vatid(client, env):
|
||||
ia = InvoiceAddress.objects.create(order=env[2], is_business=True, vat_id='ATU1234567', country=Country('AT'))
|
||||
with mock.patch('pretix.base.services.tax._validate_vat_id_EU') as mock_validate:
|
||||
mock_validate.return_value = 'AT123456'
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/checkvatid', {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/checkvatid', {}, follow=True)
|
||||
assert 'alert-success' in response.content.decode()
|
||||
ia.refresh_from_db()
|
||||
assert ia.vat_id_validated
|
||||
@@ -1781,7 +1782,7 @@ def test_check_vatid_no_entered(client, env):
|
||||
ia = InvoiceAddress.objects.create(order=env[2], is_business=True, country=Country('AT'))
|
||||
with mock.patch('pretix.base.services.tax._validate_vat_id_EU') as mock_validate:
|
||||
mock_validate.return_value = 'AT123456'
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/checkvatid', {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/checkvatid', {}, follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
ia.refresh_from_db()
|
||||
assert not ia.vat_id_validated
|
||||
@@ -1792,7 +1793,7 @@ def test_check_vatid_invalid_country(client, env):
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
with scopes_disabled():
|
||||
ia = InvoiceAddress.objects.create(order=env[2], is_business=True, vat_id='ATU1234567', country=Country('FR'))
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/checkvatid', {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/checkvatid', {}, follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
ia.refresh_from_db()
|
||||
assert not ia.vat_id_validated
|
||||
@@ -1805,7 +1806,7 @@ def test_check_vatid_noneu_country(client, env):
|
||||
ia = InvoiceAddress.objects.create(order=env[2], is_business=True, vat_id='CHU1234567', country=Country('CH'))
|
||||
with mock.patch('pretix.base.services.tax._validate_vat_id_EU') as mock_validate:
|
||||
mock_validate.return_value = 'AT123456'
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/checkvatid', {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/checkvatid', {}, follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
ia.refresh_from_db()
|
||||
assert not ia.vat_id_validated
|
||||
@@ -1818,7 +1819,7 @@ def test_check_vatid_no_country(client, env):
|
||||
ia = InvoiceAddress.objects.create(order=env[2], is_business=True, vat_id='ATU1234567')
|
||||
with mock.patch('pretix.base.services.tax._validate_vat_id_EU') as mock_validate:
|
||||
mock_validate.return_value = 'AT123456'
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/checkvatid', {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/checkvatid', {}, follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
ia.refresh_from_db()
|
||||
assert not ia.vat_id_validated
|
||||
@@ -1829,7 +1830,7 @@ def test_check_vatid_no_invoiceaddress(client, env):
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
with mock.patch('pretix.base.services.tax._validate_vat_id_EU') as mock_validate:
|
||||
mock_validate.return_value = 'AT123456'
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/checkvatid', {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/checkvatid', {}, follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
|
||||
|
||||
@@ -1843,7 +1844,7 @@ def test_check_vatid_invalid(client, env):
|
||||
raise VATIDFinalError('Fail')
|
||||
|
||||
mock_validate.side_effect = raiser
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/checkvatid', {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/checkvatid', {}, follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
ia.refresh_from_db()
|
||||
assert not ia.vat_id_validated
|
||||
@@ -1859,7 +1860,7 @@ def test_check_vatid_unavailable(client, env):
|
||||
raise VATIDTemporaryError('Fail')
|
||||
|
||||
mock_validate.side_effect = raiser
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/checkvatid', {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/checkvatid', {}, follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
ia.refresh_from_db()
|
||||
assert not ia.vat_id_validated
|
||||
@@ -1870,11 +1871,11 @@ def test_cancel_payment(client, env):
|
||||
with scopes_disabled():
|
||||
p = env[2].payments.last()
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/payments/{}/cancel'.format(p.pk), {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/payments/{}/cancel'.format(p.pk), {}, follow=True)
|
||||
assert 'alert-success' in response.content.decode()
|
||||
p.refresh_from_db()
|
||||
assert p.state == OrderPayment.PAYMENT_STATE_CANCELED
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/payments/{}/cancel'.format(p.pk), {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/payments/{}/cancel'.format(p.pk), {}, follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
|
||||
|
||||
@@ -1889,13 +1890,13 @@ def test_cancel_refund(client, env):
|
||||
execution_date=now(),
|
||||
)
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/cancel'.format(r.pk), {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/refunds/{}/cancel'.format(r.pk), {}, follow=True)
|
||||
assert 'alert-success' in response.content.decode()
|
||||
r.refresh_from_db()
|
||||
assert r.state == OrderRefund.REFUND_STATE_CANCELED
|
||||
r.state = OrderRefund.REFUND_STATE_DONE
|
||||
r.save()
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/cancel'.format(r.pk), {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/refunds/{}/cancel'.format(r.pk), {}, follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
r.refresh_from_db()
|
||||
assert r.state == OrderRefund.REFUND_STATE_DONE
|
||||
@@ -1912,7 +1913,7 @@ def test_process_refund(client, env):
|
||||
execution_date=now(),
|
||||
)
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/process'.format(r.pk), {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/refunds/{}/process'.format(r.pk), {}, follow=True)
|
||||
assert 'alert-success' in response.content.decode()
|
||||
r.refresh_from_db()
|
||||
assert r.state == OrderRefund.REFUND_STATE_DONE
|
||||
@@ -1939,7 +1940,7 @@ def test_process_refund_overpaid_externally(client, env):
|
||||
execution_date=now(),
|
||||
)
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/process'.format(r.pk), {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/refunds/{}/process'.format(r.pk), {}, follow=True)
|
||||
assert 'alert-success' in response.content.decode()
|
||||
r.refresh_from_db()
|
||||
assert r.state == OrderRefund.REFUND_STATE_DONE
|
||||
@@ -1959,7 +1960,7 @@ def test_process_refund_invalid_state(client, env):
|
||||
execution_date=now(),
|
||||
)
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/process'.format(r.pk), {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/refunds/{}/process'.format(r.pk), {}, follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
r.refresh_from_db()
|
||||
assert r.state == OrderRefund.REFUND_STATE_CANCELED
|
||||
@@ -1976,7 +1977,7 @@ def test_process_refund_mark_refunded(client, env):
|
||||
execution_date=now(),
|
||||
)
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/process'.format(r.pk), {'action': 'r'},
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/refunds/{}/process'.format(r.pk), {'action': 'r'},
|
||||
follow=True)
|
||||
assert 'alert-success' in response.content.decode()
|
||||
r.refresh_from_db()
|
||||
@@ -1996,7 +1997,7 @@ def test_done_refund(client, env):
|
||||
execution_date=now(),
|
||||
)
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/done'.format(r.pk), {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/refunds/{}/done'.format(r.pk), {}, follow=True)
|
||||
assert 'alert-success' in response.content.decode()
|
||||
r.refresh_from_db()
|
||||
assert r.state == OrderRefund.REFUND_STATE_DONE
|
||||
@@ -2013,7 +2014,7 @@ def test_done_refund_invalid_state(client, env):
|
||||
execution_date=now(),
|
||||
)
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/done'.format(r.pk), {}, follow=True)
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/refunds/{}/done'.format(r.pk), {}, follow=True)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
r.refresh_from_db()
|
||||
assert r.state == OrderRefund.REFUND_STATE_EXTERNAL
|
||||
@@ -2024,7 +2025,7 @@ def test_confirm_payment(client, env):
|
||||
with scopes_disabled():
|
||||
p = env[2].payments.last()
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/payments/{}/confirm'.format(p.pk), {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/payments/{}/confirm'.format(p.pk), {
|
||||
'amount': str(p.amount),
|
||||
'payment_date': str(now().date().isoformat()),
|
||||
}, follow=True)
|
||||
@@ -2042,7 +2043,7 @@ def test_confirm_payment_invalid_state(client, env):
|
||||
p.state = OrderPayment.PAYMENT_STATE_FAILED
|
||||
p.save()
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/payments/{}/confirm'.format(p.pk), {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/payments/{}/confirm'.format(p.pk), {
|
||||
'amount': str(p.amount),
|
||||
'payment_date': str(now().date().isoformat()),
|
||||
}, follow=True)
|
||||
@@ -2060,7 +2061,7 @@ def test_confirm_payment_partal_amount(client, env):
|
||||
p.amount -= Decimal(5.00)
|
||||
p.save()
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/payments/{}/confirm'.format(p.pk), {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/payments/{}/confirm'.format(p.pk), {
|
||||
'amount': str(p.amount),
|
||||
'payment_date': str(now().date().isoformat()),
|
||||
}, follow=True)
|
||||
@@ -2077,15 +2078,15 @@ def test_refund_paid_order_fully_mark_as_refunded(client, env):
|
||||
p = env[2].payments.last()
|
||||
p.confirm()
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.get('/control/event/dummy/dummy/orders/FOO/refund')
|
||||
response = client.get('/control/event/dummy/dummy/orders/ABC32/refund')
|
||||
doc = BeautifulSoup(response.content.decode(), "lxml")
|
||||
assert doc.select("input[name$=partial_amount]")[0]["value"] == "14.00"
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/refund', {
|
||||
'start-partial_amount': '14.00',
|
||||
'start-mode': 'full',
|
||||
'start-action': 'mark_refunded'
|
||||
}, follow=True)
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/refund', {
|
||||
'start-partial_amount': '14.00',
|
||||
'start-mode': 'full',
|
||||
'start-action': 'mark_refunded',
|
||||
@@ -2111,10 +2112,10 @@ def test_refund_paid_order_fully_mark_as_pending(client, env):
|
||||
p = env[2].payments.last()
|
||||
p.confirm()
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.get('/control/event/dummy/dummy/orders/FOO/refund')
|
||||
response = client.get('/control/event/dummy/dummy/orders/ABC32/refund')
|
||||
doc = BeautifulSoup(response.content.decode(), "lxml")
|
||||
assert doc.select("input[name$=partial_amount]")[0]["value"] == "14.00"
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/refund', {
|
||||
'start-partial_amount': '14.00',
|
||||
'start-mode': 'full',
|
||||
'start-action': 'mark_pending',
|
||||
@@ -2140,15 +2141,15 @@ def test_refund_paid_order_partially_mark_as_pending(client, env):
|
||||
p = env[2].payments.last()
|
||||
p.confirm()
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.get('/control/event/dummy/dummy/orders/FOO/refund')
|
||||
response = client.get('/control/event/dummy/dummy/orders/ABC32/refund')
|
||||
doc = BeautifulSoup(response.content.decode(), "lxml")
|
||||
assert doc.select("input[name$=partial_amount]")[0]["value"] == "14.00"
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/refund', {
|
||||
'start-partial_amount': '7.00',
|
||||
'start-mode': 'partial',
|
||||
'start-action': 'mark_pending'
|
||||
}, follow=True)
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/refund', {
|
||||
'start-partial_amount': '7.00',
|
||||
'start-mode': 'partial',
|
||||
'start-action': 'mark_pending',
|
||||
@@ -2178,8 +2179,8 @@ def test_refund_propose_lower_payment(client, env):
|
||||
amount=Decimal('6.00'), provider='stripe', state=OrderPayment.PAYMENT_STATE_CONFIRMED
|
||||
)
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
client.get('/control/event/dummy/dummy/orders/FOO/refund')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
||||
client.get('/control/event/dummy/dummy/orders/ABC32/refund')
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/refund', {
|
||||
'start-partial_amount': '7.00',
|
||||
'start-mode': 'partial',
|
||||
'start-action': 'mark_pending'
|
||||
@@ -2199,8 +2200,8 @@ def test_refund_propose_equal_payment(client, env):
|
||||
amount=Decimal('7.00'), provider='stripe', state=OrderPayment.PAYMENT_STATE_CONFIRMED
|
||||
)
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
client.get('/control/event/dummy/dummy/orders/FOO/refund')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
||||
client.get('/control/event/dummy/dummy/orders/ABC32/refund')
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/refund', {
|
||||
'start-partial_amount': '7.00',
|
||||
'start-mode': 'partial',
|
||||
'start-action': 'mark_pending'
|
||||
@@ -2220,8 +2221,8 @@ def test_refund_propose_higher_payment(client, env):
|
||||
amount=Decimal('8.00'), provider='stripe', state=OrderPayment.PAYMENT_STATE_CONFIRMED
|
||||
)
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
client.get('/control/event/dummy/dummy/orders/FOO/refund')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
||||
client.get('/control/event/dummy/dummy/orders/ABC32/refund')
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/refund', {
|
||||
'start-partial_amount': '7.00',
|
||||
'start-mode': 'partial',
|
||||
'start-action': 'mark_pending'
|
||||
@@ -2237,7 +2238,7 @@ def test_refund_amount_does_not_match_or_invalid(client, env):
|
||||
p = env[2].payments.last()
|
||||
p.confirm()
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
resp = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
||||
resp = client.post('/control/event/dummy/dummy/orders/ABC32/refund', {
|
||||
'start-partial_amount': '7.00',
|
||||
'start-mode': 'partial',
|
||||
'start-action': 'mark_pending',
|
||||
@@ -2249,7 +2250,7 @@ def test_refund_amount_does_not_match_or_invalid(client, env):
|
||||
}, follow=True)
|
||||
assert b'alert-danger' in resp.content
|
||||
assert b'do not match the' in resp.content
|
||||
resp = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
||||
resp = client.post('/control/event/dummy/dummy/orders/ABC32/refund', {
|
||||
'start-partial_amount': '15.00',
|
||||
'start-mode': 'partial',
|
||||
'start-action': 'mark_pending',
|
||||
@@ -2261,7 +2262,7 @@ def test_refund_amount_does_not_match_or_invalid(client, env):
|
||||
}, follow=True)
|
||||
assert b'alert-danger' in resp.content
|
||||
assert b'The refund amount needs to be positive' in resp.content
|
||||
resp = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
||||
resp = client.post('/control/event/dummy/dummy/orders/ABC32/refund', {
|
||||
'start-partial_amount': '7.00',
|
||||
'start-mode': 'partial',
|
||||
'start-action': 'mark_pending',
|
||||
@@ -2273,7 +2274,7 @@ def test_refund_amount_does_not_match_or_invalid(client, env):
|
||||
}, follow=True)
|
||||
assert b'alert-danger' in resp.content
|
||||
assert b'do not match the' in resp.content
|
||||
resp = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
||||
resp = client.post('/control/event/dummy/dummy/orders/ABC32/refund', {
|
||||
'start-partial_amount': '7.00',
|
||||
'start-mode': 'partial',
|
||||
'start-action': 'mark_pending',
|
||||
@@ -2311,7 +2312,7 @@ def test_refund_paid_order_automatically_failed(client, env, monkeypatch):
|
||||
monkeypatch.setattr("stripe.Charge.retrieve", charge_retr)
|
||||
monkeypatch.setattr("stripe.Refund.create", refund_create)
|
||||
|
||||
r = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
||||
r = client.post('/control/event/dummy/dummy/orders/ABC32/refund', {
|
||||
'start-partial_amount': '7.00',
|
||||
'start-mode': 'partial',
|
||||
'start-action': 'mark_pending',
|
||||
@@ -2359,7 +2360,7 @@ def test_refund_paid_order_automatically(client, env, monkeypatch):
|
||||
monkeypatch.setattr("stripe.Charge.retrieve", charge_retr)
|
||||
monkeypatch.setattr("stripe.Refund.create", refund_create)
|
||||
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/refund', {
|
||||
'start-partial_amount': '7.00',
|
||||
'start-mode': 'partial',
|
||||
'start-action': 'mark_pending',
|
||||
@@ -2386,7 +2387,7 @@ def test_refund_paid_order_offsetting_to_unknown(client, env):
|
||||
p.confirm()
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
|
||||
r = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
||||
r = client.post('/control/event/dummy/dummy/orders/ABC32/refund', {
|
||||
'start-partial_amount': '5.00',
|
||||
'start-mode': 'partial',
|
||||
'start-action': 'mark_pending',
|
||||
@@ -2422,7 +2423,7 @@ def test_refund_paid_order_offsetting_to_wrong_currency(client, env):
|
||||
)
|
||||
o.positions.create(price=5, item=ticket2)
|
||||
|
||||
r = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
||||
r = client.post('/control/event/dummy/dummy/orders/ABC32/refund', {
|
||||
'start-partial_amount': '5.00',
|
||||
'start-mode': 'partial',
|
||||
'start-action': 'mark_pending',
|
||||
@@ -2450,7 +2451,7 @@ def test_refund_paid_order_offsetting(client, env):
|
||||
total=5, locale='en'
|
||||
)
|
||||
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/refund', {
|
||||
'start-partial_amount': '5.00',
|
||||
'start-mode': 'partial',
|
||||
'start-action': 'mark_pending',
|
||||
@@ -2492,7 +2493,7 @@ def test_refund_prevent_duplicate_submit(client, env):
|
||||
)
|
||||
env[2].refunds.create(provider="manual", amount=Decimal("2.00"), state=OrderRefund.REFUND_STATE_CREATED)
|
||||
|
||||
r = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
||||
r = client.post('/control/event/dummy/dummy/orders/ABC32/refund', {
|
||||
'start-partial_amount': '5.00',
|
||||
'start-mode': 'partial',
|
||||
'start-action': 'mark_pending',
|
||||
@@ -2514,7 +2515,7 @@ def test_refund_paid_order_giftcard(client, env):
|
||||
p.confirm()
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
||||
client.post('/control/event/dummy/dummy/orders/ABC32/refund', {
|
||||
'start-partial_amount': '5.00',
|
||||
'start-mode': 'partial',
|
||||
'start-action': 'mark_pending',
|
||||
@@ -2582,7 +2583,7 @@ def test_delete_cancellation_request(client, env):
|
||||
refund_as_giftcard=True
|
||||
)
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/cancellationrequests/{}/delete'.format(r.pk), {},
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/cancellationrequests/{}/delete'.format(r.pk), {},
|
||||
follow=True)
|
||||
assert 'alert-success' in response.content.decode()
|
||||
assert not env[2].cancellation_requests.exists()
|
||||
@@ -2600,10 +2601,10 @@ def test_approve_cancellation_request(client, env):
|
||||
refund_as_giftcard=True
|
||||
)
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
response = client.get('/control/event/dummy/dummy/orders/FOO/transition?status=c&req={}'.format(r.pk), {})
|
||||
response = client.get('/control/event/dummy/dummy/orders/ABC32/transition?status=c&req={}'.format(r.pk), {})
|
||||
doc = BeautifulSoup(response.content.decode(), "lxml")
|
||||
assert doc.select('input[name=cancellation_fee]')[0]['value'] == '4.00'
|
||||
response = client.post('/control/event/dummy/dummy/orders/FOO/transition?req={}'.format(r.pk), {
|
||||
response = client.post('/control/event/dummy/dummy/orders/ABC32/transition?req={}'.format(r.pk), {
|
||||
'status': 'c',
|
||||
'cancellation_fee': '4.00'
|
||||
}, follow=True)
|
||||
|
||||
Reference in New Issue
Block a user