Compare commits

..
Author SHA1 Message Date
Raphael Michel d169958687 Add Transaction.source flag 2022-10-19 12:09:55 +02:00
198 changed files with 103954 additions and 129129 deletions
-3
View File
@@ -117,9 +117,6 @@ Example::
``loglevel``
Set console and file log level (``DEBUG``, ``INFO``, ``WARNING``, ``ERROR`` or ``CRITICAL``). Defaults to ``INFO``.
``request_id_header``
Specifies the name of a header that should be used for logging request IDs. Off by default.
Locale settings
---------------
-10
View File
@@ -61,7 +61,6 @@ invoice_address object Invoice address
AU, BR, CA, CN, MY, MX, and US.
├ internal_reference string Customer's internal reference to be printed on the invoice
├ custom_field string Custom invoice address field
├ invoice_email string Email address that should receive all invoices
├ vat_id string Customer VAT ID
└ vat_id_validated string ``true``, if the VAT ID has been validated against the
EU VAT service and validation was successful. This only
@@ -179,7 +178,6 @@ tax_rule integer The ID of the u
secret string Secret code printed on the tickets for validation
addon_to integer Internal ID of the position this position is an add-on for (or ``null``)
subevent integer ID of the date inside an event series this position belongs to (or ``null``).
discount integer ID of a discount that has been used during the creation of this position in some way (or ``null``).
pseudonymization_id string A random ID, e.g. for use in lead scanning apps
checkins list of objects List of **successful** check-ins with this ticket
├ id integer Internal ID of the check-in event
@@ -343,7 +341,6 @@ List of all orders
"country": "DE",
"state": "",
"internal_reference": "",
"invoice_email": null,
"vat_id": "EU123456789",
"vat_id_validated": false
},
@@ -374,7 +371,6 @@ List of all orders
"secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
"addon_to": null,
"subevent": null,
"discount": null,
"pseudonymization_id": "MQLJvANO3B",
"seat": null,
"checkins": [
@@ -520,7 +516,6 @@ Fetching individual orders
"country": "DE",
"state": "",
"internal_reference": "",
"invoice_email": null,
"vat_id": "EU123456789",
"vat_id_validated": false
},
@@ -551,7 +546,6 @@ Fetching individual orders
"secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
"addon_to": null,
"subevent": null,
"discount": null,
"pseudonymization_id": "MQLJvANO3B",
"seat": null,
"checkins": [
@@ -898,7 +892,6 @@ Creating orders
* ``country``
* ``state``
* ``internal_reference``
* ``invoice_email``
* ``vat_id``
* ``vat_id_validated`` (optional) If you need support for reverse charge (rarely the case), you need to check
yourself if the passed VAT ID is a valid EU VAT ID. In that case, set this to ``true``. Only valid VAT IDs will
@@ -996,7 +989,6 @@ Creating orders
"country": "UK",
"state": "",
"internal_reference": "",
"invoice_email": null,
"vat_id": ""
},
"positions": [
@@ -1495,7 +1487,6 @@ List of all order positions
"tax_rule": null,
"tax_value": "0.00",
"secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
"discount": null,
"pseudonymization_id": "MQLJvANO3B",
"seat": null,
"addon_to": null,
@@ -1606,7 +1597,6 @@ Fetching individual positions
"secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
"addon_to": null,
"subevent": null,
"discount": null,
"pseudonymization_id": "MQLJvANO3B",
"seat": null,
"checkins": [
-2
View File
@@ -19,8 +19,6 @@ max_usages integer The maximum num
redeemed (default: 1).
redeemed integer The number of times this voucher already has been
redeemed.
min_usages integer The minimum number of times this voucher must be
redeemed on first usage (default: 1).
valid_until datetime The voucher expiration date (or ``null``).
block_quota boolean If ``true``, quota is blocked for this voucher.
allow_ignore_quota boolean If ``true``, this voucher can be redeemed even if a
+1 -1
View File
@@ -38,7 +38,7 @@ Frontend
.. automodule:: pretix.presale.signals
:members: order_info, order_info_top, order_meta_from_request
:members: order_info, order_info_top, order_meta_from_request, order_source_from_request
Request flow
""""""""""""
+1 -1
View File
@@ -19,4 +19,4 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
__version__ = "4.15.0.dev0"
__version__ = "4.14.1.dev0"
-1
View File
@@ -196,7 +196,6 @@ class PretixPosSecurityProfile(AllowListSecurityProfile):
('POST', 'plugins:pretix_posbackend:posdebuglogentry-bulk-create'),
('GET', 'plugins:pretix_posbackend:poscashier-list'),
('POST', 'plugins:pretix_posbackend:stripeterminal.token'),
('POST', 'plugins:pretix_posbackend:stripeterminal.paymentintent'),
('PUT', 'plugins:pretix_posbackend:file.upload'),
('GET', 'api-v1:revokedsecrets-list'),
('GET', 'api-v1:event.settings'),
-9
View File
@@ -19,17 +19,11 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import logging
import ujson
from rest_framework import exceptions
from rest_framework.response import Response
from rest_framework.views import exception_handler, status
from pretix.base.services.locking import LockTimeoutException
logger = logging.getLogger(__name__)
def custom_exception_handler(exc, context):
response = exception_handler(exc, context)
@@ -43,7 +37,4 @@ def custom_exception_handler(exc, context):
}
)
if isinstance(exc, exceptions.APIException):
logger.info(f'API Exception [{exc.status_code}]: {ujson.dumps(exc.detail)}')
return response
-2
View File
@@ -237,14 +237,12 @@ class CartPositionCreateSerializer(BaseCartPositionCreateSerializer):
for addon_data in addons_data:
addon_data['addon_to'] = cp
addon_data['is_bundled'] = False
addon_data['cart_id'] = cp.cart_id
super().create(addon_data)
if bundled_data:
for bundle_data in bundled_data:
bundle_data['addon_to'] = cp
bundle_data['is_bundled'] = True
bundle_data['cart_id'] = cp.cart_id
super().create(bundle_data)
return cp
+1 -10
View File
@@ -411,8 +411,7 @@ class CloneEventSerializer(EventSerializer):
has_subevents = validated_data.pop('has_subevents', None)
tz = validated_data.pop('timezone', None)
sales_channels = validated_data.pop('sales_channels', None)
date_admission = validated_data.pop('date_admission', None)
new_event = super().create({**validated_data, 'plugins': None})
new_event = super().create(validated_data)
event = Event.objects.filter(slug=self.context['event'], organizer=self.context['organizer'].pk).first()
new_event.copy_data_from(event)
@@ -427,10 +426,6 @@ class CloneEventSerializer(EventSerializer):
new_event.sales_channels = sales_channels
if has_subevents is not None:
new_event.has_subevents = has_subevents
if has_subevents is not None:
new_event.has_subevents = has_subevents
if date_admission is not None:
new_event.date_admission = date_admission
new_event.save()
if tz:
new_event.settings.timezone = tz
@@ -745,7 +740,6 @@ class EventSettingsSerializer(SettingsSerializer):
'invoice_include_expire_date',
'invoice_address_explanation_text',
'invoice_email_attachment',
'invoice_email_asked',
'invoice_email_organizer',
'invoice_address_from_name',
'invoice_address_from',
@@ -761,9 +755,6 @@ class EventSettingsSerializer(SettingsSerializer):
'invoice_logo_image',
'cancel_allow_user',
'cancel_allow_user_until',
'cancel_allow_user_unpaid_keep',
'cancel_allow_user_unpaid_keep_fees',
'cancel_allow_user_unpaid_keep_percentage',
'cancel_allow_user_paid',
'cancel_allow_user_paid_until',
'cancel_allow_user_paid_keep',
-2
View File
@@ -184,8 +184,6 @@ class ItemSerializer(I18nAwareModelSerializer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['default_price'].allow_null = False
self.fields['default_price'].required = True
if not self.read_only:
self.fields['require_membership_types'].queryset = self.context['event'].organizer.membership_types.all()
self.fields['grant_membership_type'].queryset = self.context['event'].organizer.membership_types.all()
+5 -5
View File
@@ -92,7 +92,7 @@ class InvoiceAddressSerializer(I18nAwareModelSerializer):
class Meta:
model = InvoiceAddress
fields = ('last_modified', 'is_business', 'company', 'name', 'name_parts', 'street', 'zipcode', 'city', 'country',
'state', 'vat_id', 'vat_id_validated', 'custom_field', 'internal_reference', 'invoice_email')
'state', 'vat_id', 'vat_id_validated', 'custom_field', 'internal_reference')
read_only_fields = ('last_modified',)
def __init__(self, *args, **kwargs):
@@ -410,13 +410,13 @@ class OrderPositionSerializer(I18nAwareModelSerializer):
class Meta:
model = OrderPosition
fields = ('id', 'order', 'positionid', 'item', 'variation', 'price', 'attendee_name', 'attendee_name_parts',
'company', 'street', 'zipcode', 'city', 'country', 'state', 'discount',
'company', 'street', 'zipcode', 'city', 'country', 'state',
'attendee_email', 'voucher', 'tax_rate', 'tax_value', 'secret', 'addon_to', 'subevent', 'checkins',
'downloads', 'answers', 'tax_rule', 'pseudonymization_id', 'pdf_data', 'seat', 'canceled')
read_only_fields = (
'id', 'order', 'positionid', 'item', 'variation', 'price', 'voucher', 'tax_rate', 'tax_value', 'secret',
'addon_to', 'subevent', 'checkins', 'downloads', 'answers', 'tax_rule', 'pseudonymization_id', 'pdf_data',
'seat', 'canceled', 'discount',
'seat', 'canceled'
)
def __init__(self, *args, **kwargs):
@@ -721,7 +721,7 @@ class OrderPositionCreateSerializer(I18nAwareModelSerializer):
class Meta:
model = OrderPosition
fields = ('positionid', 'item', 'variation', 'price', 'attendee_name', 'attendee_name_parts', 'attendee_email',
'company', 'street', 'zipcode', 'city', 'country', 'state', 'is_bundled',
'company', 'street', 'zipcode', 'city', 'country', 'state',
'secret', 'addon_to', 'subevent', 'answers', 'seat', 'voucher')
def __init__(self, *args, **kwargs):
@@ -1378,7 +1378,7 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
state=OrderPayment.PAYMENT_STATE_CREATED
)
order.create_transactions(is_new=True, fees=fees, positions=pos_map.values())
order.create_transactions(is_new=True, fees=fees, positions=pos_map.values(), source=self.context['source'])
return order
+1 -1
View File
@@ -61,7 +61,7 @@ class VoucherSerializer(I18nAwareModelSerializer):
class Meta:
model = Voucher
fields = ('id', 'code', 'max_usages', 'redeemed', 'min_usages', 'valid_until', 'block_quota',
fields = ('id', 'code', 'max_usages', 'redeemed', 'valid_until', 'block_quota',
'allow_ignore_quota', 'price_mode', 'value', 'item', 'variation', 'quota',
'tag', 'comment', 'subevent', 'show_hidden_items', 'seat')
read_only_fields = ('id', 'redeemed')
+14
View File
@@ -0,0 +1,14 @@
from pretix.api.models import OAuthAccessToken
from pretix.base.models import Device, TeamAPIToken
def get_api_source(request):
if isinstance(request.auth, Device):
return "pretix.api", f"device:{request.auth.pk}"
elif isinstance(request.auth, TeamAPIToken):
return "pretix.api", f"token:{request.auth.pk}"
elif isinstance(request.auth, OAuthAccessToken):
return "pretix.api", f"oauth.app:{request.auth.application.pk}"
elif request.user.is_authenticated:
return "pretix.api", f"user:{request.user.pk}"
return "pretix.api", None
-5
View File
@@ -92,11 +92,6 @@ class CartPositionViewSet(CreateModelMixin, DestroyModelMixin, viewsets.ReadOnly
def perform_create(self, serializer):
raise NotImplementedError()
@transaction.atomic()
def perform_destroy(self, instance):
instance.addons.all().delete()
instance.delete()
def _require_locking(self, quota_diff, voucher_use_diff, seat_diff):
if voucher_use_diff or seat_diff:
# If any vouchers or seats are used, we lock to make sure we don't redeem them to often
+2 -12
View File
@@ -33,7 +33,6 @@
# License for the specific language governing permissions and limitations under the License.
import django_filters
from django.conf import settings
from django.db import transaction
from django.db.models import Prefetch, ProtectedError, Q
from django.utils.timezone import now
@@ -242,17 +241,13 @@ class EventViewSet(viewsets.ModelViewSet):
except Event.DoesNotExist:
raise ValidationError('Event to copy from was not found')
# Ensure that .installed() is only called when we NOT clone
plugins = serializer.validated_data.pop('plugins', None)
serializer.validated_data['plugins'] = None
new_event = serializer.save(organizer=self.request.organizer)
if copy_from:
new_event.copy_data_from(copy_from)
if plugins is not None:
new_event.set_active_plugins(plugins)
if 'plugins' in serializer.validated_data:
new_event.set_active_plugins(serializer.validated_data['plugins'])
if 'is_public' in serializer.validated_data:
new_event.is_public = serializer.validated_data['is_public']
if 'testmode' in serializer.validated_data:
@@ -261,17 +256,12 @@ class EventViewSet(viewsets.ModelViewSet):
new_event.sales_channels = serializer.validated_data['sales_channels']
if 'has_subevents' in serializer.validated_data:
new_event.has_subevents = serializer.validated_data['has_subevents']
if 'date_admission' in serializer.validated_data:
new_event.date_admission = serializer.validated_data['date_admission']
new_event.save()
if 'timezone' in serializer.validated_data:
new_event.settings.timezone = serializer.validated_data['timezone']
else:
serializer.instance.set_defaults()
new_event.set_active_plugins(plugins if plugins is not None else settings.PRETIX_PLUGINS_DEFAULT.split(','))
new_event.save(update_fields=['plugins'])
serializer.instance.log_action(
'pretix.event.added',
user=self.request.user,
+17 -24
View File
@@ -61,7 +61,7 @@ from pretix.api.serializers.orderchange import (
OrderPositionCreateForExistingOrderSerializer,
OrderPositionInfoPatchSerializer,
)
from pretix.api.views import RichOrderingFilter
from pretix.api.utils import get_api_source
from pretix.base.i18n import language
from pretix.base.models import (
CachedCombinedTicket, CachedTicket, Checkin, Device, EventMetaValue,
@@ -191,6 +191,7 @@ class OrderViewSet(viewsets.ModelViewSet):
ctx['event'] = self.request.event
ctx['pdf_data'] = self.request.query_params.get('pdf_data', 'false') == 'true'
ctx['exclude'] = self.request.query_params.getlist('exclude')
ctx['source'] = get_api_source(self.request)
return ctx
def get_queryset(self):
@@ -391,7 +392,8 @@ class OrderViewSet(viewsets.ModelViewSet):
oauth_application=request.auth.application if isinstance(request.auth, OAuthAccessToken) else None,
send_mail=send_mail,
email_comment=comment,
cancellation_fee=cancellation_fee
cancellation_fee=cancellation_fee,
source=get_api_source(request),
)
except OrderError as e:
return Response(
@@ -415,6 +417,7 @@ class OrderViewSet(viewsets.ModelViewSet):
order,
user=request.user if request.user.is_authenticated else None,
auth=request.auth if isinstance(request.auth, (Device, TeamAPIToken, OAuthAccessToken)) else None,
source=get_api_source(request),
)
except OrderError as e:
return Response(
@@ -434,6 +437,7 @@ class OrderViewSet(viewsets.ModelViewSet):
user=request.user if request.user.is_authenticated else None,
auth=request.auth if isinstance(request.auth, (Device, TeamAPIToken, OAuthAccessToken)) else None,
send_mail=send_mail,
source=get_api_source(request),
)
except Quota.QuotaExceededException as e:
return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)
@@ -454,6 +458,7 @@ class OrderViewSet(viewsets.ModelViewSet):
auth=request.auth if isinstance(request.auth, (Device, TeamAPIToken, OAuthAccessToken)) else None,
send_mail=send_mail,
comment=comment,
source=get_api_source(request),
)
except OrderError as e:
return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)
@@ -492,6 +497,7 @@ class OrderViewSet(viewsets.ModelViewSet):
order,
user=request.user if request.user.is_authenticated else None,
auth=request.auth,
source=get_api_source(request),
)
return self.retrieve(request, [], **kwargs)
@@ -509,6 +515,7 @@ class OrderViewSet(viewsets.ModelViewSet):
order,
user=request.user if request.user.is_authenticated else None,
auth=(request.auth if isinstance(request.auth, (TeamAPIToken, OAuthAccessToken, Device)) else None),
source=get_api_source(request),
)
return self.retrieve(request, [], **kwargs)
@@ -680,33 +687,28 @@ class OrderViewSet(viewsets.ModelViewSet):
)
if order.require_approval:
email_template = request.event.settings.mail_text_order_placed_require_approval
subject_template = request.event.settings.mail_subject_order_placed_require_approval
log_entry = 'pretix.event.order.email.order_placed_require_approval'
email_attendees = False
elif free_flow:
email_template = request.event.settings.mail_text_order_free
subject_template = request.event.settings.mail_subject_order_free
log_entry = 'pretix.event.order.email.order_free'
email_attendees = request.event.settings.mail_send_order_free_attendee
email_attendees_template = request.event.settings.mail_text_order_free_attendee
subject_attendees_template = request.event.settings.mail_subject_order_free_attendee
else:
email_template = request.event.settings.mail_text_order_placed
subject_template = request.event.settings.mail_subject_order_placed
log_entry = 'pretix.event.order.email.order_placed'
email_attendees = request.event.settings.mail_send_order_placed_attendee
email_attendees_template = request.event.settings.mail_text_order_placed_attendee
subject_attendees_template = request.event.settings.mail_subject_order_placed_attendee
_order_placed_email(
request.event, order, payment.payment_provider if payment else None, email_template, subject_template,
request.event, order, payment.payment_provider if payment else None, email_template,
log_entry, invoice, payment, is_free=free_flow
)
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:
_order_placed_email_attendee(request.event, order, p, email_attendees_template, subject_attendees_template,
log_entry, is_free=free_flow)
_order_placed_email_attendee(request.event, order, p, email_attendees_template, log_entry,
is_free=free_flow)
if not free_flow and order.status == Order.STATUS_PAID and payment:
payment._send_paid_mail(invoice, None, '')
@@ -936,7 +938,7 @@ with scopes_disabled():
class OrderPositionViewSet(viewsets.ModelViewSet):
serializer_class = OrderPositionSerializer
queryset = OrderPosition.all.none()
filter_backends = (DjangoFilterBackend, RichOrderingFilter)
filter_backends = (DjangoFilterBackend, OrderingFilter)
ordering = ('order__datetime', 'positionid')
ordering_fields = ('order__code', 'order__datetime', 'positionid', 'attendee_name', 'order__status',)
filterset_class = OrderPositionFilter
@@ -1490,7 +1492,8 @@ class PaymentViewSet(CreateModelMixin, viewsets.ReadOnlyModelViewSet):
if mark_refunded:
mark_order_refunded(payment.order,
user=self.request.user if self.request.user.is_authenticated else None,
auth=self.request.auth)
auth=self.request.auth,
source=get_api_source(self.request))
else:
payment.order.status = Order.STATUS_PENDING
payment.order.set_expires(
@@ -1563,7 +1566,7 @@ class RefundViewSet(CreateModelMixin, viewsets.ReadOnlyModelViewSet):
mark_refunded = request.data.get('mark_canceled', False)
if mark_refunded:
mark_order_refunded(refund.order, user=self.request.user if self.request.user.is_authenticated else None,
auth=self.request.auth)
auth=self.request.auth, source=get_api_source(self.request))
elif not (refund.order.status == Order.STATUS_PAID and refund.order.pending_sum <= 0):
refund.order.status = Order.STATUS_PENDING
refund.order.set_expires(
@@ -1610,23 +1613,13 @@ class RefundViewSet(CreateModelMixin, viewsets.ReadOnlyModelViewSet):
user=request.user if request.user.is_authenticated else None,
auth=request.auth
)
if r.state in (OrderRefund.REFUND_STATE_DONE, OrderRefund.REFUND_STATE_CANCELED, OrderRefund.REFUND_STATE_FAILED):
r.order.log_action(
f'pretix.event.order.refund.{r.state}', {
'local_id': r.local_id,
'provider': r.provider,
},
user=request.user if request.user.is_authenticated else None,
auth=request.auth
)
if mark_refunded:
try:
mark_order_refunded(
r.order,
user=request.user if request.user.is_authenticated else None,
auth=(request.auth if request.auth else None),
source=get_api_source(self.request),
)
except OrderError as e:
raise ValidationError(str(e))
-8
View File
@@ -376,14 +376,6 @@ def base_placeholders(sender, **kwargs):
SimpleFunctionalMailTextPlaceholder(
'currency', ['event'], lambda event: event.currency, lambda event: event.currency
),
SimpleFunctionalMailTextPlaceholder(
'order_email', ['order'], lambda order: order.email, 'john@example.org'
),
SimpleFunctionalMailTextPlaceholder(
'invoice_number', ['invoice'],
lambda invoice: invoice.full_invoice_no,
f'{sender.settings.invoice_numbers_prefix or (sender.slug.upper() + "-")}00000'
),
SimpleFunctionalMailTextPlaceholder(
'refund_amount', ['event_or_subevent', 'refund_amount'],
lambda event_or_subevent, refund_amount: LazyCurrencyNumber(refund_amount, event_or_subevent.currency),
+1 -1
View File
@@ -919,7 +919,7 @@ class BaseInvoiceAddressForm(forms.ModelForm):
class Meta:
model = InvoiceAddress
fields = ('is_business', 'company', 'name_parts', 'street', 'zipcode', 'city', 'country', 'state',
'vat_id', 'internal_reference', 'beneficiary', 'custom_field', 'invoice_email')
'vat_id', 'internal_reference', 'beneficiary', 'custom_field')
widgets = {
'is_business': BusinessBooleanRadio,
'street': forms.Textarea(attrs={
@@ -103,8 +103,6 @@ class Command(BaseCommand):
with language(locale), override(timezone):
for receiver, response in signal_result:
if not response:
return None
ex = response(e, o, report_status)
if ex.identifier == options['export_provider']:
params = json.loads(options.get('parameters') or '{}')
@@ -79,9 +79,9 @@ class Command(BaseCommand):
if settings.SENTRY_ENABLED:
from sentry_sdk import capture_exception
capture_exception(err)
self.stdout.write(self.style.ERROR(f'ERROR {name}: {str(err)}\n'))
self.stdout.write(self.style.ERROR(f'ERROR runperiodic {str(err)}\n'))
else:
self.stdout.write(self.style.ERROR(f'ERROR {name}: {str(err)}\n'))
self.stdout.write(self.style.ERROR(f'ERROR runperiodic {str(err)}\n'))
traceback.print_exc()
else:
if options.get('verbosity') > 1:
@@ -0,0 +1,23 @@
# Generated by Django 3.2.2 on 2022-10-19 09:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0222_alter_question_unique_together'),
]
operations = [
migrations.AddField(
model_name='transaction',
name='source_identifier',
field=models.CharField(db_index=True, max_length=190, null=True),
),
migrations.AddField(
model_name='transaction',
name='source_type',
field=models.CharField(db_index=True, max_length=190, null=True),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 3.2.12 on 2022-10-12 09:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0222_alter_question_unique_together'),
]
operations = [
migrations.AddField(
model_name='voucher',
name='min_usages',
field=models.PositiveIntegerField(default=1),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 3.2.16 on 2022-11-15 10:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0223_voucher_min_usages'),
]
operations = [
migrations.AddField(
model_name='invoiceaddress',
name='invoice_email',
field=models.EmailField(max_length=254, null=True),
),
]
+1 -1
View File
@@ -262,7 +262,7 @@ class Customer(LoggedModel):
) + '?id=' + self.identifier + '&token=' + token
mail(
self.email,
self.organizer.settings.mail_subject_customer_registration,
_('Activate your account at {organizer}').format(organizer=self.organizer.name),
self.organizer.settings.mail_text_customer_registration,
ctx,
locale=self.locale,
-1
View File
@@ -590,7 +590,6 @@ class Event(EventMixin, LoggedModel):
self.settings.event_list_type = 'calendar'
self.settings.invoice_email_attachment = True
self.settings.name_scheme = 'given_family'
self.settings.payment_banktransfer_invoice_immediately = True
@property
def social_image(self):
+40 -101
View File
@@ -564,30 +564,17 @@ class Order(LockModel, LoggedModel):
@cached_property
def user_cancel_fee(self):
fee = Decimal('0.00')
if self.status == Order.STATUS_PAID:
if self.event.settings.cancel_allow_user_paid_keep_fees:
fee += self.fees.filter(
fee_type__in=(OrderFee.FEE_TYPE_PAYMENT, OrderFee.FEE_TYPE_SHIPPING, OrderFee.FEE_TYPE_SERVICE,
OrderFee.FEE_TYPE_CANCELLATION)
).aggregate(
s=Sum('value')
)['s'] or 0
if self.event.settings.cancel_allow_user_paid_keep_percentage:
fee += self.event.settings.cancel_allow_user_paid_keep_percentage / Decimal('100.0') * (self.total - fee)
if self.event.settings.cancel_allow_user_paid_keep:
fee += self.event.settings.cancel_allow_user_paid_keep
else:
if self.event.settings.cancel_allow_user_unpaid_keep_fees:
fee += self.fees.filter(
fee_type__in=(OrderFee.FEE_TYPE_PAYMENT, OrderFee.FEE_TYPE_SHIPPING, OrderFee.FEE_TYPE_SERVICE,
OrderFee.FEE_TYPE_CANCELLATION)
).aggregate(
s=Sum('value')
)['s'] or 0
if self.event.settings.cancel_allow_user_unpaid_keep_percentage:
fee += self.event.settings.cancel_allow_user_unpaid_keep_percentage / Decimal('100.0') * (self.total - fee)
if self.event.settings.cancel_allow_user_unpaid_keep:
fee += self.event.settings.cancel_allow_user_unpaid_keep
if self.event.settings.cancel_allow_user_paid_keep_fees:
fee += self.fees.filter(
fee_type__in=(OrderFee.FEE_TYPE_PAYMENT, OrderFee.FEE_TYPE_SHIPPING, OrderFee.FEE_TYPE_SERVICE,
OrderFee.FEE_TYPE_CANCELLATION)
).aggregate(
s=Sum('value')
)['s'] or 0
if self.event.settings.cancel_allow_user_paid_keep_percentage:
fee += self.event.settings.cancel_allow_user_paid_keep_percentage / Decimal('100.0') * (self.total - fee)
if self.event.settings.cancel_allow_user_paid_keep:
fee += self.event.settings.cancel_allow_user_paid_keep
return round_decimal(min(fee, self.total), self.event.currency)
@property
@@ -655,12 +642,10 @@ class Order(LockModel, LoggedModel):
if self.user_cancel_deadline and now() > self.user_cancel_deadline:
return False
if self.status == Order.STATUS_PAID:
if self.status == Order.STATUS_PAID or self.payment_refund_sum > Decimal('0.00'):
if self.total == Decimal('0.00'):
return self.event.settings.cancel_allow_user
return self.event.settings.cancel_allow_user_paid
elif self.payment_refund_sum > Decimal('0.00'):
return False
elif self.status == Order.STATUS_PENDING:
return self.event.settings.cancel_allow_user
return False
@@ -1038,64 +1023,11 @@ class Order(LockModel, LoggedModel):
}
)
def send_invoice_to_alternate_email(self, invoice):
"""
Sends an email to the alternate invoice address.
"""
from pretix.base.services.mail import (
SendMailException, TolerantDict, mail, render_mail,
)
try:
recipient = self.invoice_address.invoice_email
except InvoiceAddress.DoesNotExist:
return
if not recipient:
return
with language(self.locale, self.event.settings.region):
context = get_email_context(event=self.event, order=self, invoice=invoice, event_or_subevent=self.event,
invoice_address=self.invoice_address)
template = self.event.settings.email_text_invoice_only
subject = self.event.settings.email_subject_invoice_only
try:
email_content = render_mail(template, context)
subject = subject.format_map(TolerantDict(context))
mail(
recipient,
subject,
template,
context=context,
event=self.event,
locale=self.locale,
order=self,
invoices=[invoice],
attach_tickets=False,
auto_email=True,
attach_ical=False,
)
except SendMailException:
raise
else:
self.log_action(
'pretix.event.order.email.order_invoice_only',
data={
'subject': subject,
'message': email_content,
'position': None,
'recipient': recipient,
'invoices': invoice.pk,
'attach_tickets': False,
'attach_ical': False,
}
)
def resend_link(self, user=None, auth=None):
with language(self.locale, self.event.settings.region):
email_template = self.event.settings.mail_text_resend_link
email_context = get_email_context(event=self.event, order=self)
email_subject = self.event.settings.mail_subject_resend_link
email_subject = _('Your order: %(code)s') % {'code': self.code}
self.send_mail(
email_subject, email_template, email_context,
'pretix.event.order.email.resend', user=user, auth=auth,
@@ -1109,10 +1041,13 @@ class Order(LockModel, LoggedModel):
continue
yield op
def create_transactions(self, is_new=False, positions=None, fees=None, dt_now=None, migrated=False,
_backfill_before_cancellation=False, save=True):
def create_transactions(self, *, source=None, is_new=False, positions=None, fees=None,
dt_now=None, migrated=False, _backfill_before_cancellation=False, save=True):
dt_now = dt_now or now()
if source is not None and (not isinstance(source, tuple) or len(source) != 2 or not all(isinstance(a, str) or a is None for a in source)):
return ValueError("source needs to be a 2-tuple of (source_type(str), source_identifier(str))")
# Count the transactions we already have
current_transaction_count = Counter()
if not is_new:
@@ -1157,6 +1092,8 @@ class Order(LockModel, LoggedModel):
tax_value=taxvalue,
fee_type=feetype,
internal_type=internaltype,
source_type=source[0] if source else None,
source_identifier=source[1] if source else None,
))
create.sort(key=lambda t: (0 if t.count < 0 else 1, t.positionid or 0))
if save:
@@ -1641,7 +1578,7 @@ class OrderPayment(models.Model):
return self.order.event.get_payment_providers(cached=True).get(self.provider)
@transaction.atomic()
def _mark_paid_inner(self, force, count_waitinglist, user, auth, ignore_date=False, overpaid=False):
def _mark_paid_inner(self, force, count_waitinglist, user, auth, ignore_date=False, overpaid=False, source=None):
from pretix.base.signals import order_paid
can_be_paid = self.order._can_be_paid(count_waitinglist=count_waitinglist, ignore_date=ignore_date, force=force)
if can_be_paid is not True:
@@ -1664,7 +1601,9 @@ class OrderPayment(models.Model):
self.order.log_action('pretix.event.order.overpaid', {}, user=user, auth=auth)
order_paid.send(self.order.event, order=self.order)
if status_change:
self.order.create_transactions()
self.order.create_transactions(
source=source or ('pretix.payment', None),
)
def fail(self, info=None, user=None, auth=None, log_data=None):
"""
@@ -1698,7 +1637,7 @@ class OrderPayment(models.Model):
}, user=user, auth=auth)
def confirm(self, count_waitinglist=True, send_mail=True, force=False, user=None, auth=None, mail_text='',
ignore_date=False, lock=True, payment_date=None):
ignore_date=False, lock=True, payment_date=None, source=None):
"""
Marks the payment as complete. If possible, this also marks the order as paid if no further
payment is required
@@ -1761,10 +1700,11 @@ class OrderPayment(models.Model):
))
return
self._mark_order_paid(count_waitinglist, send_mail, force, user, auth, mail_text, ignore_date, lock, payment_sum - refund_sum)
self._mark_order_paid(count_waitinglist, send_mail, force, user, auth, mail_text, ignore_date, lock, payment_sum - refund_sum,
source)
def _mark_order_paid(self, count_waitinglist=True, send_mail=True, force=False, user=None, auth=None, mail_text='',
ignore_date=False, lock=True, payment_refund_sum=0):
ignore_date=False, lock=True, payment_refund_sum=0, source=None):
from pretix.base.services.invoices import (
generate_invoice, invoice_qualified,
)
@@ -1778,7 +1718,7 @@ class OrderPayment(models.Model):
with lockfn():
self._mark_paid_inner(force, count_waitinglist, user, auth, overpaid=payment_refund_sum > self.order.total,
ignore_date=ignore_date)
ignore_date=ignore_date, source=source)
invoice = None
if invoice_qualified(self.order):
@@ -1806,8 +1746,8 @@ class OrderPayment(models.Model):
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)
email_subject = _('Event registration confirmed: %(code)s') % {'code': self.order.code}
try:
position.send_mail(
email_subject, email_template, email_context,
@@ -1824,8 +1764,8 @@ class OrderPayment(models.Model):
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)
email_subject = _('Payment received for your order: %(code)s') % {'code': self.order.code}
try:
self.order.send_mail(
email_subject, email_template, email_context,
@@ -2505,7 +2445,7 @@ class OrderPosition(AbstractPosition):
with language(self.order.locale, self.order.event.settings.region):
email_template = self.event.settings.mail_text_resend_link
email_context = get_email_context(event=self.order.event, order=self.order, position=self)
email_subject = self.event.settings.mail_subject_resend_link
email_subject = _('Your event registration: %(code)s') % {'code': self.order.code}
self.send_mail(
email_subject, email_template, email_context,
'pretix.event.order.email.resend', user=user, auth=auth,
@@ -2551,6 +2491,8 @@ class Transaction(models.Model):
:param id: ID of the transaction
:param order: Order the transaction belongs to
:param source_type: Functionality that caused the transaction to be created, usually the name of a module or plugin
:param source_identifier: Identifier of the entity that caused the transaction to be created, as defined by the module or plugin noted in ``source_type``.
:param datetime: Date and time of the transaction
:param migrated: Whether this object was reconstructed because the order was created before transactions where introduced
:param positionid: Affected Position ID, in case this transaction represents a change in an order position
@@ -2573,6 +2515,12 @@ class Transaction(models.Model):
related_name='transactions',
on_delete=models.PROTECT
)
source_type = models.CharField(
max_length=190, db_index=True, null=True, blank=True
)
source_identifier = models.CharField(
max_length=190, db_index=True, null=True, blank=True
)
created = models.DateTimeField(
auto_now_add=True,
db_index=True,
@@ -2834,14 +2782,6 @@ class InvoiceAddress(models.Model):
verbose_name=_('Beneficiary'),
blank=True
)
invoice_email = models.EmailField(
verbose_name=_('Send invoice to'),
help_text=_('If your accounting department requires that we send them the invoice directly, you can enter '
'their email address here. They will receive a separate email. You will still receive the invoice '
'and booking confirmation as well.'),
blank=True,
null=True,
)
objects = ScopedManager(organizer='order__event__organizer')
profiles = ScopedManager(organizer='customer__organizer')
@@ -2926,7 +2866,6 @@ class InvoiceAddress(models.Model):
'vat_id': self.vat_id,
'custom_field': self.custom_field,
'internal_reference': self.internal_reference,
'invoice_email': self.invoice_email,
'beneficiary': self.beneficiary,
})
return d
+1 -19
View File
@@ -137,8 +137,6 @@ class Voucher(LoggedModel):
:type max_usages: int
:param redeemed: The number of times this voucher already has been redeemed
:type redeemed: int
:param min_usages: The minimum number of times this voucher must be redeemed
:type min_usages: int
:param valid_until: The expiration date of this voucher (optional)
:type valid_until: datetime
:param block_quota: If set to true, this voucher will reserve quota for its holder
@@ -201,14 +199,6 @@ class Voucher(LoggedModel):
verbose_name=_("Redeemed"),
default=0
)
min_usages = models.PositiveIntegerField(
verbose_name=_("Minimum usages"),
help_text=_("If set to more than one, the voucher must be redeemed for this many products when it is used for "
"the first time. On later usages, it can also be used for lower numbers of products. Note that "
"this means that the total number of usages in some cases can be lower than this limit, e.g. in "
"case of cancellations."),
default=1
)
budget = models.DecimalField(
verbose_name=_("Maximum discount budget"),
help_text=_("This is the maximum monetary amount that will be discounted using this voucher across all usages. "
@@ -360,10 +350,6 @@ class Voucher(LoggedModel):
'redeemed': redeemed
}
)
if data.get('max_usages', 1) < data.get('min_usages', 1):
raise ValidationError(
_('The maximum number of usages may not be lower than the minimum number of usages.'),
)
@staticmethod
def clean_subevent(data, event):
@@ -478,7 +464,7 @@ class Voucher(LoggedModel):
if quota:
raise ValidationError(_('You need to choose a specific product if you select a seat.'))
if data.get('max_usages', 1) > 1 or data.get('min_usages', 1) > 1:
if data.get('max_usages', 1) > 1:
raise ValidationError(_('Seat-specific vouchers can only be used once.'))
if item and seat.product != item:
@@ -581,10 +567,6 @@ class Voucher(LoggedModel):
else:
return bool(subevent.seating_plan) if subevent else self.event.seating_plan
@property
def min_usages_remaining(self):
return max(1, self.min_usages - self.redeemed)
@classmethod
def annotate_budget_used_orders(cls, qs):
opq = OrderPosition.objects.filter(
+1 -1
View File
@@ -216,7 +216,7 @@ class WaitingListEntry(LoggedModel):
with language(self.locale, self.event.settings.region):
mail(
self.email,
self.event.settings.mail_subject_waiting_list,
_('You have been selected from the waitinglist for {event}').format(event=str(self.event)),
self.event.settings.mail_text_waiting_list,
get_email_context(event=self.event, waiting_list_entry=self),
self.event,
+1 -11
View File
@@ -40,7 +40,6 @@ import os
import re
import subprocess
import tempfile
import unicodedata
import uuid
from collections import OrderedDict
from functools import partial
@@ -828,13 +827,6 @@ class Renderer:
if o['italic']:
font += ' I'
try:
ad = getAscentDescent(font, float(o['fontsize']))
except KeyError: # font not known, fall back
logger.warning(f'Use of unknown font "{font}"')
font = 'Open Sans'
ad = getAscentDescent(font, float(o['fontsize']))
align_map = {
'left': TA_LEFT,
'center': TA_CENTER,
@@ -861,12 +853,10 @@ class Renderer:
except:
logger.exception('Reshaping/Bidi fixes failed on string {}'.format(repr(text)))
# reportlab does not support unicode combination characters
text = unicodedata.normalize("NFKC", text)
p = Paragraph(text, style=style)
w, h = p.wrapOn(canvas, float(o['width']) * mm, 1000 * mm)
# p_size = p.wrap(float(o['width']) * mm, 1000 * mm)
ad = getAscentDescent(font, float(o['fontsize']))
canvas.saveState()
# The ascent/descent offsets here are not really proven to be correct, they're just empirical values to get
# reportlab render similarly to browser canvas.
+2 -1
View File
@@ -210,7 +210,8 @@ def cancel_event(self, event: Event, subevent: int, auto_refund: bool,
fee += min(p.price, Decimal(keep_fee_per_ticket))
fee = round_decimal(min(fee, o.payment_refund_sum), event.currency)
_cancel_order(o.pk, user, send_mail=False, cancellation_fee=fee, keep_fees=keep_fee_objects)
_cancel_order(o.pk, user, send_mail=False, cancellation_fee=fee, keep_fees=keep_fee_objects,
source=("pretix.cancelevent", None))
refund_amount = o.payment_refund_sum
try:
+2 -78
View File
@@ -110,11 +110,6 @@ error_messages = {
'positions have been removed from your cart.'),
'price_too_high': _('The entered price is to high.'),
'voucher_invalid': _('This voucher code is not known in our database.'),
'voucher_min_usages': _('The voucher code "%(voucher)s" can only be used if you select at least %(number)s '
'matching products.'),
'voucher_min_usages_removed': _('The voucher code "%(voucher)s" can only be used if you select at least '
'%(number)s matching products. We have therefore removed some positions from '
'your cart that can no longer be purchased like this.'),
'voucher_redeemed': _('This voucher code has already been used the maximum number of times allowed.'),
'voucher_redeemed_cart': _('This voucher code is currently locked since it is already contained in a cart. This '
'might mean that someone else is redeeming this voucher right now, or that you tried '
@@ -195,7 +190,7 @@ class CartManager:
AddOperation = namedtuple('AddOperation', ('count', 'item', 'variation', 'voucher', 'quotas',
'addon_to', 'subevent', 'bundled', 'seat', 'listed_price',
'price_after_voucher', 'custom_price_input',
'custom_price_input_is_net', 'voucher_ignored'))
'custom_price_input_is_net'))
RemoveOperation = namedtuple('RemoveOperation', ('position',))
VoucherOperation = namedtuple('VoucherOperation', ('position', 'voucher', 'price_after_voucher'))
ExtendOperation = namedtuple('ExtendOperation', ('position', 'count', 'item', 'variation', 'voucher',
@@ -330,16 +325,12 @@ class CartManager:
(isinstance(op, self.ExtendOperation) and op.position.is_bundled)
):
if op.item.require_voucher and op.voucher is None:
if getattr(op, 'voucher_ignored', False):
raise CartError(error_messages['voucher_redeemed'])
raise CartError(error_messages['voucher_required'])
if (
(op.item.hide_without_voucher or (op.variation and op.variation.hide_without_voucher)) and
(op.voucher is None or not op.voucher.show_hidden_items)
):
if getattr(op, 'voucher_ignored', False):
raise CartError(error_messages['voucher_redeemed'])
raise CartError(error_messages['voucher_required'])
if not op.item.is_available() or (op.variation and not op.variation.is_available()):
@@ -484,7 +475,7 @@ class CartManager:
self._check_item_constraints(op)
if cp.voucher:
self._voucher_use_diff[cp.voucher] += 2
self._voucher_use_diff[cp.voucher] += 1
self._operations.append(op)
return err
@@ -533,15 +524,6 @@ class CartManager:
voucher_use_diff[voucher] += 1
ops.append((listed_price - price_after_voucher, self.VoucherOperation(p, voucher, price_after_voucher)))
for voucher, cnt in list(voucher_use_diff.items()):
if 0 < cnt < voucher.min_usages_remaining:
raise CartError(
_(error_messages['voucher_min_usages']) % {
'voucher': voucher.code,
'number': voucher.min_usages_remaining,
}
)
# If there are not enough voucher usages left for the full cart, let's apply them in the order that benefits
# the user the most.
ops.sort(key=lambda k: k[0], reverse=True)
@@ -590,7 +572,6 @@ class CartManager:
item = self._items_cache[i['item']]
variation = self._variations_cache[i['variation']] if i['variation'] is not None else None
voucher = None
voucher_ignored = False
if i.get('voucher'):
try:
@@ -600,24 +581,6 @@ class CartManager:
else:
voucher_use_diff[voucher] += i['count']
if i.get('voucher_ignore_if_redeemed', False):
# This is a special case handling for when a user clicks "+" on an existing line in their cart
# that has a voucher attached. If the voucher still has redemptions left, we'll add another line
# with the same voucher, but if it does not we silently continue as if there was no voucher,
# leading to either a higher-priced ticket or an error. Still, this leads to less error cases
# than either of the possible default assumptions.
predicted_redeemed_after = (
voucher.redeemed +
CartPosition.objects.filter(voucher=voucher, expires__gte=self.now_dt).count() +
self._voucher_use_diff[voucher] +
voucher_use_diff[voucher]
)
if predicted_redeemed_after > voucher.max_usages:
i.pop('voucher')
voucher_ignored = True
voucher = None
voucher_use_diff[voucher] -= i['count']
# Fetch all quotas. If there are no quotas, this item is not allowed to be sold.
quotas = list(item.quotas.filter(subevent=subevent)
if variation is None else variation.quotas.filter(subevent=subevent))
@@ -664,7 +627,6 @@ class CartManager:
price_after_voucher=bundle.designated_price,
custom_price_input=None,
custom_price_input_is_net=False,
voucher_ignored=False,
)
self._check_item_constraints(bop, operations)
bundled.append(bop)
@@ -694,7 +656,6 @@ class CartManager:
price_after_voucher=price_after_voucher,
custom_price_input=custom_price,
custom_price_input_is_net=self.event.settings.display_net_prices,
voucher_ignored=voucher_ignored,
)
self._check_item_constraints(op, operations)
operations.append(op)
@@ -826,7 +787,6 @@ class CartManager:
price_after_voucher=listed_price,
custom_price_input=custom_price,
custom_price_input_is_net=self.event.settings.display_net_prices,
voucher_ignored=False,
)
self._check_item_constraints(op, operations)
operations.append(op)
@@ -955,41 +915,6 @@ class CartManager:
)
return err
def _check_min_per_voucher(self):
vouchers = Counter()
for p in self.positions:
vouchers[p.voucher] += 1
for op in self._operations:
if isinstance(op, self.AddOperation):
vouchers[op.voucher] += op.count
elif isinstance(op, self.RemoveOperation):
vouchers[op.position.voucher] -= 1
err = None
for voucher, count in vouchers.items():
if not voucher or count == 0:
continue
if count < voucher.min_usages_remaining:
self._operations = [o for o in self._operations if not (
isinstance(o, self.AddOperation) and o.voucher and o.voucher.pk == voucher.pk
)]
removals = [o.position.pk for o in self._operations if isinstance(o, self.RemoveOperation)]
for p in self.positions:
if p.voucher_id == voucher.pk and p.pk not in removals:
self._operations.append(self.RemoveOperation(position=p))
err = _(error_messages['voucher_min_usages_removed']) % {
'voucher': voucher.code,
'number': voucher.min_usages_remaining,
}
if not err:
raise CartError(
_(error_messages['voucher_min_usages']) % {
'voucher': voucher.code,
'number': voucher.min_usages_remaining,
}
)
return err
def _perform_operations(self):
vouchers_ok = self._get_voucher_availability()
quotas_ok = _get_quota_availability(self._quota_diff, self.now_dt)
@@ -1246,7 +1171,6 @@ class CartManager:
err = self._delete_out_of_timeframe()
err = self.extend_expired_positions() or err
err = err or self._check_min_per_voucher()
lockfn = NoLockManager
if self._require_locking():
+4 -17
View File
@@ -379,18 +379,13 @@ def invoice_pdf_task(invoice: int):
with scope(organizer=i.order.event.organizer):
if i.shredded:
return None
to_delete = i.file.name
with transaction.atomic(), language(i.locale, i.event.settings.region):
if i.file:
i.file.delete()
with language(i.locale, i.event.settings.region):
fname, ftype, fcontent = i.event.invoice_renderer.generate(i)
i.file.save(fname, ContentFile(fcontent), save=False)
i.save(update_fields=['file'])
try:
i.file.storage.delete(to_delete)
except:
logger.exception('Could not delete previous PDF file for invoice')
return i.file.name
return i.file.name
def invoice_qualified(order: Order):
@@ -489,14 +484,6 @@ def fetch_ecb_rates(sender, **kwargs):
logger.exception('Could not retrieve rates from ECB')
@app.task(base=TransactionAwareTask)
def send_invoice_to_customer_alternative_email(invoice_id):
with scopes_disabled():
i = Invoice.objects.get(pk=invoice_id)
with scope(organizer=i.order.event.organizer):
i.order.send_invoice_to_alternate_email(i)
@receiver(signal=periodic_task)
@scopes_disabled()
def send_invoices_to_organizer(sender, **kwargs):
+1 -1
View File
@@ -163,7 +163,7 @@ def lock_event_redis(event):
retries = 5
for i in range(retries):
try:
if lock.acquire(blocking=False):
if lock.acquire(False):
return True
except RedisError:
logger.exception('Error locking an event')
+8 -14
View File
@@ -63,7 +63,6 @@ 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
from pretix.base.email import ClassicMailRenderer
from pretix.base.i18n import language
@@ -98,8 +97,7 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
context: Dict[str, Any] = None, event: Event = None, locale: str = None, order: Order = None,
position: OrderPosition = None, *, headers: dict = None, sender: str = None, organizer: Organizer = None,
customer: Customer = None, invoices: Sequence = None, attach_tickets=False, auto_email=True, user=None,
attach_ical=False, attach_cached_files: Sequence = None, attach_other_files: list=None,
plain_text_without_links=False):
attach_ical=False, attach_cached_files: Sequence = None, attach_other_files: list=None):
"""
Sends out an email to a user. The mail will be sent synchronously or asynchronously depending on the installation.
@@ -198,7 +196,7 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
else:
sender = formataddr((settings.PRETIX_INSTANCE_NAME, sender))
subject = raw_subject = str(subject).replace('\n', ' ').replace('\r', '')[:900]
subject = raw_subject = str(subject)
signature = ""
bcc = []
@@ -243,7 +241,7 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
if order and order.testmode:
subject = "[TESTMODE] " + subject
if order and position and not plain_text_without_links:
if order and position:
body_plain += _(
"You are receiving this email because someone placed an order for {event} for you."
).format(event=event.name)
@@ -259,7 +257,7 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
}
)
)
elif order and not plain_text_without_links:
elif order:
body_plain += _(
"You are receiving this email because you placed an order for {event}."
).format(event=event.name)
@@ -279,9 +277,7 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
with override(timezone):
try:
if plain_text_without_links:
body_html = None
elif 'position' in inspect.signature(renderer.render).parameters:
if 'position' in inspect.signature(renderer.render).parameters:
body_html = renderer.render(content_plain, signature, raw_subject, order, position)
else:
# Backwards compatibility
@@ -435,9 +431,8 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
}
)
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.attach('event-{}.ics'.format(i), cal.serialize(), 'text/calendar')
email = email_filter.send_chained(event, 'message', message=email, order=order, user=user)
@@ -599,9 +594,8 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
raise SendMailException('Failed to send an email to {}.'.format(to))
else:
for i in invoices_sent:
if not i.order.invoice_address.invoice_email or i.order.invoice_address.invoice_email in to:
i.sent_to_customer = now()
i.save(update_fields=['sent_to_customer'])
i.sent_to_customer = now()
i.save(update_fields=['sent_to_customer'])
def mail_send(*args, **kwargs):
+2 -1
View File
@@ -195,7 +195,8 @@ def import_orders(event: Event, fileid: str, settings: dict, locale: str, user)
user=user,
data={'source': 'import'}
)
save_transactions += o.create_transactions(is_new=True, fees=[], positions=o._positions, save=False)
save_transactions += o.create_transactions(is_new=True, fees=[], positions=o._positions, save=False,
source=('pretix.orderimport', None))
Transaction.objects.bulk_create(save_transactions)
for o in orders:
+55 -88
View File
@@ -54,7 +54,7 @@ from django.db.transaction import get_connection
from django.dispatch import receiver
from django.utils.functional import cached_property
from django.utils.timezone import make_aware, now
from django.utils.translation import gettext as _
from django.utils.translation import gettext as _, gettext_lazy
from django_scopes import scopes_disabled
from pretix.api.models import OAuthApplication
@@ -80,7 +80,6 @@ from pretix.base.secrets import assign_ticket_secret
from pretix.base.services import tickets
from pretix.base.services.invoices import (
generate_cancellation, generate_invoice, invoice_qualified,
send_invoice_to_customer_alternative_email,
)
from pretix.base.services.locking import LockTimeoutException, NoLockManager
from pretix.base.services.mail import SendMailException
@@ -116,8 +115,6 @@ error_messages = {
'server was too busy. Please try again.'),
'not_started': _('The booking period for this event has not yet started.'),
'ended': _('The booking period has ended.'),
'voucher_min_usages': _('The voucher code "%(voucher)s" can only be used if you select at least %(number)s '
'matching products.'),
'voucher_invalid': _('The voucher code used for one of the items in your cart is not known in our database.'),
'voucher_redeemed': _('The voucher code used for one of the items in your cart has already been used the maximum '
'number of times allowed. We removed this item from your cart.'),
@@ -151,7 +148,7 @@ def mark_order_paid(*args, **kwargs):
raise NotImplementedError("This method is no longer supported since pretix 1.17.")
def reactivate_order(order: Order, force: bool=False, user: User=None, auth=None):
def reactivate_order(order: Order, force: bool=False, user: User=None, auth=None, source=None):
"""
Reactivates a canceled order. If ``force`` is not set to ``True``, this will fail if there is not
enough quota.
@@ -192,7 +189,7 @@ def reactivate_order(order: Order, force: bool=False, user: User=None, auth=None
for m in position.granted_memberships.all():
m.canceled = False
m.save()
order.create_transactions()
order.create_transactions(source=source)
else:
raise OrderError(is_available)
@@ -205,7 +202,7 @@ def reactivate_order(order: Order, force: bool=False, user: User=None, auth=None
generate_invoice(order)
def extend_order(order: Order, new_date: datetime, force: bool=False, user: User=None, auth=None):
def extend_order(order: Order, new_date: datetime, force: bool=False, user: User=None, auth=None, source=None):
"""
Extends the deadline of an order. If the order is already expired, the quota will be checked to
see if this is actually still possible. If ``force`` is set to ``True``, the result of this check
@@ -234,7 +231,7 @@ def extend_order(order: Order, new_date: datetime, force: bool=False, user: User
num_invoices = order.invoices.filter(is_cancellation=False).count()
if num_invoices > 0 and order.invoices.filter(is_cancellation=True).count() >= num_invoices and invoice_qualified(order):
generate_invoice(order)
order.create_transactions()
order.create_transactions(source=source)
if order.status == Order.STATUS_PENDING:
change(was_expired=False)
@@ -248,16 +245,17 @@ def extend_order(order: Order, new_date: datetime, force: bool=False, user: User
@transaction.atomic
def mark_order_refunded(order, user=None, auth=None, api_token=None):
def mark_order_refunded(order, user=None, auth=None, api_token=None, source=None):
oautha = auth.pk if isinstance(auth, OAuthApplication) else None
device = auth.pk if isinstance(auth, Device) else None
api_token = (api_token.pk if api_token else None) or (auth if isinstance(auth, TeamAPIToken) else None)
return _cancel_order(
order.pk, user.pk if user else None, send_mail=False, api_token=api_token, device=device, oauth_application=oautha
order.pk, user.pk if user else None, send_mail=False, api_token=api_token, device=device, oauth_application=oautha,
source=source
)
def mark_order_expired(order, user=None, auth=None):
def mark_order_expired(order, user=None, auth=None, source=None):
"""
Mark this order as expired. This sets the payment status and returns the order object.
:param order: The order to change
@@ -276,13 +274,13 @@ def mark_order_expired(order, user=None, auth=None):
i = order.invoices.filter(is_cancellation=False).last()
if i and not i.refered.exists():
generate_cancellation(i)
order.create_transactions()
order.create_transactions(source=source)
order_expired.send(order.event, order=order)
return order
def approve_order(order, user=None, send_mail: bool=True, auth=None, force=False):
def approve_order(order, user=None, send_mail: bool=True, auth=None, force=False, source=None):
"""
Mark this order as approved
:param order: The order to change
@@ -295,7 +293,7 @@ def approve_order(order, user=None, send_mail: bool=True, auth=None, force=False
order.require_approval = False
order.set_expires(now(), order.event.subevents.filter(id__in=[p.subevent_id for p in order.positions.all()]))
order.save(update_fields=['require_approval', 'expires'])
order.create_transactions()
order.create_transactions(source=source)
order.log_action('pretix.event.order.approved', user=user, auth=auth)
if order.total == Decimal('0.00'):
@@ -325,10 +323,10 @@ def approve_order(order, user=None, send_mail: bool=True, auth=None, force=False
with language(order.locale, order.event.settings.region):
if order.total == Decimal('0.00'):
email_template = order.event.settings.mail_text_order_approved_free
email_subject = order.event.settings.mail_subject_order_approved_free
email_subject = _('Order approved and confirmed: %(code)s') % {'code': order.code}
else:
email_template = order.event.settings.mail_text_order_approved
email_subject = order.event.settings.mail_subject_order_approved
email_subject = _('Order approved and awaiting payment: %(code)s') % {'code': order.code}
email_context = get_email_context(event=order.event, order=order)
try:
@@ -344,7 +342,7 @@ def approve_order(order, user=None, send_mail: bool=True, auth=None, force=False
return order.pk
def deny_order(order, comment='', user=None, send_mail: bool=True, auth=None):
def deny_order(order, comment='', user=None, send_mail: bool=True, auth=None, source=None):
"""
Mark this order as canceled
:param order: The order to change
@@ -368,15 +366,15 @@ def deny_order(order, comment='', user=None, send_mail: bool=True, auth=None):
for position in order.positions.all():
if position.voucher:
Voucher.objects.filter(pk=position.voucher.pk).update(redeemed=Greatest(0, F('redeemed') - 1))
order.create_transactions()
order.create_transactions(source=source)
order_denied.send(order.event, order=order)
if send_mail:
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)
with language(order.locale, order.event.settings.region):
email_subject = _('Order denied: %(code)s') % {'code': order.code}
try:
order.send_mail(
email_subject, email_template, email_context,
@@ -389,7 +387,7 @@ def deny_order(order, comment='', user=None, send_mail: bool=True, auth=None):
def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device=None, oauth_application=None,
cancellation_fee=None, keep_fees=None, cancel_invoice=True, comment=None):
cancellation_fee=None, keep_fees=None, cancel_invoice=True, comment=None, source=None):
"""
Mark this order as canceled
:param order: The order to change
@@ -463,13 +461,9 @@ def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device
f._calculate_tax()
f.save()
if cancellation_fee > order.total:
raise OrderError(_('The cancellation fee cannot be higher than the total amount of this order.'))
elif order.payment_refund_sum < cancellation_fee:
order.status = Order.STATUS_PENDING
order.set_expires()
else:
order.status = Order.STATUS_PAID
if order.payment_refund_sum < cancellation_fee:
raise OrderError(_('The cancellation fee cannot be higher than the payment credit of this order.'))
order.status = Order.STATUS_PAID
order.total = cancellation_fee
order.cancellation_date = now()
order.save(update_fields=['status', 'cancellation_date', 'total'])
@@ -493,13 +487,13 @@ def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device
data={'cancellation_fee': cancellation_fee, 'comment': comment})
order.cancellation_requests.all().delete()
order.create_transactions()
order.create_transactions(source=source)
if send_mail:
email_template = order.event.settings.mail_text_order_canceled
with language(order.locale, order.event.settings.region):
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 "")
email_subject = _('Order canceled: %(code)s') % {'code': order.code}
try:
order.send_mail(
email_subject, email_template, email_context,
@@ -576,7 +570,6 @@ def _check_positions(event: Event, now_dt: datetime, positions: List[CartPositio
products_seen = Counter()
q_avail = Counter()
v_avail = Counter()
v_usages = Counter()
v_budget = {}
deleted_positions = set()
seats_seen = set()
@@ -614,7 +607,6 @@ def _check_positions(event: Event, now_dt: datetime, positions: List[CartPositio
break
if cp.voucher:
v_usages[cp.voucher] += 1
if cp.voucher not in v_avail:
redeemed_in_carts = CartPosition.objects.filter(
Q(voucher=cp.voucher) & Q(event=event) & Q(expires__gte=now_dt)
@@ -726,13 +718,6 @@ def _check_positions(event: Event, now_dt: datetime, positions: List[CartPositio
# Sorry, can't let you keep that!
delete(cp)
for voucher, cnt in v_usages.items():
if 0 < cnt < voucher.min_usages_remaining:
raise OrderError(error_messages['voucher_min_usages'], {
'voucher': voucher.code,
'number': voucher.min_usages_remaining,
})
# Check prices
sorted_positions = [cp for cp in sorted_positions if cp.pk and cp.pk not in deleted_positions]
old_total = sum(cp.price for cp in sorted_positions)
@@ -829,7 +814,7 @@ def _get_fees(positions: List[CartPosition], payment_provider: BasePaymentProvid
def _create_order(event: Event, email: str, positions: List[CartPosition], now_dt: datetime,
payment_provider: BasePaymentProvider, locale: str=None, address: InvoiceAddress=None,
meta_info: dict=None, sales_channel: str='web', gift_cards: list=None, shown_total=None,
customer=None):
customer=None, source=None):
p = None
sales_channel = get_all_sales_channels()[sales_channel]
@@ -931,7 +916,7 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d
)
orderpositions = OrderPosition.transform_cart_positions(positions, order)
order.create_transactions(positions=orderpositions, fees=fees, is_new=True)
order.create_transactions(positions=orderpositions, fees=fees, is_new=True, source=source, dt_now=now_dt)
order.log_action('pretix.event.order.placed')
if order.require_approval:
order.log_action('pretix.event.order.placed.require_approval')
@@ -943,12 +928,13 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d
return order, p
def _order_placed_email(event: Event, order: Order, pprov: BasePaymentProvider, email_template, subject_template,
log_entry: str, invoice, payment: OrderPayment, is_free=False):
def _order_placed_email(event: Event, order: Order, pprov: BasePaymentProvider, email_template, log_entry: str,
invoice, payment: OrderPayment, is_free=False):
email_context = get_email_context(event=event, order=order, payment=payment if pprov else None)
email_subject = gettext_lazy('Your order: {code}')
try:
order.send_mail(
subject_template, email_template, email_context,
email_subject, email_template, email_context,
log_entry,
invoices=[invoice] if invoice and event.settings.invoice_email_attachment else [],
attach_tickets=True,
@@ -961,13 +947,13 @@ def _order_placed_email(event: Event, order: Order, pprov: BasePaymentProvider,
logger.exception('Order received email could not be sent')
def _order_placed_email_attendee(event: Event, order: Order, position: OrderPosition, email_template, subject_template,
log_entry: str, is_free=False):
def _order_placed_email_attendee(event: Event, order: Order, position: OrderPosition, email_template, log_entry: str, is_free=False):
email_context = get_email_context(event=event, order=order, position=position)
email_subject = gettext_lazy('Your event registration: {code}')
try:
position.send_mail(
subject_template, email_template, email_context,
email_subject, email_template, email_context,
log_entry,
invoices=[],
attach_tickets=True,
@@ -982,7 +968,7 @@ def _order_placed_email_attendee(event: Event, order: Order, position: OrderPosi
def _perform_order(event: Event, payment_provider: str, position_ids: List[str],
email: str, locale: str, address: int, meta_info: dict=None, sales_channel: str='web',
gift_cards: list=None, shown_total=None, customer=None):
gift_cards: list=None, shown_total=None, customer=None, source=None):
if payment_provider:
pprov = event.get_payment_providers().get(payment_provider)
if not pprov:
@@ -1041,7 +1027,7 @@ def _perform_order(event: Event, payment_provider: str, position_ids: List[str],
_check_positions(event, now_dt, positions, address=addr, sales_channel=sales_channel, customer=customer)
order, payment = _create_order(event, email, positions, now_dt, pprov,
locale=locale, address=addr, meta_info=meta_info, sales_channel=sales_channel,
gift_cards=gift_cards, shown_total=shown_total, customer=customer)
gift_cards=gift_cards, shown_total=shown_total, customer=customer, source=source)
free_order_flow = payment and payment_provider == 'free' and order.pending_sum == Decimal('0.00') and not order.require_approval
if free_order_flow:
@@ -1063,42 +1049,30 @@ def _perform_order(event: Event, payment_provider: str, position_ids: List[str],
if order.email:
if order.require_approval:
email_template = event.settings.mail_text_order_placed_require_approval
subject_template = event.settings.mail_subject_order_placed_require_approval
log_entry = 'pretix.event.order.email.order_placed_require_approval'
email_attendees = False
elif free_order_flow:
email_template = event.settings.mail_text_order_free
subject_template = event.settings.mail_subject_order_free
log_entry = 'pretix.event.order.email.order_free'
email_attendees = event.settings.mail_send_order_free_attendee
email_attendees_template = event.settings.mail_text_order_free_attendee
subject_attendees_template = event.settings.mail_subject_order_free_attendee
else:
email_template = event.settings.mail_text_order_placed
subject_template = event.settings.mail_subject_order_placed
log_entry = 'pretix.event.order.email.order_placed'
email_attendees = event.settings.mail_send_order_placed_attendee
email_attendees_template = event.settings.mail_text_order_placed_attendee
subject_attendees_template = event.settings.mail_subject_order_placed_attendee
if sales_channel in event.settings.mail_sales_channel_placed_paid:
_order_placed_email(event, order, pprov, email_template, subject_template, log_entry, invoice, payment,
_order_placed_email(event, order, pprov, email_template, log_entry, invoice, payment,
is_free=free_order_flow)
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:
_order_placed_email_attendee(event, order, p, email_attendees_template, subject_attendees_template, log_entry,
_order_placed_email_attendee(event, order, p, email_attendees_template, log_entry,
is_free=free_order_flow)
try:
if invoice and order.invoice_address.invoice_email:
# We fire this task with a little delay since both mail() calls will call invoice_pdf_task and this
# will reduce the chance of the PDF renderer running twice except under exceptional load patterns.
send_invoice_to_customer_alternative_email.apply_async(args=(invoice.pk,), countdown=60)
except InvoiceAddress.DoesNotExist:
pass
return order.id
@@ -1109,21 +1083,13 @@ def expire_orders(sender, **kwargs):
event_id = None
expire = None
qs = Order.objects.filter(
expires__lt=now(),
status=Order.STATUS_PENDING,
require_approval=False
).exclude(
Exists(
OrderFee.objects.filter(order_id=OuterRef('pk'), fee_type=OrderFee.FEE_TYPE_CANCELLATION)
)
).select_related('event').order_by('event_id')
for o in qs:
for o in Order.objects.filter(expires__lt=now(), status=Order.STATUS_PENDING,
require_approval=False).select_related('event').order_by('event_id'):
if o.event_id != event_id:
expire = o.event.settings.get('payment_term_expire_automatically', as_type=bool)
event_id = o.event_id
if expire:
mark_order_expired(o)
mark_order_expired(o, source=("pretix.periodic", None))
@receiver(signal=periodic_task)
@@ -1159,9 +1125,9 @@ def send_expiry_warnings(sender, **kwargs):
email_template = settings.mail_text_order_expire_warning
email_context = get_email_context(event=o.event, order=o)
if settings.payment_term_expire_automatically:
email_subject = settings.mail_subject_order_expire_warning
email_subject = _('Your order is about to expire: %(code)s') % {'code': o.code}
else:
email_subject = settings.mail_subject_order_pending_warning
email_subject = _('Your order is pending payment: %(code)s') % {'code': o.code}
try:
o.send_mail(
@@ -1234,8 +1200,8 @@ def send_download_reminders(sender, **kwargs):
o.download_reminder_sent = True
o.save(update_fields=['download_reminder_sent'])
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)
email_subject = _('Your ticket is ready for download: %(code)s') % {'code': o.code}
try:
o.send_mail(
email_subject, email_template, email_context,
@@ -1258,7 +1224,6 @@ def send_download_reminders(sender, **kwargs):
continue
if p.addon_to_id is None and p.attendee_email and p.attendee_email != o.email:
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(
@@ -1274,7 +1239,7 @@ def notify_user_changed_order(order, user=None, auth=None, invoices=[]):
with language(order.locale, order.event.settings.region):
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
email_subject = _('Your order has been changed: %(code)s') % {'code': order.code}
try:
order.send_mail(
email_subject, email_template, email_context,
@@ -1317,7 +1282,7 @@ class OrderChangeManager:
CancelFeeOperation = namedtuple('CancelFeeOperation', ('fee', 'price_diff'))
RegenerateSecretOperation = namedtuple('RegenerateSecretOperation', ('position',))
def __init__(self, order: Order, user=None, auth=None, notify=True, reissue_invoice=True):
def __init__(self, order: Order, user=None, auth=None, notify=True, reissue_invoice=True, source=None):
self.order = order
self.user = user
self.auth = auth
@@ -1332,6 +1297,7 @@ class OrderChangeManager:
self.notify = notify
self._invoice_dirty = False
self._invoices = []
self.source = source
def change_item(self, position: OrderPosition, item: Item, variation: Optional[ItemVariation]):
if (not variation and item.has_variations) or (variation and variation.item_id != item.pk):
@@ -2367,9 +2333,9 @@ class OrderChangeManager:
self._reissue_invoice()
self._clear_tickets_cache()
self.order.touch()
self.order.create_transactions()
self.order.create_transactions(source=self.source)
if self.split_order:
self.split_order.create_transactions()
self.split_order.create_transactions(source=self.source)
if self.notify:
notify_user_changed_order(
@@ -2404,12 +2370,12 @@ class OrderChangeManager:
@app.task(base=ProfiledEventTask, bind=True, max_retries=5, default_retry_delay=1, throws=(OrderError,))
def perform_order(self, event: Event, payment_provider: str, positions: List[str],
email: str=None, locale: str=None, address: int=None, meta_info: dict=None,
sales_channel: str='web', gift_cards: list=None, shown_total=None, customer=None):
sales_channel: str='web', gift_cards: list=None, shown_total=None, customer=None, source=None):
with language(locale):
try:
try:
return _perform_order(event, payment_provider, positions, email, locale, address, meta_info,
sales_channel, gift_cards, shown_total, customer)
sales_channel, gift_cards, shown_total, customer, source=source)
except LockTimeoutException:
self.retry()
except (MaxRetriesExceededError, LockTimeoutException):
@@ -2539,11 +2505,12 @@ def _try_auto_refund(order, auto_refund=True, manual_refund=False, allow_partial
@scopes_disabled()
def cancel_order(self, order: int, user: int=None, send_mail: bool=True, api_token=None, oauth_application=None,
device=None, cancellation_fee=None, try_auto_refund=False, refund_as_giftcard=False,
email_comment=None, refund_comment=None, cancel_invoice=True):
email_comment=None, refund_comment=None, cancel_invoice=True, source=None):
try:
try:
ret = _cancel_order(order, user, send_mail, api_token, device, oauth_application,
cancellation_fee, cancel_invoice=cancel_invoice, comment=email_comment)
cancellation_fee, cancel_invoice=cancel_invoice, comment=email_comment,
source=source)
if try_auto_refund:
_try_auto_refund(order, refund_as_giftcard=refund_as_giftcard,
comment=refund_comment)
@@ -2555,7 +2522,7 @@ def cancel_order(self, order: int, user: int=None, send_mail: bool=True, api_tok
def change_payment_provider(order: Order, payment_provider, amount=None, new_payment=None, create_log=True,
recreate_invoices=True):
recreate_invoices=True, source=None):
if not get_connection().in_atomic_block:
raise Exception('change_payment_provider should only be called in atomic transaction!')
@@ -2643,7 +2610,7 @@ def change_payment_provider(order: Order, payment_provider, amount=None, new_pay
generate_cancellation(i)
generate_invoice(order)
order.create_transactions()
order.create_transactions(source=source)
return old_fee, new_fee, fee, new_payment
+2 -2
View File
@@ -112,7 +112,7 @@ def dictsum(*dicts) -> dict:
def order_overview(
event: Event, subevent: SubEvent=None, date_filter='', date_from=None, date_until=None, fees=False,
admission_only=False, base_qs=None
admission_only=False
) -> Tuple[List[Tuple[ItemCategory, List[Item]]], Dict[str, Tuple[Decimal, Decimal]]]:
items = event.items.all().select_related(
'category', # for re-grouping
@@ -120,7 +120,7 @@ def order_overview(
'variations'
).order_by('category__position', 'category_id', 'position', 'name')
qs = OrderPosition.all if base_qs is None else base_qs
qs = OrderPosition.all
if isinstance(subevent, (list, QuerySet)):
qs = qs.filter(subevent__in=subevent)
elif subevent:
+3 -160
View File
@@ -972,18 +972,6 @@ DEFAULTS = {
"to emails."),
)
},
'invoice_email_asked': {
'default': 'False',
'type': bool,
'form_class': forms.BooleanField,
'serializer_class': serializers.BooleanField,
'form_kwargs': dict(
label=_("Allow customers to enter an alternate email address"),
help_text=_("If an alternate email address is entered, the invoice will be sent there in addition to being "
"sent to the user."),
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_invoice_email_attachment'}),
)
},
'invoice_email_organizer': {
'default': '',
'type': str,
@@ -1433,45 +1421,6 @@ DEFAULTS = {
label=_("Customers can cancel their unpaid orders"),
)
},
'cancel_allow_user_unpaid_keep': {
'default': '0.00',
'type': Decimal,
'form_class': forms.DecimalField,
'serializer_class': serializers.DecimalField,
'serializer_kwargs': dict(
max_digits=10, decimal_places=2
),
'form_kwargs': dict(
label=_("Charge a fixed cancellation fee"),
help_text=_("Only affects orders pending payments, a cancellation fee for free orders is never charged. "
"Note that it will be your responsibility to claim the cancellation fee from the user."),
)
},
'cancel_allow_user_unpaid_keep_fees': {
'default': 'False',
'type': bool,
'form_class': forms.BooleanField,
'serializer_class': serializers.BooleanField,
'form_kwargs': dict(
label=_("Charge payment, shipping and service fees"),
help_text=_("Only affects orders pending payments, a cancellation fee for free orders is never charged. "
"Note that it will be your responsibility to claim the cancellation fee from the user."),
)
},
'cancel_allow_user_unpaid_keep_percentage': {
'default': '0.00',
'type': Decimal,
'form_class': forms.DecimalField,
'serializer_class': serializers.DecimalField,
'serializer_kwargs': dict(
max_digits=10, decimal_places=2
),
'form_kwargs': dict(
label=_("Charge a percentual cancellation fee"),
help_text=_("Only affects orders pending payments, a cancellation fee for free orders is never charged. "
"Note that it will be your responsibility to claim the cancellation fee from the user."),
)
},
'cancel_allow_user_until': {
'default': None,
'type': RelativeDateWrapper,
@@ -1759,14 +1708,6 @@ DEFAULTS = {
'type': LazyI18nString,
'default': ""
},
'mail_subject_resend_link': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Your order: {code}")),
},
'mail_subject_resend_link_attendee': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Your event registration: {code}")),
},
'mail_text_resend_link': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
@@ -1780,10 +1721,6 @@ You can change your order details and view the status of your order at
Best regards,
Your {event} team"""))
},
'mail_subject_resend_all_links': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Your orders for {event}")),
},
'mail_text_resend_all_links': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
@@ -1796,10 +1733,6 @@ The list is as follows:
Best regards,
Your {event} team"""))
},
'mail_subject_order_free_attendee': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Your event registration: {code}")),
},
'mail_text_order_free_attendee': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello {attendee_name},
@@ -1812,14 +1745,6 @@ You can view the details and status of your ticket here:
Best regards,
Your {event} team"""))
},
'mail_send_order_free_attendee': {
'type': bool,
'default': 'False'
},
'mail_subject_order_free': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Your order: {code}")),
},
'mail_text_order_free': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
@@ -1833,9 +1758,9 @@ You can change your order details and view the status of your order at
Best regards,
Your {event} team"""))
},
'mail_subject_order_placed_require_approval': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Your order: {code}")),
'mail_send_order_free_attendee': {
'type': bool,
'default': 'False'
},
'mail_text_order_placed_require_approval': {
'type': LazyI18nString,
@@ -1851,10 +1776,6 @@ You can change your order details and view the status of your order at
Best regards,
Your {event} team"""))
},
'mail_subject_order_placed': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Your order: {code}")),
},
'mail_text_order_placed': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
@@ -1898,10 +1819,6 @@ Your {event} team"""))
'type': bool,
'default': 'False'
},
'mail_subject_order_placed_attendee': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Your event registration: {code}")),
},
'mail_text_order_placed_attendee': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello {attendee_name},
@@ -1914,10 +1831,6 @@ You can view the details and status of your ticket here:
Best regards,
Your {event} team"""))
},
'mail_subject_order_changed': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Your order has been changed: {code}")),
},
'mail_text_order_changed': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
@@ -1930,10 +1843,6 @@ You can view the status of your order at
Best regards,
Your {event} team"""))
},
'mail_subject_order_paid': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Payment received for your order: {code}")),
},
'mail_text_order_paid': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
@@ -1952,10 +1861,6 @@ Your {event} team"""))
'type': bool,
'default': 'False'
},
'mail_subject_order_paid_attendee': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Event registration confirmed: {code}")),
},
'mail_text_order_paid_attendee': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello {attendee_name},
@@ -1983,14 +1888,6 @@ Your {event} team"""))
'type': int,
'default': '3'
},
'mail_subject_order_expire_warning': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Your order is about to expire: {code}")),
},
'mail_subject_order_pending_warning': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Your order is pending payment: {code}")),
},
'mail_text_order_expire_warning': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
@@ -2005,10 +1902,6 @@ You can view the payment information and the status of your order at
Best regards,
Your {event} team"""))
},
'mail_subject_waiting_list': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("You have been selected from the waitinglist for {event}")),
},
'mail_text_waiting_list': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
@@ -2038,10 +1931,6 @@ as possible to the next person on the waiting list:
Best regards,
Your {event} team"""))
},
'mail_subject_order_canceled': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Order canceled: {code}")),
},
'mail_text_order_canceled': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
@@ -2056,10 +1945,6 @@ You can view the details of your order at
Best regards,
Your {event} team"""))
},
'mail_subject_order_approved': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Order approved and awaiting payment: {code}")),
},
'mail_text_order_approved': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
@@ -2076,10 +1961,6 @@ You can select a payment method and perform the payment here:
Best regards,
Your {event} team"""))
},
'mail_subject_order_approved_free': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Order approved and confirmed: {code}")),
},
'mail_text_order_approved_free': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
@@ -2093,10 +1974,6 @@ You can change your order details and view the status of your order at
Best regards,
Your {event} team"""))
},
'mail_subject_order_denied': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Order denied: {code}")),
},
'mail_text_order_denied': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
@@ -2110,20 +1987,6 @@ You can view the details of your order here:
{url}
Best regards,
Your {event} team"""))
},
'mail_subject_invoice_only': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Invoice {invoice_number}")),
},
'mail_text_invoice_only': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
you receive this message because an order for {event} was placed by {order_email} and we have been asked to forward the invoice to you.
Best regards,
Your {event} team"""))
},
'mail_text_order_custom_mail': {
@@ -2144,10 +2007,6 @@ Your {event} team"""))
'type': bool,
'default': 'False'
},
'mail_subject_download_reminder_attendee': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Your ticket is ready for download: {code}")),
},
'mail_text_download_reminder_attendee': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello {attendee_name},
@@ -2160,10 +2019,6 @@ Your {event} team"""))
Best regards,
Your {event} team"""))
},
'mail_subject_download_reminder': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Your ticket is ready for download: {code}")),
},
'mail_text_download_reminder': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
@@ -2176,10 +2031,6 @@ If you did not do so already, you can download your ticket here:
Best regards,
Your {event} team"""))
},
'mail_subject_customer_registration': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Activate your account at {organizer}")),
},
'mail_text_customer_registration': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello {name},
@@ -2198,10 +2049,6 @@ Best regards,
Your {organizer} team"""))
},
'mail_subject_customer_email_change': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Confirm email address for your account at {organizer}")),
},
'mail_text_customer_email_change': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello {name},
@@ -2220,10 +2067,6 @@ Best regards,
Your {organizer} team"""))
},
'mail_subject_customer_reset': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("Set a new password for your account at {organizer}")),
},
'mail_text_customer_reset': {
'type': LazyI18nString,
'default': LazyI18nString.from_gettext(gettext_noop("""Hello {name},
-4
View File
@@ -64,10 +64,6 @@ class EventPluginSignal(django.dispatch.Signal):
# Send to all events!
return True
# If sentry packed this in a wrapper, unpack that
if "sentry" in receiver.__module__:
receiver = receiver.__wrapped__
# Find the Django application this belongs to
searchpath = receiver.__module__
core_module = any([searchpath.startswith(cm) for cm in settings.CORE_MODULES])
+4 -140
View File
@@ -666,9 +666,6 @@ class CancelSettingsForm(SettingsForm):
'cancel_allow_user_until',
'cancel_allow_user_paid',
'cancel_allow_user_paid_until',
'cancel_allow_user_unpaid_keep',
'cancel_allow_user_unpaid_keep_fees',
'cancel_allow_user_unpaid_keep_percentage',
'cancel_allow_user_paid_keep',
'cancel_allow_user_paid_keep_fees',
'cancel_allow_user_paid_keep_percentage',
@@ -802,7 +799,6 @@ class InvoiceSettingsForm(SettingsForm):
'invoice_numbers_counter_length',
'invoice_address_explanation_text',
'invoice_email_attachment',
'invoice_email_asked',
'invoice_email_organizer',
'invoice_address_from_name',
'invoice_address_from',
@@ -931,11 +927,6 @@ class MailSettingsForm(SettingsForm):
required=True,
choices=[]
)
mail_subject_order_placed = I18nFormField(
label=_("Subject sent to order contact address"),
required=False,
widget=I18nTextInput,
)
mail_text_order_placed = I18nFormField(
label=_("Text sent to order contact address"),
required=False,
@@ -947,22 +938,12 @@ class MailSettingsForm(SettingsForm):
'tickets, the following email will be sent out to the attendees.'),
required=False,
)
mail_subject_order_placed_attendee = I18nFormField(
label=_("Subject sent to attendees"),
required=False,
widget=I18nTextInput,
)
mail_text_order_placed_attendee = I18nFormField(
label=_("Text sent to attendees"),
required=False,
widget=I18nTextarea,
)
mail_subject_order_paid = I18nFormField(
label=_("Subject sent to order contact address"),
required=False,
widget=I18nTextInput,
)
mail_text_order_paid = I18nFormField(
label=_("Text sent to order contact address"),
required=False,
@@ -974,22 +955,12 @@ class MailSettingsForm(SettingsForm):
'tickets, the following email will be sent out to the attendees.'),
required=False,
)
mail_subject_order_paid_attendee = I18nFormField(
label=_("Subject sent to attendees"),
required=False,
widget=I18nTextInput,
)
mail_text_order_paid_attendee = I18nFormField(
label=_("Text sent to attendees"),
required=False,
widget=I18nTextarea,
)
mail_subject_order_free = I18nFormField(
label=_("Subject sent to order contact address"),
required=False,
widget=I18nTextInput,
)
mail_text_order_free = I18nFormField(
label=_("Text sent to order contact address"),
required=False,
@@ -1001,47 +972,22 @@ class MailSettingsForm(SettingsForm):
'tickets, the following email will be sent out to the attendees.'),
required=False,
)
mail_subject_order_free_attendee = I18nFormField(
label=_("Subject sent to attendees"),
required=False,
widget=I18nTextInput,
)
mail_text_order_free_attendee = I18nFormField(
label=_("Text sent to attendees"),
required=False,
widget=I18nTextarea,
)
mail_subject_order_changed = I18nFormField(
label=_("Subject"),
required=False,
widget=I18nTextInput,
)
mail_text_order_changed = I18nFormField(
label=_("Text"),
required=False,
widget=I18nTextarea,
)
mail_subject_resend_link = I18nFormField(
label=_("Subject (sent by admin)"),
required=False,
widget=I18nTextInput,
)
mail_subject_resend_link_attendee = I18nFormField(
label=_("Subject (sent by admin to attendee)"),
required=False,
widget=I18nTextInput,
)
mail_text_resend_link = I18nFormField(
label=_("Text (sent by admin)"),
required=False,
widget=I18nTextarea,
)
mail_subject_resend_all_links = I18nFormField(
label=_("Subject (requested by user)"),
required=False,
widget=I18nTextInput,
)
mail_text_resend_all_links = I18nFormField(
label=_("Text (requested by user)"),
required=False,
@@ -1059,31 +1005,11 @@ class MailSettingsForm(SettingsForm):
required=False,
widget=I18nTextarea,
)
mail_subject_order_expire_warning = I18nFormField(
label=_("Subject (if order will expire automatically)"),
required=False,
widget=I18nTextInput,
)
mail_subject_order_pending_warning = I18nFormField(
label=_("Subject (if order will not expire automatically)"),
required=False,
widget=I18nTextInput,
)
mail_subject_waiting_list = I18nFormField(
label=_("Subject"),
required=False,
widget=I18nTextInput,
)
mail_text_waiting_list = I18nFormField(
label=_("Text"),
required=False,
widget=I18nTextarea,
)
mail_subject_order_canceled = I18nFormField(
label=_("Subject"),
required=False,
widget=I18nTextInput,
)
mail_text_order_canceled = I18nFormField(
label=_("Text"),
required=False,
@@ -1094,11 +1020,6 @@ class MailSettingsForm(SettingsForm):
required=False,
widget=I18nTextarea,
)
mail_subject_download_reminder = I18nFormField(
label=_("Subject sent to order contact address"),
required=False,
widget=I18nTextInput,
)
mail_text_download_reminder = I18nFormField(
label=_("Text sent to order contact address"),
required=False,
@@ -1110,11 +1031,6 @@ class MailSettingsForm(SettingsForm):
'tickets, the following email will be sent out to the attendees.'),
required=False,
)
mail_subject_download_reminder_attendee = I18nFormField(
label=_("Subject sent to attendees"),
required=False,
widget=I18nTextInput,
)
mail_text_download_reminder_attendee = I18nFormField(
label=_("Text sent to attendees"),
required=False,
@@ -1127,102 +1043,50 @@ class MailSettingsForm(SettingsForm):
help_text=_("This email will be sent out this many days before the order event starts. If the "
"field is empty, the mail will never be sent.")
)
mail_subject_order_placed_require_approval = I18nFormField(
label=_("Subject for received order"),
required=False,
widget=I18nTextInput,
)
mail_text_order_placed_require_approval = I18nFormField(
label=_("Text for received order"),
label=_("Received order"),
required=False,
widget=I18nTextarea,
)
mail_subject_order_approved = I18nFormField(
label=_("Subject for approved order"),
required=False,
widget=I18nTextInput,
)
mail_text_order_approved = I18nFormField(
label=_("Text for approved order"),
label=_("Approved order"),
required=False,
widget=I18nTextarea,
help_text=_("This will only be sent out for non-free orders. Free orders will receive the free order "
"template from below instead."),
)
mail_subject_order_approved_free = I18nFormField(
label=_("Subject for approved free order"),
required=False,
widget=I18nTextInput,
)
mail_text_order_approved_free = I18nFormField(
label=_("Text for approved free order"),
label=_("Approved free order"),
required=False,
widget=I18nTextarea,
help_text=_("This will only be sent out for free orders. Non-free orders will receive the non-free order "
"template from above instead."),
)
mail_subject_order_denied = I18nFormField(
label=_("Subject for denied order"),
required=False,
widget=I18nTextInput,
)
mail_text_order_denied = I18nFormField(
label=_("Text for denied order"),
required=False,
widget=I18nTextarea,
)
mail_subject_invoice_only = I18nFormField(
label=_("Subject"),
required=False,
widget=I18nTextInput,
)
mail_text_invoice_only = I18nFormField(
label=_("Text"),
label=_("Denied order"),
required=False,
widget=I18nTextarea,
)
base_context = {
'mail_text_order_placed': ['event', 'order', 'payment'],
'mail_subject_order_placed': ['event', 'order', 'payment'],
'mail_text_order_placed_attendee': ['event', 'order', 'position'],
'mail_subject_order_placed_attendee': ['event', 'order', 'position'],
'mail_text_order_placed_require_approval': ['event', 'order'],
'mail_subject_order_placed_require_approval': ['event', 'order'],
'mail_text_order_approved': ['event', 'order'],
'mail_subject_order_approved': ['event', 'order'],
'mail_text_order_approved_free': ['event', 'order'],
'mail_subject_order_approved_free': ['event', 'order'],
'mail_text_order_denied': ['event', 'order', 'comment'],
'mail_subject_order_denied': ['event', 'order', 'comment'],
'mail_text_order_paid': ['event', 'order', 'payment_info'],
'mail_subject_order_paid': ['event', 'order', 'payment_info'],
'mail_text_order_paid_attendee': ['event', 'order', 'position'],
'mail_subject_order_paid_attendee': ['event', 'order', 'position'],
'mail_text_order_free': ['event', 'order'],
'mail_subject_order_free': ['event', 'order'],
'mail_text_order_free_attendee': ['event', 'order', 'position'],
'mail_subject_order_free_attendee': ['event', 'order', 'position'],
'mail_text_order_changed': ['event', 'order'],
'mail_subject_order_changed': ['event', 'order'],
'mail_text_order_canceled': ['event', 'order', 'comment'],
'mail_subject_order_canceled': ['event', 'order', 'comment'],
'mail_text_order_expire_warning': ['event', 'order'],
'mail_subject_order_expire_warning': ['event', 'order'],
'mail_subject_order_pending_warning': ['event', 'order'],
'mail_text_order_custom_mail': ['event', 'order'],
'mail_text_download_reminder': ['event', 'order'],
'mail_subject_download_reminder': ['event', 'order'],
'mail_text_download_reminder_attendee': ['event', 'order', 'position'],
'mail_subject_download_reminder_attendee': ['event', 'order', 'position'],
'mail_text_resend_link': ['event', 'order'],
'mail_subject_resend_link': ['event', 'order'],
'mail_subject_resend_link_attendee': ['event', 'order'],
'mail_text_waiting_list': ['event', 'waiting_list_entry'],
'mail_subject_waiting_list': ['event', 'waiting_list_entry'],
'mail_text_resend_all_links': ['event', 'orders'],
'mail_subject_resend_all_links': ['event', 'orders'],
'mail_subject_invoice_only': ['event', 'order', 'invoice'],
'mail_text_invoice_only': ['event', 'order', 'invoice'],
'mail_attach_ical_description': ['event', 'event_or_subevent'],
}
+4
View File
@@ -768,6 +768,10 @@ class ItemAddOnsFormSet(I18nFormSet):
if self._should_delete_form(form):
# This form is going to be deleted so any of its errors
# should not cause the entire formset to be invalid.
try:
categories.remove(form.cleaned_data['addon_category'].pk)
except KeyError:
pass
continue
if 'addon_category' in form.cleaned_data:
+13 -9
View File
@@ -158,7 +158,7 @@ class CancelForm(ForceQuotaConfirmationForm):
localize=True,
label=_('Keep a cancellation fee of'),
help_text=_('If you keep a fee, all positions within this order will be canceled and the order will be reduced '
'to a cancellation fee. Payment and shipping fees will be canceled as well, so include them '
'to a paid cancellation fee. Payment and shipping fees will be canceled as well, so include them '
'in your cancellation fee if you want to keep them. Please always enter a gross value, '
'tax will be calculated automatically.'),
)
@@ -176,19 +176,23 @@ class CancelForm(ForceQuotaConfirmationForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
change_decimal_field(self.fields['cancellation_fee'], self.instance.event.currency)
self.fields['cancellation_fee'].widget.attrs['placeholder'] = floatformat(
Decimal('0.00'),
settings.CURRENCY_PLACES.get(self.instance.event.currency, 2)
)
self.fields['cancellation_fee'].max_value = self.instance.total
prs = self.instance.payment_refund_sum
if prs > 0:
change_decimal_field(self.fields['cancellation_fee'], self.instance.event.currency)
self.fields['cancellation_fee'].widget.attrs['placeholder'] = floatformat(
Decimal('0.00'),
settings.CURRENCY_PLACES.get(self.instance.event.currency, 2)
)
self.fields['cancellation_fee'].max_value = prs
else:
del self.fields['cancellation_fee']
if not self.instance.invoices.exists():
del self.fields['cancel_invoice']
def clean_cancellation_fee(self):
val = self.cleaned_data['cancellation_fee'] or Decimal('0.00')
if val > self.instance.total:
raise ValidationError(_('The cancellation fee cannot be higher than the total amount of this order.'))
if val > self.instance.payment_refund_sum:
raise ValidationError(_('The cancellation fee cannot be higher than the payment credit of this order.'))
return val
+1 -21
View File
@@ -45,9 +45,7 @@ from django.utils.crypto import get_random_string
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _, pgettext_lazy
from django_scopes.forms import SafeModelMultipleChoiceField
from i18nfield.forms import (
I18nFormField, I18nFormSetMixin, I18nTextarea, I18nTextInput,
)
from i18nfield.forms import I18nFormField, I18nFormSetMixin, I18nTextarea
from phonenumber_field.formfields import PhoneNumberField
from pytz import common_timezones
@@ -459,31 +457,16 @@ class MailSettingsForm(SettingsForm):
}}
)
mail_subject_customer_registration = I18nFormField(
label=_("Subject"),
required=False,
widget=I18nTextInput,
)
mail_text_customer_registration = I18nFormField(
label=_("Text"),
required=False,
widget=I18nTextarea,
)
mail_subject_customer_email_change = I18nFormField(
label=_("Subject"),
required=False,
widget=I18nTextInput,
)
mail_text_customer_email_change = I18nFormField(
label=_("Text"),
required=False,
widget=I18nTextarea,
)
mail_subject_customer_reset = I18nFormField(
label=_("Subject"),
required=False,
widget=I18nTextInput,
)
mail_text_customer_reset = I18nFormField(
label=_("Text"),
required=False,
@@ -492,11 +475,8 @@ class MailSettingsForm(SettingsForm):
base_context = {
'mail_text_customer_registration': ['customer', 'url'],
'mail_subject_customer_registration': ['customer', 'url'],
'mail_text_customer_email_change': ['customer', 'url'],
'mail_subject_customer_email_change': ['customer', 'url'],
'mail_text_customer_reset': ['customer', 'url'],
'mail_subject_customer_reset': ['customer', 'url'],
}
def _get_sample_context(self, base_parameters):
+4 -7
View File
@@ -72,7 +72,7 @@ class VoucherForm(I18nModelForm):
localized_fields = '__all__'
fields = [
'code', 'valid_until', 'block_quota', 'allow_ignore_quota', 'value', 'tag',
'comment', 'max_usages', 'min_usages', 'price_mode', 'subevent', 'show_hidden_items', 'budget'
'comment', 'max_usages', 'price_mode', 'subevent', 'show_hidden_items', 'budget'
]
field_classes = {
'valid_until': SplitDateTimeField,
@@ -308,7 +308,7 @@ class VoucherBulkForm(VoucherForm):
localized_fields = '__all__'
fields = [
'valid_until', 'block_quota', 'allow_ignore_quota', 'value', 'tag', 'comment',
'max_usages', 'min_usages', 'price_mode', 'subevent', 'show_hidden_items', 'budget'
'max_usages', 'price_mode', 'subevent', 'show_hidden_items', 'budget'
]
field_classes = {
'valid_until': SplitDateTimeField,
@@ -345,11 +345,8 @@ class VoucherBulkForm(VoucherForm):
if ',' in raw or ';' in raw:
if '@' in r[0]:
raise ValidationError(_('CSV input needs to contain a header row in the first line.'))
try:
dialect = csv.Sniffer().sniff(raw[:1024])
reader = csv.DictReader(StringIO(raw), dialect=dialect)
except csv.Error as e:
raise ValidationError(_('CSV parsing failed: {error}.').format(error=str(e)))
dialect = csv.Sniffer().sniff(raw[:1024])
reader = csv.DictReader(StringIO(raw), dialect=dialect)
if 'email' not in reader.fieldnames:
raise ValidationError(_('CSV input needs to contain a field with the header "{header}".').format(header="email"))
unknown_fields = [f for f in reader.fieldnames if f not in ('email', 'name', 'tag', 'number')]
-1
View File
@@ -392,7 +392,6 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
'pretix.event.order.email.order_approved': _('An email has been sent to notify the user that the order has '
'been approved.'),
'pretix.event.order.email.order_placed': _('An email has been sent to notify the user that the order has been received and requires payment.'),
'pretix.event.order.email.order_invoice_only': _('An invoice has been sent to an alternate email address.'),
'pretix.event.order.email.order_placed_require_approval': _('An email has been sent to notify the user that '
'the order has been received and requires '
'approval.'),
@@ -11,7 +11,7 @@
{% trans "You configured your account to require authentication with a second medium, e.g. your phone. Please enter your verification code here:" %}
</p>
<div class="form-group">
<input class="form-control" name="token" placeholder="{% trans "Token" %}" autocomplete="one-time-code"
<input class="form-control" name="token" placeholder="{% trans "Token" %}"
type="text" required="required" autofocus="autofocus" id="webauthn-response">
</div>
<div class="sr-only alert alert-danger" id="webauthn-error">
@@ -116,12 +116,8 @@
</form>
{{ items|json_script:"items" }}
{% if DEBUG %}
<script type="text/javascript" src="{% static "vuejs/vue.js" %}"></script>
{% else %}
<script type="text/javascript" src="{% static "vuejs/vue.min.js" %}"></script>
{% endif %}
{% compress js %}
<script type="text/javascript" src="{% static "vuejs/vue.js" %}"></script>
<script type="text/javascript" src="{% static "d3/d3.v6.js" %}"></script>
<script type="text/javascript" src="{% static "d3/d3-color.v2.js" %}"></script>
<script type="text/javascript" src="{% static "d3/d3-dispatch.v2.js" %}"></script>
@@ -132,8 +128,6 @@
<script type="text/javascript" src="{% static "d3/d3-transition.v2.js" %}"></script>
<script type="text/javascript" src="{% static "d3/d3-drag.v2.js" %}"></script>
<script type="text/javascript" src="{% static "d3/d3-zoom.v2.js" %}"></script>
{% endcompress %}
{% compress js %}
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/checkinrules/jsonlogic-boolalg.js" %}"></script>
<script type="text/vue" src="{% static 'pretixcontrol/js/ui/checkinrules/datetimefield.vue' %}"></script>
<script type="text/vue" src="{% static 'pretixcontrol/js/ui/checkinrules/timefield.vue' %}"></script>
@@ -11,9 +11,6 @@
<legend>{% trans "Unpaid or free orders" %}</legend>
{% bootstrap_field form.cancel_allow_user layout="control" %}
{% bootstrap_field form.cancel_allow_user_until layout="control" %}
{% bootstrap_field form.cancel_allow_user_unpaid_keep layout="control" %}
{% bootstrap_field form.cancel_allow_user_unpaid_keep_percentage layout="control" %}
{% bootstrap_field form.cancel_allow_user_unpaid_keep_fees layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Paid orders" %}</legend>
@@ -12,7 +12,6 @@
{% bootstrap_field form.invoice_generate layout="control" %}
{% bootstrap_field form.invoice_generate_sales_channels layout="control" %}
{% bootstrap_field form.invoice_email_attachment layout="control" %}
{% bootstrap_field form.invoice_email_asked layout="control" %}
{% bootstrap_field form.invoice_email_organizer layout="control" %}
{% bootstrap_field form.invoice_language layout="control" %}
{% bootstrap_field form.invoice_include_free layout="control" %}
@@ -88,41 +88,37 @@
<h4>{% trans "Text" %}</h4>
<div class="panel-group" id="questions_group">
{% blocktrans asvar title_placed_order %}Placed order{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_placed" title=title_placed_order items="mail_subject_order_placed,mail_text_order_placed,mail_send_order_placed_attendee,mail_subject_order_placed_attendee,mail_text_order_placed_attendee" exclude="mail_send_order_placed_attendee" %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_placed" title=title_placed_order items="mail_text_order_placed,mail_send_order_placed_attendee,mail_text_order_placed_attendee" exclude="mail_send_order_placed_attendee" %}
{% blocktrans asvar title_paid_order %}Paid order{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_paid" title=title_paid_order items="mail_subject_order_paid,mail_text_order_paid,mail_send_order_paid_attendee,mail_subject_order_paid_attendee,mail_text_order_paid_attendee" exclude="mail_send_order_paid_attendee" %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_paid" title=title_paid_order items="mail_text_order_paid,mail_send_order_paid_attendee,mail_text_order_paid_attendee" exclude="mail_send_order_paid_attendee" %}
{% blocktrans asvar title_free_order %}Free order{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_free" title=title_free_order items="mail_subject_order_free,mail_text_order_free,mail_send_order_free_attendee,mail_subject_order_free_attendee,mail_text_order_free_attendee" exclude="mail_send_order_free_attendee" %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_free" title=title_free_order items="mail_text_order_free,mail_send_order_free_attendee,mail_text_order_free_attendee" exclude="mail_send_order_free_attendee" %}
{% blocktrans asvar title_resend_link %}Resend link{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="resend_link" title=title_resend_link items="mail_subject_resend_link,mail_subject_resend_link_attendee,mail_text_resend_link,mail_subject_resend_all_links,mail_text_resend_all_links" %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="resend_link" title=title_resend_link items="mail_text_resend_link,mail_text_resend_all_links" %}
{% blocktrans asvar title_order_changed %}Order changed{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_changed" title=title_order_changed items="mail_subject_order_changed,mail_text_order_changed" %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_changed" title=title_order_changed items="mail_text_order_changed" %}
{% blocktrans asvar title_payment_reminder %}Payment reminder{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_expirew" title=title_payment_reminder items="mail_days_order_expire_warning,mail_subject_order_expire_warning,mail_subject_order_pending_warning,mail_text_order_expire_warning" exclude="mail_days_order_expire_warning" %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_expirew" title=title_payment_reminder items="mail_days_order_expire_warning,mail_text_order_expire_warning" exclude="mail_days_order_expire_warning" %}
{% blocktrans asvar title_waiting_list_notification %}Waiting list notification{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="waiting_list" title=title_waiting_list_notification items="mail_subject_waiting_list,mail_text_waiting_list" %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="waiting_list" title=title_waiting_list_notification items="mail_text_waiting_list" %}
{% blocktrans asvar title_order_canceled %}Order canceled{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_canceled" title=title_order_canceled items="mail_subject_order_canceled,mail_text_order_canceled" %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_canceled" title=title_order_canceled items="mail_text_order_canceled" %}
{% blocktrans asvar title_order_custom_mail %}Order custom mail{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="custom_mail" title=title_order_custom_mail items="mail_text_order_custom_mail" %}
{% blocktrans asvar title_download_tickets_reminder %}Reminder to download tickets{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="ticket_reminder" title=title_download_tickets_reminder items="mail_days_download_reminder,mail_subject_download_reminder,mail_text_download_reminder,mail_send_download_reminder_attendee,mail_subject_download_reminder_attendee,mail_text_download_reminder_attendee,mail_sales_channel_download_reminder" exclude="mail_days_download_reminder,mail_send_download_reminder_attendee,mail_sales_channel_download_reminder" %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="ticket_reminder" title=title_download_tickets_reminder items="mail_days_download_reminder,mail_text_download_reminder,mail_send_download_reminder_attendee,mail_text_download_reminder_attendee,mail_sales_channel_download_reminder" exclude="mail_days_download_reminder,mail_send_download_reminder_attendee,mail_sales_channel_download_reminder" %}
{% blocktrans asvar title_require_approval %}Order approval process{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="ticket_reminder" title=title_require_approval items="mail_subject_order_placed_require_approval,mail_text_order_placed_require_approval,mail_subject_order_approved,mail_text_order_approved,mail_subject_order_approved_free,mail_text_order_approved_free,mail_subject_order_denied,mail_text_order_denied" %}
{% blocktrans asvar title_invoice_only %}Invoice to alternate address{% endblocktrans %}
{% blocktrans asvar description_invoice_only %}This email text is used if you activated "Allow customers to enter an alternate email address" in your invoice settings and a user asks that the invoice is sent to an alternate email address. This email, unlike all other emails, will be sent without any styling to prevent issues with automated processing in accounting systems.{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="ticket_reminder" title=title_invoice_only description=description_invoice_only items="mail_subject_invoice_only,mail_text_invoice_only" %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="ticket_reminder" title=title_require_approval items="mail_text_order_placed_require_approval,mail_text_order_approved,mail_text_order_approved_free,mail_text_order_denied" %}
</div>
<h4>{% trans "Attachments" %}</h4>
{% bootstrap_field form.mail_attachment_new_order layout="control" %}
@@ -10,9 +10,6 @@
</summary>
<div id="{{ pid }}">
<div class="panel-body">
{% if description %}
<p class="help-block">{{ description }}</p>
{% endif %}
{% with exclude|split as exclusion %}
{% with items|split as item_list %}
{% for item in item_list %}
@@ -10,7 +10,7 @@
<fieldset>
<legend>{% trans "General information" %}</legend>
{% bootstrap_field form.name layout="control" %}
<div class="optional-field-wrapper" data-text="{% trans "Use a different name internally" %}">
<div class="internal-name-wrapper">
{% bootstrap_field form.internal_name layout="control" %}
</div>
{% bootstrap_field form.copy_from layout="control" %}
@@ -12,7 +12,7 @@
<legend>{% trans "General" %}</legend>
{% bootstrap_field form.active layout="control" %}
{% bootstrap_field form.name layout="control" %}
<div class="optional-field-wrapper" data-text="{% trans "Use a different name internally" %}">
<div class="internal-name-wrapper">
{% bootstrap_field form.internal_name layout="control" %}
</div>
{% bootstrap_field form.category layout="control" %}
@@ -12,7 +12,7 @@
<fieldset>
<legend>{% trans "General information" %}</legend>
{% bootstrap_field form.name layout="control" %}
<div class="optional-field-wrapper" data-text="{% trans "Use a different name internally" %}">
<div class="internal-name-wrapper">
{% bootstrap_field form.internal_name layout="control" %}
</div>
{% bootstrap_field form.description layout="control" %}
@@ -190,13 +190,7 @@
</dd>
{% if order.status == "n" %}
<dt>{% trans "Expiry date" %}</dt>
<dd>
{{ order.expires|date:"SHORT_DATETIME_FORMAT" }}
{% if has_cancellation_fee and request.event.settings.payment_term_expire_automatically %}
<span class="fa fa-warning text-danger" data-toggle="tooltip"
title="{% trans "This order will not expire automatically as it has an open cancellation fee." %}"></span>
{% endif %}
</dd>
<dd>{{ order.expires|date:"SHORT_DATETIME_FORMAT" }}</dd>
{% endif %}
{% if request.organizer.settings.customer_accounts %}
<dt>{% trans "Customer account" %}</dt>
@@ -923,10 +917,6 @@
{% endif %}
<dt>{% trans "Internal reference" %}</dt>
<dd>{{ order.invoice_address.internal_reference }}</dd>
{% if request.event.settings.invoice_email_asked %}
<dt>{% trans "Send invoice to" %}</dt>
<dd>{{ order.invoice_address.invoice_email }}</dd>
{% endif %}
</dl>
</div>
</div>
@@ -58,13 +58,13 @@
<legend>{% trans "E-mail content" %}</legend>
<div class="panel-group" id="questions_group">
{% blocktrans asvar title_customer_registration %}Customer account registration{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="customer_registration" title=title_customer_registration items="mail_subject_customer_registration,mail_text_customer_registration" %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="customer_registration" title=title_customer_registration items="mail_text_customer_registration" %}
{% blocktrans asvar title_email_change %}Customer account email change{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="email_change" title=title_email_change items="mail_subject_customer_email_change,mail_text_customer_email_change" %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="email_change" title=title_email_change items="mail_text_customer_email_change" %}
{% blocktrans asvar title_reset %}Customer account password reset{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="reset" title=title_reset items="mail_subject_customer_reset,mail_text_customer_reset" %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="reset" title=title_reset items="mail_text_customer_reset" %}
</div>
</fieldset>
</div>
@@ -73,7 +73,6 @@
<legend>{% trans "Advanced settings" %}</legend>
{% bootstrap_field form.block_quota layout="control" %}
{% bootstrap_field form.allow_ignore_quota layout="control" %}
{% bootstrap_field form.min_usages layout="control" %}
{% bootstrap_field form.budget addon_after=request.event.currency layout="control" %}
{% bootstrap_field form.tag layout="control" %}
{% bootstrap_field form.comment layout="control" %}
@@ -85,7 +85,6 @@
<legend>{% trans "Advanced settings" %}</legend>
{% bootstrap_field form.block_quota layout="control" %}
{% bootstrap_field form.allow_ignore_quota layout="control" %}
{% bootstrap_field form.min_usages layout="control" %}
{% bootstrap_field form.budget addon_after=request.event.currency layout="control" %}
{% bootstrap_field form.tag layout="control" %}
{% bootstrap_field form.comment layout="control" %}
+4 -8
View File
@@ -41,7 +41,6 @@ from decimal import Decimal
from itertools import groupby
from urllib.parse import urlsplit
import bleach
from django.conf import settings
from django.contrib import messages
from django.contrib.contenttypes.models import ContentType
@@ -724,7 +723,7 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
if preview_item not in MailSettingsForm.base_context:
return HttpResponseBadRequest(_('invalid item'))
regex = r"^" + re.escape(preview_item) + r"_(?P<idx>[\d]+)$"
regex = r"^" + re.escape(preview_item) + r"_(?P<idx>[\d+])$"
msgs = {}
for k, v in request.POST.items():
# only accept allowed fields
@@ -733,12 +732,9 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
idx = matched.group('idx')
if idx in self.supported_locale:
with language(self.supported_locale[idx], self.request.event.settings.region):
if k.startswith('mail_subject_') or k.endswith('_invoice_only'):
msgs[self.supported_locale[idx]] = bleach.clean(v).format_map(self.placeholders(preview_item))
else:
msgs[self.supported_locale[idx]] = markdown_compile_email(
v.format_map(self.placeholders(preview_item))
)
msgs[self.supported_locale[idx]] = markdown_compile_email(
v.format_map(self.placeholders(preview_item))
)
return JsonResponse({
'item': preview_item,
+1 -10
View File
@@ -32,7 +32,6 @@ from django.utils.translation import gettext_lazy as _
from django.views.generic import TemplateView
from pretix.base import email
from pretix.base.forms import SECRET_REDACTED
from pretix.base.models import Event
from pretix.base.services.mail import mail
from pretix.control.forms.filter import OrganizerFilterForm
@@ -170,13 +169,6 @@ class MailSettingsSetupView(TemplateView):
)
if allow_save:
self.object.settings.smtp_use_custom = False
del self.object.settings.smtp_host
del self.object.settings.smtp_port
del self.object.settings.smtp_username
del self.object.settings.smtp_password
del self.object.settings.smtp_use_tls
del self.object.settings.smtp_use_ssl
for k, v in self.simple_form.cleaned_data.items():
self.object.settings.set(k, v)
self.log_action(self.simple_form.cleaned_data)
@@ -245,8 +237,7 @@ class MailSettingsSetupView(TemplateView):
if request.POST.get('state') == 'save':
for k, v in self.smtp_form.cleaned_data.items():
if v != SECRET_REDACTED:
self.object.settings.set(k, v)
self.object.settings.set(k, v)
self.object.settings.smtp_use_custom = True
self.log_action({**self.smtp_form.cleaned_data, 'smtp_use_custom': True})
messages.success(request, _('Your changes have been saved.'))
+1 -2
View File
@@ -276,8 +276,7 @@ class EventWizard(SafeSessionWizardView):
t.limit_events.add(event)
elif event.organizer.settings.event_team_provisioning:
t = Team.objects.create(
organizer=event.organizer,
name=_('Team {event}').format(event=str(event.name)[:188] + "" if len(str(event.name)) > 190 else str(event.name)),
organizer=event.organizer, name=_('Team {event}').format(event=event.name),
can_change_event_settings=True, can_change_items=True,
can_view_orders=True, can_change_orders=True, can_view_vouchers=True,
can_change_vouchers=True
+10 -8
View File
@@ -293,7 +293,6 @@ class OrderDetail(OrderView):
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['items'] = self.get_items()
ctx['has_cancellation_fee'] = any(f.fee_type == OrderFee.FEE_TYPE_CANCELLATION for f in ctx['items']['fees'])
ctx['event'] = self.request.event
ctx['payments'] = self.order.payments.order_by('-created')
ctx['refunds'] = self.order.refunds.select_related('payment').order_by('-created')
@@ -558,7 +557,7 @@ class OrderApprove(OrderView):
def post(self, *args, **kwargs):
if self.order.require_approval:
try:
approve_order(self.order, user=self.request.user)
approve_order(self.order, user=self.request.user, source=("pretix.control", f"user:{self.request.user.pk}"))
except OrderError as e:
messages.error(self.request, str(e))
else:
@@ -609,7 +608,8 @@ class OrderDeny(OrderView):
try:
deny_order(self.order, user=self.request.user,
comment=self.request.POST.get('comment'),
send_mail=self.request.POST.get('send_email') == 'on')
send_mail=self.request.POST.get('send_email') == 'on',
source=("pretix.control", f"user:{self.request.user.pk}"))
except OrderError as e:
messages.error(self.request, str(e))
else:
@@ -704,7 +704,7 @@ class OrderRefundProcess(OrderView):
if self.order.status != Order.STATUS_CANCELED and self.order.positions.exists():
if self.request.POST.get("action") == "r":
mark_order_refunded(self.order, user=self.request.user)
mark_order_refunded(self.order, user=self.request.user, source=("pretix.control", f"user:{self.request.user.pk}"))
elif not (self.order.status == Order.STATUS_PAID and self.order.pending_sum <= 0):
self.order.status = Order.STATUS_PENDING
self.order.set_expires(
@@ -1072,7 +1072,7 @@ class OrderRefundView(OrderView):
if any_success:
if self.start_form.cleaned_data.get('action') == 'mark_refunded':
if self.order.cancel_allowed():
mark_order_refunded(self.order, user=self.request.user)
mark_order_refunded(self.order, user=self.request.user, source=("pretix.control", f"user:{self.request.user.pk}"))
elif self.start_form.cleaned_data.get('action') == 'mark_pending':
if not (self.order.status == Order.STATUS_PAID and self.order.pending_sum <= 0):
self.order.status = Order.STATUS_PENDING
@@ -1275,7 +1275,8 @@ class OrderTransition(OrderView):
email_comment=self.mark_canceled_form.cleaned_data['comment'],
send_mail=self.mark_canceled_form.cleaned_data['send_email'],
cancel_invoice=self.mark_canceled_form.cleaned_data.get('cancel_invoice', True),
cancellation_fee=self.mark_canceled_form.cleaned_data.get('cancellation_fee'))
cancellation_fee=self.mark_canceled_form.cleaned_data.get('cancellation_fee'),
source=("pretix.control", f"user:{self.request.user.pk}"))
except OrderError as e:
messages.error(self.request, str(e))
else:
@@ -1298,7 +1299,7 @@ class OrderTransition(OrderView):
else:
return self.get(self.request, *args, **kwargs)
elif self.order.status == Order.STATUS_PENDING and to == 'e':
mark_order_expired(self.order, user=self.request.user)
mark_order_expired(self.order, user=self.request.user, source=("pretix.control", f"user:{self.request.user.pk}"))
messages.success(self.request, _('The order has been marked as expired.'))
return redirect(self.get_order_url())
@@ -1575,7 +1576,8 @@ class OrderReactivate(OrderView):
reactivate_order(
self.order,
user=self.request.user,
force=self.reactivate_form.cleaned_data.get('force', False)
force=self.reactivate_form.cleaned_data.get('force', False),
source=("pretix.control", f"user:{self.request.user.pk}"),
)
messages.success(self.request, _('The order has been reactivated.'))
except OrderError as e:
+5 -9
View File
@@ -37,7 +37,6 @@ import re
from datetime import timedelta
from decimal import Decimal
import bleach
from django import forms
from django.conf import settings
from django.contrib import messages
@@ -325,7 +324,7 @@ class MailSettingsPreview(OrganizerPermissionRequiredMixin, View):
if preview_item not in MailSettingsForm.base_context:
return HttpResponseBadRequest(_('invalid item'))
regex = r"^" + re.escape(preview_item) + r"_(?P<idx>[\d]+)$"
regex = r"^" + re.escape(preview_item) + r"_(?P<idx>[\d+])$"
msgs = {}
for k, v in request.POST.items():
# only accept allowed fields
@@ -334,12 +333,9 @@ class MailSettingsPreview(OrganizerPermissionRequiredMixin, View):
idx = matched.group('idx')
if idx in self.supported_locale:
with language(self.supported_locale[idx], self.request.organizer.settings.region):
if k.startswith('mail_subject_'):
msgs[self.supported_locale[idx]] = bleach.clean(v).format_map(self.placeholders(preview_item))
else:
msgs[self.supported_locale[idx]] = markdown_compile_email(
v.format_map(self.placeholders(preview_item))
)
msgs[self.supported_locale[idx]] = markdown_compile_email(
v.format_map(self.placeholders(preview_item))
)
return JsonResponse({
'item': preview_item,
@@ -2240,7 +2236,7 @@ class CustomerDetailView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMi
) + '?id=' + self.customer.identifier + '&token=' + token
mail(
self.customer.email,
self.request.organizer.settings.mail_subject_customer_reset,
_('Set a new password for your account at {organizer}').format(organizer=self.request.organizer.name),
self.request.organizer.settings.mail_text_customer_reset,
ctx,
locale=self.customer.locale,
+18 -1
View File
@@ -23,7 +23,8 @@ import pyuca
from babel.core import Locale
from django.core.cache import cache
from django.utils import translation
from django_countries import Countries
from django.utils.encoding import force_str
from django_countries import Countries, CountryTuple
from django_countries.fields import CountryField
from phonenumbers.data import _COUNTRY_CODE_TO_REGION_CODE
@@ -60,6 +61,22 @@ class CachedCountries(Countries):
cache.set(cache_key, val, 3600 * 24 * 30)
yield from val
def translate_pair(self, code: str, name=None):
# We need to temporarily override this function until
# https://github.com/SmileyChris/django-countries/issues/364
# is fixed
if name is None:
name = self.countries[code]
if isinstance(name, dict):
if "names" in name:
country_name: str = name["names"][0]
else:
country_name = name["name"]
else:
country_name = name
country_name = force_str(country_name)
return CountryTuple(code, country_name)
class FastCountryField(CountryField):
def __init__(self, *args, **kwargs):
-40
View File
@@ -21,49 +21,9 @@
#
import logging
import sentry_sdk
from django.core.signals import request_finished
from django.dispatch import receiver
try:
from asgiref.local import Local
except ImportError:
from threading import local as Local
from django.conf import settings
class AdminExistsFilter(logging.Filter):
def filter(self, record):
return not settings.DEBUG and len(settings.ADMINS) > 0
local = Local()
class RequestIdFilter(logging.Filter):
def filter(self, record):
record.request_id = getattr(local, 'request_id', None)
return True
class RequestIdMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if settings.REQUEST_ID_HEADER and settings.REQUEST_ID_HEADER in request.headers:
local.request_id = request.request_id = request.headers[settings.REQUEST_ID_HEADER]
if settings.SENTRY_ENABLED:
sentry_sdk.set_tag("request_id", request.request_id)
else:
local.request_id = request.request_id = None
return self.get_response(request)
@receiver(request_finished)
def on_request_finished(sender, **kwargs):
# not part of middleware, since things could be logged after the middleware stack is finished
local.request_id = None
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-11-07 15:22+0000\n"
"POT-Creation-Date: 2022-10-11 09:30+0000\n"
"PO-Revision-Date: 2021-09-15 11:22+0000\n"
"Last-Translator: Mohamed Tawfiq <mtawfiq@wafyapp.com>\n"
"Language-Team: Arabic <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -132,18 +132,18 @@ msgstr ""
msgid "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:159
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:157
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Continue"
msgstr "المتابعة"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:217
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:215
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
msgid "Confirming your payment …"
msgstr "جاري تأكيد الدفع الخاص بك …"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:242
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:240
msgid "Payment method unavailable"
msgstr ""
@@ -643,32 +643,32 @@ msgstr[3] "سيتم حجز العناصر لك في سلة التسوق لعدة
msgstr[4] "سيتم حجز العناصر لك في سلة التسوق لدقائق {num}."
msgstr[5] "سيتم حجز العناصر لك في سلة التسوق لمدة {num}."
#: pretix/static/pretixpresale/js/ui/main.js:152
#: pretix/static/pretixpresale/js/ui/main.js:144
msgid "The organizer keeps %(currency)s %(amount)s"
msgstr "يحصل المنظم على %(currency) %(amount)"
#: pretix/static/pretixpresale/js/ui/main.js:160
#: pretix/static/pretixpresale/js/ui/main.js:152
msgid "You get %(currency)s %(amount)s back"
msgstr "ستسترد %(currency)%(amount)"
#: pretix/static/pretixpresale/js/ui/main.js:176
#: pretix/static/pretixpresale/js/ui/main.js:168
msgid "Please enter the amount the organizer can keep."
msgstr "الرجاء إدخال المبلغ الذي يمكن للمنظم الاحتفاظ به."
#: pretix/static/pretixpresale/js/ui/main.js:388
#: pretix/static/pretixpresale/js/ui/main.js:380
msgid "Please enter a quantity for one of the ticket types."
msgstr "الرجاء إدخال عدد لأحد أنواع التذاكر."
#: pretix/static/pretixpresale/js/ui/main.js:424
#: pretix/static/pretixpresale/js/ui/main.js:416
msgid "required"
msgstr "مطلوب"
#: pretix/static/pretixpresale/js/ui/main.js:527
#: pretix/static/pretixpresale/js/ui/main.js:546
#: pretix/static/pretixpresale/js/ui/main.js:519
#: pretix/static/pretixpresale/js/ui/main.js:538
msgid "Time zone:"
msgstr "المنطقة الزمنية:"
#: pretix/static/pretixpresale/js/ui/main.js:537
#: pretix/static/pretixpresale/js/ui/main.js:529
msgid "Your local time:"
msgstr "التوقيت المحلي:"
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-11-07 15:22+0000\n"
"POT-Creation-Date: 2022-10-11 09:30+0000\n"
"PO-Revision-Date: 2020-12-19 07:00+0000\n"
"Last-Translator: albert <albert.serra.monner@gmail.com>\n"
"Language-Team: Catalan <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -129,18 +129,18 @@ msgstr ""
msgid "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:159
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:157
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Continue"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:217
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:215
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
msgid "Confirming your payment …"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:242
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:240
msgid "Payment method unavailable"
msgstr ""
@@ -616,34 +616,34 @@ msgid_plural "The items in your cart are reserved for you for {num} minutes."
msgstr[0] "El contingut de la cistella ja no el teniu reservat."
msgstr[1] "El contingut de la cistella ja no el teniu reservat."
#: pretix/static/pretixpresale/js/ui/main.js:152
#: pretix/static/pretixpresale/js/ui/main.js:144
msgid "The organizer keeps %(currency)s %(amount)s"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:160
#: pretix/static/pretixpresale/js/ui/main.js:152
msgid "You get %(currency)s %(amount)s back"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:176
#: pretix/static/pretixpresale/js/ui/main.js:168
msgid "Please enter the amount the organizer can keep."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:388
#: pretix/static/pretixpresale/js/ui/main.js:380
msgid "Please enter a quantity for one of the ticket types."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:424
#: pretix/static/pretixpresale/js/ui/main.js:416
#, fuzzy
#| msgid "Cart expired"
msgid "required"
msgstr "Cistella expirada"
#: pretix/static/pretixpresale/js/ui/main.js:527
#: pretix/static/pretixpresale/js/ui/main.js:546
#: pretix/static/pretixpresale/js/ui/main.js:519
#: pretix/static/pretixpresale/js/ui/main.js:538
msgid "Time zone:"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:537
#: pretix/static/pretixpresale/js/ui/main.js:529
msgid "Your local time:"
msgstr ""
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-11-07 15:22+0000\n"
"POT-Creation-Date: 2022-10-11 09:30+0000\n"
"PO-Revision-Date: 2021-12-06 23:00+0000\n"
"Last-Translator: Ondřej Sokol <osokol@treesoft.cz>\n"
"Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -131,18 +131,18 @@ msgstr ""
msgid "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:159
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:157
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Continue"
msgstr "Pokračovat"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:217
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:215
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
msgid "Confirming your payment …"
msgstr "Potvrzuji vaši platbu …"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:242
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:240
msgid "Payment method unavailable"
msgstr ""
@@ -636,32 +636,32 @@ msgstr[1] ""
msgstr[2] ""
"Produkty v nákupním košíku jsou pro vás rezervovány na dalších {num} minut."
#: pretix/static/pretixpresale/js/ui/main.js:152
#: pretix/static/pretixpresale/js/ui/main.js:144
msgid "The organizer keeps %(currency)s %(amount)s"
msgstr "Organizátor si ponechává %(currency)s %(amount)s"
#: pretix/static/pretixpresale/js/ui/main.js:160
#: pretix/static/pretixpresale/js/ui/main.js:152
msgid "You get %(currency)s %(amount)s back"
msgstr "Dostanete %(currency)s %(amount)s zpět"
#: pretix/static/pretixpresale/js/ui/main.js:176
#: pretix/static/pretixpresale/js/ui/main.js:168
msgid "Please enter the amount the organizer can keep."
msgstr "Zadejte částku, kterou si organizátor může ponechat."
#: pretix/static/pretixpresale/js/ui/main.js:388
#: pretix/static/pretixpresale/js/ui/main.js:380
msgid "Please enter a quantity for one of the ticket types."
msgstr "Zadejte prosím množství pro jeden z typů vstupenek."
#: pretix/static/pretixpresale/js/ui/main.js:424
#: pretix/static/pretixpresale/js/ui/main.js:416
msgid "required"
msgstr "povinný"
#: pretix/static/pretixpresale/js/ui/main.js:527
#: pretix/static/pretixpresale/js/ui/main.js:546
#: pretix/static/pretixpresale/js/ui/main.js:519
#: pretix/static/pretixpresale/js/ui/main.js:538
msgid "Time zone:"
msgstr "Časové pásmo:"
#: pretix/static/pretixpresale/js/ui/main.js:537
#: pretix/static/pretixpresale/js/ui/main.js:529
msgid "Your local time:"
msgstr "Místní čas:"
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-11-07 15:22+0000\n"
"POT-Creation-Date: 2022-10-11 09:30+0000\n"
"PO-Revision-Date: 2022-04-01 13:36+0000\n"
"Last-Translator: Anna-itk <abc@aarhus.dk>\n"
"Language-Team: Danish <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -130,7 +130,7 @@ msgstr ""
msgid "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:159
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:157
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
#, fuzzy
#| msgctxt "widget"
@@ -138,13 +138,13 @@ msgstr ""
msgid "Continue"
msgstr "Fortsæt"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:217
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:215
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
msgid "Confirming your payment …"
msgstr "Bekræfter din betaling …"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:242
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:240
msgid "Payment method unavailable"
msgstr ""
@@ -665,40 +665,40 @@ msgid_plural "The items in your cart are reserved for you for {num} minutes."
msgstr[0] "Varerne i din kurv er reserveret for dig i et minut."
msgstr[1] "Varerne i din kurv er reserveret for dig i {num} minutter."
#: pretix/static/pretixpresale/js/ui/main.js:152
#: pretix/static/pretixpresale/js/ui/main.js:144
#, fuzzy
#| msgctxt "widget"
#| msgid "from %(currency)s %(price)s"
msgid "The organizer keeps %(currency)s %(amount)s"
msgstr "fra %(currency)s %(price)s"
#: pretix/static/pretixpresale/js/ui/main.js:160
#: pretix/static/pretixpresale/js/ui/main.js:152
#, fuzzy
#| msgctxt "widget"
#| msgid "from %(currency)s %(price)s"
msgid "You get %(currency)s %(amount)s back"
msgstr "fra %(currency)s %(price)s"
#: pretix/static/pretixpresale/js/ui/main.js:176
#: pretix/static/pretixpresale/js/ui/main.js:168
msgid "Please enter the amount the organizer can keep."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:388
#: pretix/static/pretixpresale/js/ui/main.js:380
msgid "Please enter a quantity for one of the ticket types."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:424
#: pretix/static/pretixpresale/js/ui/main.js:416
#, fuzzy
#| msgid "Cart expired"
msgid "required"
msgstr "Kurv udløbet"
#: pretix/static/pretixpresale/js/ui/main.js:527
#: pretix/static/pretixpresale/js/ui/main.js:546
#: pretix/static/pretixpresale/js/ui/main.js:519
#: pretix/static/pretixpresale/js/ui/main.js:538
msgid "Time zone:"
msgstr "Tidszone:"
#: pretix/static/pretixpresale/js/ui/main.js:537
#: pretix/static/pretixpresale/js/ui/main.js:529
msgid "Your local time:"
msgstr "Din lokaltid:"
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-11-07 15:22+0000\n"
"POT-Creation-Date: 2022-10-11 09:30+0000\n"
"PO-Revision-Date: 2022-07-26 08:58+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -129,18 +129,18 @@ msgstr "WeChat Pay"
msgid "Mercado Pago"
msgstr "Mercado Pago"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:159
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:157
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Continue"
msgstr "Fortfahren"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:217
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:215
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
msgid "Confirming your payment …"
msgstr "Zahlung wird bestätigt …"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:242
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:240
msgid "Payment method unavailable"
msgstr "Zahlungsmethode nicht verfügbar"
@@ -638,32 +638,32 @@ msgstr[0] ""
msgstr[1] ""
"Die Produkte in Ihrem Warenkorb sind noch {num} Minuten für Sie reserviert."
#: pretix/static/pretixpresale/js/ui/main.js:152
#: pretix/static/pretixpresale/js/ui/main.js:144
msgid "The organizer keeps %(currency)s %(amount)s"
msgstr "Der Veranstalter behält %(currency)s %(amount)s ein"
#: pretix/static/pretixpresale/js/ui/main.js:160
#: pretix/static/pretixpresale/js/ui/main.js:152
msgid "You get %(currency)s %(amount)s back"
msgstr "Sie erhalten %(currency)s %(amount)s zurück"
#: pretix/static/pretixpresale/js/ui/main.js:176
#: pretix/static/pretixpresale/js/ui/main.js:168
msgid "Please enter the amount the organizer can keep."
msgstr "Bitte geben Sie den Betrag ein, den der Veranstalter einbehalten darf."
#: pretix/static/pretixpresale/js/ui/main.js:388
#: pretix/static/pretixpresale/js/ui/main.js:380
msgid "Please enter a quantity for one of the ticket types."
msgstr "Bitte tragen Sie eine Menge für eines der Produkte ein."
#: pretix/static/pretixpresale/js/ui/main.js:424
#: pretix/static/pretixpresale/js/ui/main.js:416
msgid "required"
msgstr "verpflichtend"
#: pretix/static/pretixpresale/js/ui/main.js:527
#: pretix/static/pretixpresale/js/ui/main.js:546
#: pretix/static/pretixpresale/js/ui/main.js:519
#: pretix/static/pretixpresale/js/ui/main.js:538
msgid "Time zone:"
msgstr "Zeitzone:"
#: pretix/static/pretixpresale/js/ui/main.js:537
#: pretix/static/pretixpresale/js/ui/main.js:529
msgid "Your local time:"
msgstr "Deine lokale Zeit:"
File diff suppressed because it is too large Load Diff
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-11-07 15:22+0000\n"
"POT-Creation-Date: 2022-10-11 09:30+0000\n"
"PO-Revision-Date: 2022-07-26 08:58+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
@@ -129,18 +129,18 @@ msgstr "WeChat Pay"
msgid "Mercado Pago"
msgstr "Mercado Pago"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:159
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:157
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Continue"
msgstr "Fortfahren"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:217
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:215
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
msgid "Confirming your payment …"
msgstr "Zahlung wird bestätigt …"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:242
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:240
msgid "Payment method unavailable"
msgstr "Zahlungsmethode nicht verfügbar"
@@ -637,32 +637,32 @@ msgstr[0] ""
msgstr[1] ""
"Die Produkte in deinem Warenkorb sind noch {num} Minuten für dich reserviert."
#: pretix/static/pretixpresale/js/ui/main.js:152
#: pretix/static/pretixpresale/js/ui/main.js:144
msgid "The organizer keeps %(currency)s %(amount)s"
msgstr "Der Veranstalter behält %(currency)s %(amount)s ein"
#: pretix/static/pretixpresale/js/ui/main.js:160
#: pretix/static/pretixpresale/js/ui/main.js:152
msgid "You get %(currency)s %(amount)s back"
msgstr "Du erhältst %(currency)s %(amount)s zurück"
#: pretix/static/pretixpresale/js/ui/main.js:176
#: pretix/static/pretixpresale/js/ui/main.js:168
msgid "Please enter the amount the organizer can keep."
msgstr "Bitte gib den Betrag ein, den der Veranstalter einbehalten darf."
#: pretix/static/pretixpresale/js/ui/main.js:388
#: pretix/static/pretixpresale/js/ui/main.js:380
msgid "Please enter a quantity for one of the ticket types."
msgstr "Bitte trage eine Menge für eines der Produkte ein."
#: pretix/static/pretixpresale/js/ui/main.js:424
#: pretix/static/pretixpresale/js/ui/main.js:416
msgid "required"
msgstr "verpflichtend"
#: pretix/static/pretixpresale/js/ui/main.js:527
#: pretix/static/pretixpresale/js/ui/main.js:546
#: pretix/static/pretixpresale/js/ui/main.js:519
#: pretix/static/pretixpresale/js/ui/main.js:538
msgid "Time zone:"
msgstr "Zeitzone:"
#: pretix/static/pretixpresale/js/ui/main.js:537
#: pretix/static/pretixpresale/js/ui/main.js:529
msgid "Your local time:"
msgstr "Deine lokale Zeit:"
+905 -1006
View File
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-11-07 15:22+0000\n"
"POT-Creation-Date: 2022-10-11 09:30+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -128,18 +128,18 @@ msgstr ""
msgid "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:159
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:157
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Continue"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:217
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:215
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
msgid "Confirming your payment …"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:242
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:240
msgid "Payment method unavailable"
msgstr ""
@@ -611,32 +611,32 @@ msgid_plural "The items in your cart are reserved for you for {num} minutes."
msgstr[0] ""
msgstr[1] ""
#: pretix/static/pretixpresale/js/ui/main.js:152
#: pretix/static/pretixpresale/js/ui/main.js:144
msgid "The organizer keeps %(currency)s %(amount)s"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:160
#: pretix/static/pretixpresale/js/ui/main.js:152
msgid "You get %(currency)s %(amount)s back"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:176
#: pretix/static/pretixpresale/js/ui/main.js:168
msgid "Please enter the amount the organizer can keep."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:388
#: pretix/static/pretixpresale/js/ui/main.js:380
msgid "Please enter a quantity for one of the ticket types."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:424
#: pretix/static/pretixpresale/js/ui/main.js:416
msgid "required"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:527
#: pretix/static/pretixpresale/js/ui/main.js:546
#: pretix/static/pretixpresale/js/ui/main.js:519
#: pretix/static/pretixpresale/js/ui/main.js:538
msgid "Time zone:"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:537
#: pretix/static/pretixpresale/js/ui/main.js:529
msgid "Your local time:"
msgstr ""
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-11-07 15:22+0000\n"
"POT-Creation-Date: 2022-10-11 09:30+0000\n"
"PO-Revision-Date: 2019-10-03 19:00+0000\n"
"Last-Translator: Chris Spy <chrispiropoulou@hotmail.com>\n"
"Language-Team: Greek <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -131,7 +131,7 @@ msgstr ""
msgid "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:159
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:157
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
#, fuzzy
#| msgctxt "widget"
@@ -139,13 +139,13 @@ msgstr ""
msgid "Continue"
msgstr "Συνέχεια"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:217
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:215
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
msgid "Confirming your payment …"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:242
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:240
msgid "Payment method unavailable"
msgstr ""
@@ -677,40 +677,40 @@ msgid_plural "The items in your cart are reserved for you for {num} minutes."
msgstr[0] "Τα είδη στο καλάθι θα παραμείνουν δεσμευμένα για ένα λεπτό."
msgstr[1] "Τα είδη στο καλάθι θα παραμείνουν δεσμευμένα για {num} λεπτά."
#: pretix/static/pretixpresale/js/ui/main.js:152
#: pretix/static/pretixpresale/js/ui/main.js:144
#, fuzzy
#| msgctxt "widget"
#| msgid "from %(currency)s %(price)s"
msgid "The organizer keeps %(currency)s %(amount)s"
msgstr "απο %(currency)s %(price)s"
#: pretix/static/pretixpresale/js/ui/main.js:160
#: pretix/static/pretixpresale/js/ui/main.js:152
#, fuzzy
#| msgctxt "widget"
#| msgid "from %(currency)s %(price)s"
msgid "You get %(currency)s %(amount)s back"
msgstr "απο %(currency)s %(price)s"
#: pretix/static/pretixpresale/js/ui/main.js:176
#: pretix/static/pretixpresale/js/ui/main.js:168
msgid "Please enter the amount the organizer can keep."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:388
#: pretix/static/pretixpresale/js/ui/main.js:380
msgid "Please enter a quantity for one of the ticket types."
msgstr "Εισαγάγετε μια ποσότητα για έναν από τους τύπους εισιτηρίων."
#: pretix/static/pretixpresale/js/ui/main.js:424
#: pretix/static/pretixpresale/js/ui/main.js:416
#, fuzzy
#| msgid "Cart expired"
msgid "required"
msgstr "Το καλάθι έληξε"
#: pretix/static/pretixpresale/js/ui/main.js:527
#: pretix/static/pretixpresale/js/ui/main.js:546
#: pretix/static/pretixpresale/js/ui/main.js:519
#: pretix/static/pretixpresale/js/ui/main.js:538
msgid "Time zone:"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:537
#: pretix/static/pretixpresale/js/ui/main.js:529
msgid "Your local time:"
msgstr ""
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-11-07 15:22+0000\n"
"POT-Creation-Date: 2022-10-11 09:30+0000\n"
"PO-Revision-Date: 2021-11-25 21:00+0000\n"
"Last-Translator: Ismael Menéndez Fernández <ismael.menendez@balidea.com>\n"
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -131,18 +131,18 @@ msgstr ""
msgid "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:159
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:157
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Continue"
msgstr "Continuar"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:217
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:215
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
msgid "Confirming your payment …"
msgstr "Confirmando el pago…"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:242
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:240
msgid "Payment method unavailable"
msgstr ""
@@ -637,32 +637,32 @@ msgstr[0] ""
msgstr[1] ""
"Los elementos en su carrito de compras se han reservado por {num} minutos."
#: pretix/static/pretixpresale/js/ui/main.js:152
#: pretix/static/pretixpresale/js/ui/main.js:144
msgid "The organizer keeps %(currency)s %(amount)s"
msgstr "El organizador se queda %(currency)s %(price)s"
#: pretix/static/pretixpresale/js/ui/main.js:160
#: pretix/static/pretixpresale/js/ui/main.js:152
msgid "You get %(currency)s %(amount)s back"
msgstr "Obtienes %(currency)s %(price)s de vuelta"
#: pretix/static/pretixpresale/js/ui/main.js:176
#: pretix/static/pretixpresale/js/ui/main.js:168
msgid "Please enter the amount the organizer can keep."
msgstr "Por favor, ingrese el monto que el organizador puede quedarse."
#: pretix/static/pretixpresale/js/ui/main.js:388
#: pretix/static/pretixpresale/js/ui/main.js:380
msgid "Please enter a quantity for one of the ticket types."
msgstr "Por favor, introduzca un valor para cada tipo de entrada."
#: pretix/static/pretixpresale/js/ui/main.js:424
#: pretix/static/pretixpresale/js/ui/main.js:416
msgid "required"
msgstr "campo requerido"
#: pretix/static/pretixpresale/js/ui/main.js:527
#: pretix/static/pretixpresale/js/ui/main.js:546
#: pretix/static/pretixpresale/js/ui/main.js:519
#: pretix/static/pretixpresale/js/ui/main.js:538
msgid "Time zone:"
msgstr "Zona horaria:"
#: pretix/static/pretixpresale/js/ui/main.js:537
#: pretix/static/pretixpresale/js/ui/main.js:529
msgid "Your local time:"
msgstr "Su hora local:"
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-11-07 15:22+0000\n"
"POT-Creation-Date: 2022-10-11 09:30+0000\n"
"PO-Revision-Date: 2021-11-10 05:00+0000\n"
"Last-Translator: Jaakko Rinta-Filppula <jaakko@r-f.fi>\n"
"Language-Team: Finnish <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -131,7 +131,7 @@ msgstr ""
msgid "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:159
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:157
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
#, fuzzy
#| msgctxt "widget"
@@ -139,13 +139,13 @@ msgstr ""
msgid "Continue"
msgstr "Jatka"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:217
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:215
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
msgid "Confirming your payment …"
msgstr "Maksuasi vahvistetaan …"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:242
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:240
msgid "Payment method unavailable"
msgstr ""
@@ -636,34 +636,34 @@ msgid_plural "The items in your cart are reserved for you for {num} minutes."
msgstr[0] "Ostoskorissasi olevat tuotteet eivät ole enää varattu sinulle."
msgstr[1] "Ostoskorissasi olevat tuotteet eivät ole enää varattu sinulle."
#: pretix/static/pretixpresale/js/ui/main.js:152
#: pretix/static/pretixpresale/js/ui/main.js:144
msgid "The organizer keeps %(currency)s %(amount)s"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:160
#: pretix/static/pretixpresale/js/ui/main.js:152
msgid "You get %(currency)s %(amount)s back"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:176
#: pretix/static/pretixpresale/js/ui/main.js:168
msgid "Please enter the amount the organizer can keep."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:388
#: pretix/static/pretixpresale/js/ui/main.js:380
msgid "Please enter a quantity for one of the ticket types."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:424
#: pretix/static/pretixpresale/js/ui/main.js:416
#, fuzzy
#| msgid "Cart expired"
msgid "required"
msgstr "Ostoskori on vanhentunut"
#: pretix/static/pretixpresale/js/ui/main.js:527
#: pretix/static/pretixpresale/js/ui/main.js:546
#: pretix/static/pretixpresale/js/ui/main.js:519
#: pretix/static/pretixpresale/js/ui/main.js:538
msgid "Time zone:"
msgstr "Aikavyöhyke:"
#: pretix/static/pretixpresale/js/ui/main.js:537
#: pretix/static/pretixpresale/js/ui/main.js:529
msgid "Your local time:"
msgstr ""
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: French\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-11-07 15:22+0000\n"
"POT-Creation-Date: 2022-10-11 09:30+0000\n"
"PO-Revision-Date: 2022-04-07 10:40+0000\n"
"Last-Translator: Eva-Maria Obermann <obermann@rami.io>\n"
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -130,7 +130,7 @@ msgstr ""
msgid "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:159
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:157
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
#, fuzzy
#| msgctxt "widget"
@@ -138,13 +138,13 @@ msgstr ""
msgid "Continue"
msgstr "Continuer"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:217
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:215
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
msgid "Confirming your payment …"
msgstr "Confirmation de votre paiment…"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:242
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:240
msgid "Payment method unavailable"
msgstr ""
@@ -665,40 +665,40 @@ msgid_plural "The items in your cart are reserved for you for {num} minutes."
msgstr[0] "Les articles de votre panier sont réservés pour une minute."
msgstr[1] "Les articles de votre panier sont réservés pendant {num} minutes."
#: pretix/static/pretixpresale/js/ui/main.js:152
#: pretix/static/pretixpresale/js/ui/main.js:144
#, fuzzy
#| msgctxt "widget"
#| msgid "from %(currency)s %(price)s"
msgid "The organizer keeps %(currency)s %(amount)s"
msgstr "de %(currency)s %(price)s"
#: pretix/static/pretixpresale/js/ui/main.js:160
#: pretix/static/pretixpresale/js/ui/main.js:152
#, fuzzy
#| msgctxt "widget"
#| msgid "from %(currency)s %(price)s"
msgid "You get %(currency)s %(amount)s back"
msgstr "de %(currency)s %(price)s"
#: pretix/static/pretixpresale/js/ui/main.js:176
#: pretix/static/pretixpresale/js/ui/main.js:168
msgid "Please enter the amount the organizer can keep."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:388
#: pretix/static/pretixpresale/js/ui/main.js:380
msgid "Please enter a quantity for one of the ticket types."
msgstr "SVP entrez une quantité pour un de vos types de billets."
#: pretix/static/pretixpresale/js/ui/main.js:424
#: pretix/static/pretixpresale/js/ui/main.js:416
#, fuzzy
#| msgid "Cart expired"
msgid "required"
msgstr "Panier expiré"
#: pretix/static/pretixpresale/js/ui/main.js:527
#: pretix/static/pretixpresale/js/ui/main.js:546
#: pretix/static/pretixpresale/js/ui/main.js:519
#: pretix/static/pretixpresale/js/ui/main.js:538
msgid "Time zone:"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:537
#: pretix/static/pretixpresale/js/ui/main.js:529
msgid "Your local time:"
msgstr ""
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-11-07 15:22+0000\n"
"POT-Creation-Date: 2022-10-11 09:30+0000\n"
"PO-Revision-Date: 2022-02-22 22:00+0000\n"
"Last-Translator: Ismael Menéndez Fernández <ismael.menendez@balidea.com>\n"
"Language-Team: Galician <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -131,18 +131,18 @@ msgstr ""
msgid "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:159
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:157
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Continue"
msgstr "Continuar"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:217
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:215
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
msgid "Confirming your payment …"
msgstr "Confirmando o pagamento…"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:242
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:240
msgid "Payment method unavailable"
msgstr ""
@@ -634,32 +634,32 @@ msgstr[0] "Os artigos da túa cesta están reservados para ti durante un minuto.
msgstr[1] ""
"Os artigos da túa cesta están reservados para ti durante {num} minutos."
#: pretix/static/pretixpresale/js/ui/main.js:152
#: pretix/static/pretixpresale/js/ui/main.js:144
msgid "The organizer keeps %(currency)s %(amount)s"
msgstr "O organizador queda %(currency)s %(price)s"
#: pretix/static/pretixpresale/js/ui/main.js:160
#: pretix/static/pretixpresale/js/ui/main.js:152
msgid "You get %(currency)s %(amount)s back"
msgstr "Obtés %(currency)s %(price)s de volta"
#: pretix/static/pretixpresale/js/ui/main.js:176
#: pretix/static/pretixpresale/js/ui/main.js:168
msgid "Please enter the amount the organizer can keep."
msgstr "Por favor, ingrese a cantidade que pode conservar o organizador."
#: pretix/static/pretixpresale/js/ui/main.js:388
#: pretix/static/pretixpresale/js/ui/main.js:380
msgid "Please enter a quantity for one of the ticket types."
msgstr "Por favor, introduza un valor para cada tipo de entrada."
#: pretix/static/pretixpresale/js/ui/main.js:424
#: pretix/static/pretixpresale/js/ui/main.js:416
msgid "required"
msgstr "campo requirido"
#: pretix/static/pretixpresale/js/ui/main.js:527
#: pretix/static/pretixpresale/js/ui/main.js:546
#: pretix/static/pretixpresale/js/ui/main.js:519
#: pretix/static/pretixpresale/js/ui/main.js:538
msgid "Time zone:"
msgstr "Zona horaria:"
#: pretix/static/pretixpresale/js/ui/main.js:537
#: pretix/static/pretixpresale/js/ui/main.js:529
msgid "Your local time:"
msgstr "A súa hora local:"
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-11-07 15:22+0000\n"
"POT-Creation-Date: 2022-10-11 09:30+0000\n"
"PO-Revision-Date: 2021-09-24 13:54+0000\n"
"Last-Translator: ofirtro <ofir.tro@gmail.com>\n"
"Language-Team: Hebrew <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -130,18 +130,18 @@ msgstr ""
msgid "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:159
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:157
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Continue"
msgstr "המשך"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:217
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:215
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
msgid "Confirming your payment …"
msgstr "מאמת את התשלום שלך…"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:242
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:240
msgid "Payment method unavailable"
msgstr ""
@@ -619,32 +619,32 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
#: pretix/static/pretixpresale/js/ui/main.js:152
#: pretix/static/pretixpresale/js/ui/main.js:144
msgid "The organizer keeps %(currency)s %(amount)s"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:160
#: pretix/static/pretixpresale/js/ui/main.js:152
msgid "You get %(currency)s %(amount)s back"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:176
#: pretix/static/pretixpresale/js/ui/main.js:168
msgid "Please enter the amount the organizer can keep."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:388
#: pretix/static/pretixpresale/js/ui/main.js:380
msgid "Please enter a quantity for one of the ticket types."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:424
#: pretix/static/pretixpresale/js/ui/main.js:416
msgid "required"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:527
#: pretix/static/pretixpresale/js/ui/main.js:546
#: pretix/static/pretixpresale/js/ui/main.js:519
#: pretix/static/pretixpresale/js/ui/main.js:538
msgid "Time zone:"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:537
#: pretix/static/pretixpresale/js/ui/main.js:529
msgid "Your local time:"
msgstr ""
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-11-07 15:22+0000\n"
"POT-Creation-Date: 2022-10-11 09:30+0000\n"
"PO-Revision-Date: 2020-01-24 08:00+0000\n"
"Last-Translator: Prokaj Miklós <mixolid0@gmail.com>\n"
"Language-Team: Hungarian <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -131,7 +131,7 @@ msgstr ""
msgid "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:159
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:157
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
#, fuzzy
#| msgctxt "widget"
@@ -139,13 +139,13 @@ msgstr ""
msgid "Continue"
msgstr "Folytatás"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:217
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:215
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
msgid "Confirming your payment …"
msgstr "A fizetés megerősítése…"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:242
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:240
msgid "Payment method unavailable"
msgstr ""
@@ -665,40 +665,40 @@ msgid_plural "The items in your cart are reserved for you for {num} minutes."
msgstr[0] "A kosár tartalma egy percig foglalva van számodra."
msgstr[1] "A kosár tartalma {num} percig foglalva van számodra."
#: pretix/static/pretixpresale/js/ui/main.js:152
#: pretix/static/pretixpresale/js/ui/main.js:144
#, fuzzy
#| msgctxt "widget"
#| msgid "from %(currency)s %(price)s"
msgid "The organizer keeps %(currency)s %(amount)s"
msgstr "%(currency) %(price)-tól"
#: pretix/static/pretixpresale/js/ui/main.js:160
#: pretix/static/pretixpresale/js/ui/main.js:152
#, fuzzy
#| msgctxt "widget"
#| msgid "from %(currency)s %(price)s"
msgid "You get %(currency)s %(amount)s back"
msgstr "%(currency) %(price)-tól"
#: pretix/static/pretixpresale/js/ui/main.js:176
#: pretix/static/pretixpresale/js/ui/main.js:168
msgid "Please enter the amount the organizer can keep."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:388
#: pretix/static/pretixpresale/js/ui/main.js:380
msgid "Please enter a quantity for one of the ticket types."
msgstr "Adjon meg egy mennyiséget az egyik jegytípusból."
#: pretix/static/pretixpresale/js/ui/main.js:424
#: pretix/static/pretixpresale/js/ui/main.js:416
#, fuzzy
#| msgid "Cart expired"
msgid "required"
msgstr "A kosár lejárt"
#: pretix/static/pretixpresale/js/ui/main.js:527
#: pretix/static/pretixpresale/js/ui/main.js:546
#: pretix/static/pretixpresale/js/ui/main.js:519
#: pretix/static/pretixpresale/js/ui/main.js:538
msgid "Time zone:"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:537
#: pretix/static/pretixpresale/js/ui/main.js:529
msgid "Your local time:"
msgstr ""
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-11-07 15:22+0000\n"
"POT-Creation-Date: 2022-10-11 09:30+0000\n"
"PO-Revision-Date: 2022-05-08 19:00+0000\n"
"Last-Translator: Emanuele Signoretta <signorettae@gmail.com>\n"
"Language-Team: Italian <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -133,18 +133,18 @@ msgstr ""
msgid "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:159
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:157
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Continue"
msgstr "Continua"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:217
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:215
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
msgid "Confirming your payment …"
msgstr "Stiamo processando il tuo pagamento …"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:242
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:240
msgid "Payment method unavailable"
msgstr ""
@@ -632,32 +632,32 @@ msgid_plural "The items in your cart are reserved for you for {num} minutes."
msgstr[0] "Gli elementi nel tuo carrello sono riservati per 1 minuto."
msgstr[1] "Gli elementi nel tuo carrello sono riservati per {num} minuti."
#: pretix/static/pretixpresale/js/ui/main.js:152
#: pretix/static/pretixpresale/js/ui/main.js:144
msgid "The organizer keeps %(currency)s %(amount)s"
msgstr "L'organizzatore trattiene %(currency)s %(amount)s"
#: pretix/static/pretixpresale/js/ui/main.js:160
#: pretix/static/pretixpresale/js/ui/main.js:152
msgid "You get %(currency)s %(amount)s back"
msgstr "Ricevi indietro %(currency)s %(amount)s"
#: pretix/static/pretixpresale/js/ui/main.js:176
#: pretix/static/pretixpresale/js/ui/main.js:168
msgid "Please enter the amount the organizer can keep."
msgstr "Inserisci l'importo che l'organizzatore può trattenere."
#: pretix/static/pretixpresale/js/ui/main.js:388
#: pretix/static/pretixpresale/js/ui/main.js:380
msgid "Please enter a quantity for one of the ticket types."
msgstr "Inserisci la quantità per una tipologia di biglietto."
#: pretix/static/pretixpresale/js/ui/main.js:424
#: pretix/static/pretixpresale/js/ui/main.js:416
msgid "required"
msgstr "richiesto"
#: pretix/static/pretixpresale/js/ui/main.js:527
#: pretix/static/pretixpresale/js/ui/main.js:546
#: pretix/static/pretixpresale/js/ui/main.js:519
#: pretix/static/pretixpresale/js/ui/main.js:538
msgid "Time zone:"
msgstr "Fuso orario:"
#: pretix/static/pretixpresale/js/ui/main.js:537
#: pretix/static/pretixpresale/js/ui/main.js:529
msgid "Your local time:"
msgstr "Ora locale:"
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-11-07 15:22+0000\n"
"POT-Creation-Date: 2022-10-11 09:30+0000\n"
"PO-Revision-Date: 2022-03-15 00:00+0000\n"
"Last-Translator: Yuriko Matsunami <y.matsunami@enobyte.com>\n"
"Language-Team: Japanese <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -131,18 +131,18 @@ msgstr ""
msgid "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:159
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:157
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Continue"
msgstr "次へ"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:217
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:215
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
msgid "Confirming your payment …"
msgstr "お支払い内容の確認"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:242
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:240
msgid "Payment method unavailable"
msgstr ""
@@ -627,32 +627,32 @@ msgid "The items in your cart are reserved for you for one minute."
msgid_plural "The items in your cart are reserved for you for {num} minutes."
msgstr[0] "カート内の商品の予約は {num} 分以内に完了します。"
#: pretix/static/pretixpresale/js/ui/main.js:152
#: pretix/static/pretixpresale/js/ui/main.js:144
msgid "The organizer keeps %(currency)s %(amount)s"
msgstr "主催者には%(currency)s %(amount)sが与えられます"
#: pretix/static/pretixpresale/js/ui/main.js:160
#: pretix/static/pretixpresale/js/ui/main.js:152
msgid "You get %(currency)s %(amount)s back"
msgstr "%(currency)s %(amount)s が払い戻されます"
#: pretix/static/pretixpresale/js/ui/main.js:176
#: pretix/static/pretixpresale/js/ui/main.js:168
msgid "Please enter the amount the organizer can keep."
msgstr "イベント開催者が受け取る料金を入力してください。"
#: pretix/static/pretixpresale/js/ui/main.js:388
#: pretix/static/pretixpresale/js/ui/main.js:380
msgid "Please enter a quantity for one of the ticket types."
msgstr "商品の総数を入力してください。"
#: pretix/static/pretixpresale/js/ui/main.js:424
#: pretix/static/pretixpresale/js/ui/main.js:416
msgid "required"
msgstr "必須"
#: pretix/static/pretixpresale/js/ui/main.js:527
#: pretix/static/pretixpresale/js/ui/main.js:546
#: pretix/static/pretixpresale/js/ui/main.js:519
#: pretix/static/pretixpresale/js/ui/main.js:538
msgid "Time zone:"
msgstr "タイムゾーン:"
#: pretix/static/pretixpresale/js/ui/main.js:537
#: pretix/static/pretixpresale/js/ui/main.js:529
msgid "Your local time:"
msgstr "現地時間:"
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-11-07 15:22+0000\n"
"POT-Creation-Date: 2022-10-11 09:30+0000\n"
"PO-Revision-Date: 2022-04-06 03:00+0000\n"
"Last-Translator: Liga V <lerning_by_dreaming@gmx.de>\n"
"Language-Team: Latvian <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -132,18 +132,18 @@ msgstr ""
msgid "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:159
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:157
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Continue"
msgstr "Turpināt"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:217
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:215
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
msgid "Confirming your payment …"
msgstr "Jūsu maksājums tiek apstrādāts …"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:242
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:240
msgid "Payment method unavailable"
msgstr ""
@@ -638,32 +638,32 @@ msgstr[0] "Preces jūsu grozā ir rezervētas nulle minūtes."
msgstr[1] "Preces jūsu grozā ir rezervētas vienu minūti."
msgstr[2] "Preces jūsu grozā ir rezervētas uz {num} minūtēm."
#: pretix/static/pretixpresale/js/ui/main.js:152
#: pretix/static/pretixpresale/js/ui/main.js:144
msgid "The organizer keeps %(currency)s %(amount)s"
msgstr "Pasākuma organizators patur %(valūta)s %(skaits)s"
#: pretix/static/pretixpresale/js/ui/main.js:160
#: pretix/static/pretixpresale/js/ui/main.js:152
msgid "You get %(currency)s %(amount)s back"
msgstr "Jūs saņemsiet %(valūta)s %(cena)s atpakaļ"
#: pretix/static/pretixpresale/js/ui/main.js:176
#: pretix/static/pretixpresale/js/ui/main.js:168
msgid "Please enter the amount the organizer can keep."
msgstr "Lūdzu ievadiet skaitu (summu), ko pasākuma organizators var paturēt."
#: pretix/static/pretixpresale/js/ui/main.js:388
#: pretix/static/pretixpresale/js/ui/main.js:380
msgid "Please enter a quantity for one of the ticket types."
msgstr "Lūdzu, ievadiet nepieciešamo daudzumu izvēlētajam biļešu veidam."
#: pretix/static/pretixpresale/js/ui/main.js:424
#: pretix/static/pretixpresale/js/ui/main.js:416
msgid "required"
msgstr "obligāts"
#: pretix/static/pretixpresale/js/ui/main.js:527
#: pretix/static/pretixpresale/js/ui/main.js:546
#: pretix/static/pretixpresale/js/ui/main.js:519
#: pretix/static/pretixpresale/js/ui/main.js:538
msgid "Time zone:"
msgstr "Laika zona:"
#: pretix/static/pretixpresale/js/ui/main.js:537
#: pretix/static/pretixpresale/js/ui/main.js:529
msgid "Your local time:"
msgstr "Vietējais laiks:"
File diff suppressed because it is too large Load Diff

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