forked from CGM_Public/pretix_original
Compare commits
1 Commits
fieldset-p
...
fix-shippi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e2b856887 |
@@ -44,7 +44,7 @@ dependencies = [
|
||||
"django-formtools==2.5.1",
|
||||
"django-hierarkey==1.2.*",
|
||||
"django-hijack==3.7.*",
|
||||
"django-i18nfield==1.9.*,>=1.9.5",
|
||||
"django-i18nfield==1.9.*,>=1.9.4",
|
||||
"django-libsass==0.9",
|
||||
"django-localflavor==4.0",
|
||||
"django-markup",
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.core.exceptions import PermissionDenied, ValidationError
|
||||
from django.db import transaction
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.functional import cached_property
|
||||
@@ -43,7 +43,6 @@ from django.utils.translation import gettext as _
|
||||
from django_countries.serializers import CountryFieldMixin
|
||||
from pytz import common_timezones
|
||||
from rest_framework import serializers
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.fields import ChoiceField, Field
|
||||
from rest_framework.relations import SlugRelatedField
|
||||
|
||||
|
||||
@@ -19,8 +19,57 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from django.conf import settings
|
||||
from django.core.validators import URLValidator
|
||||
from i18nfield.rest_framework import I18nAwareModelSerializer, I18nField
|
||||
from i18nfield.fields import I18nCharField, I18nTextField
|
||||
from i18nfield.strings import LazyI18nString
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.fields import Field
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
|
||||
|
||||
class I18nField(Field):
|
||||
def __init__(self, **kwargs):
|
||||
self.allow_blank = kwargs.pop('allow_blank', False)
|
||||
self.trim_whitespace = kwargs.pop('trim_whitespace', True)
|
||||
self.max_length = kwargs.pop('max_length', None)
|
||||
self.min_length = kwargs.pop('min_length', None)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def to_representation(self, value):
|
||||
if hasattr(value, 'data'):
|
||||
if isinstance(value.data, dict):
|
||||
return value.data
|
||||
elif value.data is None:
|
||||
return None
|
||||
else:
|
||||
return {
|
||||
settings.LANGUAGE_CODE: str(value.data)
|
||||
}
|
||||
elif value is None:
|
||||
return None
|
||||
else:
|
||||
return {
|
||||
settings.LANGUAGE_CODE: str(value)
|
||||
}
|
||||
|
||||
def to_internal_value(self, data):
|
||||
if isinstance(data, str):
|
||||
return LazyI18nString(data)
|
||||
elif isinstance(data, dict):
|
||||
if any([k not in dict(settings.LANGUAGES) for k in data.keys()]):
|
||||
raise ValidationError('Invalid languages included.')
|
||||
return LazyI18nString(data)
|
||||
else:
|
||||
raise ValidationError('Invalid data type.')
|
||||
|
||||
|
||||
class I18nAwareModelSerializer(ModelSerializer):
|
||||
pass
|
||||
|
||||
|
||||
I18nAwareModelSerializer.serializer_field_mapping[I18nCharField] = I18nField
|
||||
I18nAwareModelSerializer.serializer_field_mapping[I18nTextField] = I18nField
|
||||
|
||||
|
||||
class I18nURLField(I18nField):
|
||||
@@ -35,10 +84,3 @@ class I18nURLField(I18nField):
|
||||
else:
|
||||
URLValidator()(value.data)
|
||||
return value
|
||||
|
||||
|
||||
__all__ = [
|
||||
"I18nAwareModelSerializer", # for backwards compatibility
|
||||
"I18nField", # for backwards compatibility
|
||||
"I18nURLField",
|
||||
]
|
||||
|
||||
@@ -277,10 +277,6 @@ class NamePartsFormField(forms.MultiValueField):
|
||||
return value
|
||||
|
||||
|
||||
def name_parts_is_empty(name_parts_dict):
|
||||
return not any(k != "_scheme" and v for k, v in name_parts_dict.items())
|
||||
|
||||
|
||||
class WrappedPhonePrefixSelect(Select):
|
||||
initial = None
|
||||
|
||||
@@ -1153,12 +1149,12 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
||||
data['vat_id'] = ''
|
||||
if data.get('is_business') and not ask_for_vat_id(data.get('country')):
|
||||
data['vat_id'] = ''
|
||||
if self.address_validation and self.event.settings.invoice_address_required and not self.all_optional:
|
||||
if self.event.settings.invoice_address_required:
|
||||
if data.get('is_business') and not data.get('company'):
|
||||
raise ValidationError({"company": _('You need to provide a company name.')})
|
||||
if not data.get('is_business') and name_parts_is_empty(data.get('name_parts', {})):
|
||||
if not data.get('is_business') and not data.get('name_parts'):
|
||||
raise ValidationError(_('You need to provide your name.'))
|
||||
if not data.get('street') and not data.get('zipcode') and not data.get('city'):
|
||||
if not self.all_optional and 'street' in self.fields and not data.get('street') and not data.get('zipcode') and not data.get('city'):
|
||||
raise ValidationError({"street": _('This field is required.')})
|
||||
|
||||
if 'vat_id' in self.changed_data or not data.get('vat_id'):
|
||||
@@ -1171,7 +1167,7 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
||||
|
||||
if all(
|
||||
not v for k, v in data.items() if k not in ('is_business', 'country', 'name_parts')
|
||||
) and name_parts_is_empty(data.get('name_parts', {})):
|
||||
) and len(data.get('name_parts', {})) == 1:
|
||||
# Do not save the country if it is the only field set -- we don't know the user even checked it!
|
||||
self.cleaned_data['country'] = ''
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ class Command(BaseCommand):
|
||||
try:
|
||||
r = receiver(signal=periodic_task, sender=self)
|
||||
except Exception as err:
|
||||
if isinstance(err, KeyboardInterrupt):
|
||||
if isinstance(Exception, KeyboardInterrupt):
|
||||
raise err
|
||||
if settings.SENTRY_ENABLED:
|
||||
from sentry_sdk import capture_exception
|
||||
|
||||
@@ -1419,51 +1419,50 @@ class GiftCardPayment(BasePaymentProvider):
|
||||
def payment_refund_supported(self, payment: OrderPayment) -> bool:
|
||||
return True
|
||||
|
||||
def _add_giftcard_to_cart(self, cs, gc):
|
||||
from pretix.base.services.cart import add_payment_to_cart_session
|
||||
|
||||
if gc.currency != self.event.currency:
|
||||
raise ValidationError(_("This gift card does not support this currency."))
|
||||
if gc.testmode and not self.event.testmode:
|
||||
raise ValidationError(_("This gift card can only be used in test mode."))
|
||||
if not gc.testmode and self.event.testmode:
|
||||
raise ValidationError(_("Only test gift cards can be used in test mode."))
|
||||
if gc.expires and gc.expires < time_machine_now():
|
||||
raise ValidationError(_("This gift card is no longer valid."))
|
||||
if gc.value <= Decimal("0.00"):
|
||||
raise ValidationError(_("All credit on this gift card has been used."))
|
||||
|
||||
for p in cs.get('payments', []):
|
||||
if p['provider'] == self.identifier and p['info_data']['gift_card'] == gc.pk:
|
||||
raise ValidationError(_("This gift card is already used for your payment."))
|
||||
|
||||
add_payment_to_cart_session(
|
||||
cs,
|
||||
self,
|
||||
max_value=gc.value,
|
||||
info_data={
|
||||
'gift_card': gc.pk,
|
||||
'gift_card_secret': gc.secret,
|
||||
}
|
||||
)
|
||||
|
||||
def checkout_prepare(self, request: HttpRequest, cart: Dict[str, Any]) -> Union[bool, str, None]:
|
||||
from pretix.base.services.cart import add_payment_to_cart
|
||||
|
||||
for p in get_cart(request):
|
||||
if p.item.issue_giftcard:
|
||||
messages.error(request, _("You cannot pay with gift cards when buying a gift card."))
|
||||
return
|
||||
|
||||
cs = cart_session(request)
|
||||
try:
|
||||
gc = self.event.organizer.accepted_gift_cards.get(
|
||||
secret=request.POST.get("giftcard").strip()
|
||||
)
|
||||
cs = cart_session(request)
|
||||
try:
|
||||
self._add_giftcard_to_cart(cs, gc)
|
||||
return True
|
||||
except ValidationError as e:
|
||||
messages.error(request, str(e.message))
|
||||
if gc.currency != self.event.currency:
|
||||
messages.error(request, _("This gift card does not support this currency."))
|
||||
return
|
||||
if gc.testmode and not self.event.testmode:
|
||||
messages.error(request, _("This gift card can only be used in test mode."))
|
||||
return
|
||||
if not gc.testmode and self.event.testmode:
|
||||
messages.error(request, _("Only test gift cards can be used in test mode."))
|
||||
return
|
||||
if gc.expires and gc.expires < time_machine_now():
|
||||
messages.error(request, _("This gift card is no longer valid."))
|
||||
return
|
||||
if gc.value <= Decimal("0.00"):
|
||||
messages.error(request, _("All credit on this gift card has been used."))
|
||||
return
|
||||
|
||||
for p in cs.get('payments', []):
|
||||
if p['provider'] == self.identifier and p['info_data']['gift_card'] == gc.pk:
|
||||
messages.error(request, _("This gift card is already used for your payment."))
|
||||
return
|
||||
|
||||
add_payment_to_cart(
|
||||
request,
|
||||
self,
|
||||
max_value=gc.value,
|
||||
info_data={
|
||||
'gift_card': gc.pk,
|
||||
'gift_card_secret': gc.secret,
|
||||
}
|
||||
)
|
||||
return True
|
||||
except GiftCard.DoesNotExist:
|
||||
if self.event.vouchers.filter(code__iexact=request.POST.get("giftcard")).exists():
|
||||
messages.warning(request, _("You entered a voucher instead of a gift card. Vouchers can only be entered on the first page of the shop below "
|
||||
|
||||
@@ -1426,28 +1426,6 @@ class CartManager:
|
||||
raise CartError(err)
|
||||
|
||||
|
||||
def add_payment_to_cart_session(cart_session, provider, min_value: Decimal=None, max_value: Decimal=None, info_data: dict=None):
|
||||
"""
|
||||
:param cart_session: The current cart session.
|
||||
:param provider: The instance of your payment provider.
|
||||
:param min_value: The minimum value this payment instrument supports, or ``None`` for unlimited.
|
||||
:param max_value: The maximum value this payment instrument supports, or ``None`` for unlimited. Highly discouraged
|
||||
to use for payment providers which charge a payment fee, as this can be very user-unfriendly if
|
||||
users need a second payment method just for the payment fee of the first method.
|
||||
:param info_data: A dictionary of information that will be passed through to the ``OrderPayment.info_data`` attribute.
|
||||
:return:
|
||||
"""
|
||||
cart_session.setdefault('payments', [])
|
||||
cart_session['payments'].append({
|
||||
'id': str(uuid.uuid4()),
|
||||
'provider': provider.identifier,
|
||||
'multi_use_supported': provider.multi_use_supported,
|
||||
'min_value': str(min_value) if min_value is not None else None,
|
||||
'max_value': str(max_value) if max_value is not None else None,
|
||||
'info_data': info_data or {},
|
||||
})
|
||||
|
||||
|
||||
def add_payment_to_cart(request, provider, min_value: Decimal=None, max_value: Decimal=None, info_data: dict=None):
|
||||
"""
|
||||
:param request: The current HTTP request context.
|
||||
@@ -1462,7 +1440,16 @@ def add_payment_to_cart(request, provider, min_value: Decimal=None, max_value: D
|
||||
from pretix.presale.views.cart import cart_session
|
||||
|
||||
cs = cart_session(request)
|
||||
add_payment_to_cart_session(cs, provider, min_value, max_value, info_data)
|
||||
cs.setdefault('payments', [])
|
||||
|
||||
cs['payments'].append({
|
||||
'id': str(uuid.uuid4()),
|
||||
'provider': provider.identifier,
|
||||
'multi_use_supported': provider.multi_use_supported,
|
||||
'min_value': str(min_value) if min_value is not None else None,
|
||||
'max_value': str(max_value) if max_value is not None else None,
|
||||
'info_data': info_data or {},
|
||||
})
|
||||
|
||||
|
||||
def get_fees(event, request, total, invoice_address, payments, positions):
|
||||
|
||||
@@ -56,7 +56,6 @@ from django.utils.translation import (
|
||||
from django_countries.fields import Country
|
||||
from hierarkey.models import GlobalSettingsBase, Hierarkey
|
||||
from i18nfield.forms import I18nFormField, I18nTextarea, I18nTextInput
|
||||
from i18nfield.rest_framework import I18nField
|
||||
from i18nfield.strings import LazyI18nString
|
||||
from phonenumbers import PhoneNumber, parse
|
||||
from rest_framework import serializers
|
||||
@@ -64,7 +63,7 @@ from rest_framework import serializers
|
||||
from pretix.api.serializers.fields import (
|
||||
ListMultipleChoiceField, UploadedFileField,
|
||||
)
|
||||
from pretix.api.serializers.i18n import I18nURLField
|
||||
from pretix.api.serializers.i18n import I18nField, I18nURLField
|
||||
from pretix.base.forms import I18nMarkdownTextarea, I18nURLFormField
|
||||
from pretix.base.models.tax import VAT_ID_COUNTRIES, TaxRule
|
||||
from pretix.base.reldate import (
|
||||
|
||||
@@ -52,12 +52,12 @@ def money_filter(value: Decimal, arg='', hide_currency=False):
|
||||
# would make the numbers incorrect. If this branch executes, it's likely a bug in
|
||||
# pretix, but we won't show wrong numbers!
|
||||
if hide_currency:
|
||||
return floatformat(value, "2g")
|
||||
return floatformat(value, 2)
|
||||
else:
|
||||
return '{} {}'.format(arg, floatformat(value, "2g"))
|
||||
return '{} {}'.format(arg, floatformat(value, 2))
|
||||
|
||||
if hide_currency:
|
||||
return floatformat(value, f"{places}g")
|
||||
return floatformat(value, places)
|
||||
|
||||
locale_parts = translation.get_language().split("-", 1)
|
||||
locale = locale_parts[0]
|
||||
@@ -70,7 +70,7 @@ def money_filter(value: Decimal, arg='', hide_currency=False):
|
||||
try:
|
||||
return format_currency(value, arg, locale=locale)
|
||||
except:
|
||||
return '{} {}'.format(arg, floatformat(value, f"{places}g"))
|
||||
return '{} {}'.format(arg, floatformat(value, places))
|
||||
|
||||
|
||||
@register.filter("money_numberfield")
|
||||
|
||||
@@ -220,6 +220,7 @@
|
||||
{% endif %}
|
||||
{% bootstrap_field formset.empty_form.available_from visibility_field=formset.empty_form.available_from_mode layout="control_with_visibility" %}
|
||||
{% bootstrap_field formset.empty_form.available_until visibility_field=formset.empty_form.available_until_mode layout="control_with_visibility" %}
|
||||
{% bootstrap_field formset.empty_form.available_until layout="control" %}
|
||||
{% bootstrap_field formset.empty_form.all_sales_channels layout="control" %}
|
||||
{% bootstrap_field formset.empty_form.limit_sales_channels layout="control" %}
|
||||
{% bootstrap_field formset.empty_form.hide_without_voucher layout="control" %}
|
||||
|
||||
@@ -38,7 +38,6 @@ from zoneinfo import ZoneInfo
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.humanize.templatetags.humanize import intcomma
|
||||
from django.db.models import (
|
||||
Count, IntegerField, Max, Min, OuterRef, Prefetch, Q, Subquery, Sum,
|
||||
)
|
||||
@@ -48,6 +47,7 @@ from django.http import JsonResponse
|
||||
from django.shortcuts import render
|
||||
from django.template.loader import get_template
|
||||
from django.urls import reverse
|
||||
from django.utils import formats
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.html import escape
|
||||
from django.utils.timezone import now
|
||||
@@ -67,7 +67,6 @@ from pretix.control.signals import (
|
||||
from pretix.helpers.daterange import daterange
|
||||
|
||||
from ...base.models.orders import CancellationRequest
|
||||
from ...base.templatetags.money import money_filter
|
||||
from ..logdisplay import OVERVIEW_BANLIST
|
||||
|
||||
NUM_WIDGET = '<div class="numwidget"><span class="num">{num}</span><span class="text">{text}</span></div>'
|
||||
@@ -112,7 +111,7 @@ def base_widgets(sender, subevent=None, lazy=False, **kwargs):
|
||||
|
||||
return [
|
||||
{
|
||||
'content': None if lazy else NUM_WIDGET.format(num=intcomma(tickc), text=_('Attendees (ordered)')),
|
||||
'content': None if lazy else NUM_WIDGET.format(num=tickc, text=_('Attendees (ordered)')),
|
||||
'lazy': 'attendees-ordered',
|
||||
'display_size': 'small',
|
||||
'priority': 100,
|
||||
@@ -122,7 +121,7 @@ def base_widgets(sender, subevent=None, lazy=False, **kwargs):
|
||||
}) + ('?subevent={}'.format(subevent.pk) if subevent else '')
|
||||
},
|
||||
{
|
||||
'content': None if lazy else NUM_WIDGET.format(num=intcomma(paidc), text=_('Attendees (paid)')),
|
||||
'content': None if lazy else NUM_WIDGET.format(num=paidc, text=_('Attendees (paid)')),
|
||||
'lazy': 'attendees-paid',
|
||||
'display_size': 'small',
|
||||
'priority': 100,
|
||||
@@ -133,9 +132,7 @@ def base_widgets(sender, subevent=None, lazy=False, **kwargs):
|
||||
},
|
||||
{
|
||||
'content': None if lazy else NUM_WIDGET.format(
|
||||
num=money_filter(round_decimal(rev, sender.currency), sender.currency, hide_currency=True),
|
||||
text=_('Total revenue ({currency})').format(currency=sender.currency)
|
||||
),
|
||||
num=formats.localize(round_decimal(rev, sender.currency)), text=_('Total revenue ({currency})').format(currency=sender.currency)),
|
||||
'lazy': 'total-revenue',
|
||||
'display_size': 'small',
|
||||
'priority': 100,
|
||||
@@ -210,7 +207,7 @@ def waitinglist_widgets(sender, subevent=None, lazy=False, **kwargs):
|
||||
|
||||
widgets.append({
|
||||
'content': None if lazy else NUM_WIDGET.format(
|
||||
num=intcomma(happy), text=_('available to give to people on waiting list')
|
||||
num=str(happy), text=_('available to give to people on waiting list')
|
||||
),
|
||||
'lazy': 'waitinglist-avail',
|
||||
'priority': 50,
|
||||
@@ -220,7 +217,7 @@ def waitinglist_widgets(sender, subevent=None, lazy=False, **kwargs):
|
||||
})
|
||||
})
|
||||
widgets.append({
|
||||
'content': None if lazy else NUM_WIDGET.format(num=intcomma(wles.count()), text=_('total waiting list length')),
|
||||
'content': None if lazy else NUM_WIDGET.format(num=str(wles.count()), text=_('total waiting list length')),
|
||||
'lazy': 'waitinglist-length',
|
||||
'display_size': 'small',
|
||||
'priority': 50,
|
||||
@@ -248,7 +245,7 @@ def quota_widgets(sender, subevent=None, lazy=False, **kwargs):
|
||||
status, left = qa.results[q] if q in qa.results else q.availability(allow_cache=True)
|
||||
widgets.append({
|
||||
'content': None if lazy else NUM_WIDGET.format(
|
||||
num='{}/{}'.format(intcomma(left), intcomma(q.size)) if q.size is not None else '\u221e',
|
||||
num='{}/{}'.format(left, q.size) if q.size is not None else '\u221e',
|
||||
text=_('{quota} left').format(quota=escape(q.name))
|
||||
),
|
||||
'lazy': 'quota-{}'.format(q.pk),
|
||||
@@ -300,7 +297,7 @@ def checkin_widget(sender, subevent=None, lazy=False, **kwargs):
|
||||
for cl in qs:
|
||||
widgets.append({
|
||||
'content': None if lazy else NUM_WIDGET.format(
|
||||
num='{}/{}'.format(intcomma(cl.inside_count), intcomma(cl.position_count)),
|
||||
num='{}/{}'.format(cl.inside_count, cl.position_count),
|
||||
text=_('Present – {list}').format(list=escape(cl.name))
|
||||
),
|
||||
'lazy': 'checkin-{}'.format(cl.pk),
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-11-19 15:34+0000\n"
|
||||
"PO-Revision-Date: 2024-12-03 20:00+0000\n"
|
||||
"PO-Revision-Date: 2024-11-29 23:00+0000\n"
|
||||
"Last-Translator: CVZ-es <damien.bremont@casadevelazquez.org>\n"
|
||||
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix/"
|
||||
"es/>\n"
|
||||
@@ -31498,7 +31498,7 @@ msgstr "Lo sentimos, se ha producido un error en el proceso de pago."
|
||||
#: pretix/plugins/ticketoutputpdf/apps.py:44
|
||||
#: pretix/plugins/ticketoutputpdf/apps.py:47
|
||||
msgid "PDF ticket output"
|
||||
msgstr "Salida de entradas en PDF"
|
||||
msgstr "Salida de entradas de PDF"
|
||||
|
||||
#: pretix/plugins/ticketoutputpdf/apps.py:52
|
||||
msgid ""
|
||||
|
||||
@@ -4,7 +4,7 @@ msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-11-19 15:34+0000\n"
|
||||
"PO-Revision-Date: 2024-12-03 20:00+0000\n"
|
||||
"PO-Revision-Date: 2024-11-29 23:00+0000\n"
|
||||
"Last-Translator: CVZ-es <damien.bremont@casadevelazquez.org>\n"
|
||||
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix/fr/"
|
||||
">\n"
|
||||
@@ -8954,7 +8954,7 @@ msgstr "Nombre maximum d'articles par commande"
|
||||
|
||||
#: pretix/base/settings.py:316
|
||||
msgid "Add-on products will not be counted."
|
||||
msgstr "Les Add-Ons e sont pas comptabilisés."
|
||||
msgstr "Les Add-Ons ne seront pas pris en compte."
|
||||
|
||||
#: pretix/base/settings.py:325
|
||||
msgid ""
|
||||
@@ -20630,16 +20630,17 @@ msgid ""
|
||||
"product. You can also specify the minimum and maximum number of add-ons of "
|
||||
"the given category that can or need to be chosen."
|
||||
msgstr ""
|
||||
"Avec les add-ons, vous pouvez spécifier des produits qui peuvent être "
|
||||
"achetés en complément de ce produit. Par exemple, si vous organisez une "
|
||||
"conférence avec un ticket de conférence de base et un certain nombre d’"
|
||||
"ateliers, vous pouvez définir les ateliers comme des modules complémentaires "
|
||||
"au ticket de conférence. Avec cette configuration, les ateliers ne peuvent "
|
||||
"pas être achetés seuls, mais uniquement en combinaison avec un billet de "
|
||||
"conférence. Vous pouvez spécifier ici des catégories de produits qui peuvent "
|
||||
"être utilisés comme modules complémentaires à ce produit. Vous pouvez "
|
||||
"également spécifier le nombre minimal et maximal de modules complémentaires "
|
||||
"de la catégorie donnée qui peuvent ou doivent être choisis."
|
||||
"Avec les modules complémentaires, vous pouvez spécifier des produits qui "
|
||||
"peuvent être achetés en complément de ce produit. Par exemple, si vous "
|
||||
"organisez une conférence avec un ticket de conférence de base et un certain "
|
||||
"nombre d’ateliers, vous pouvez définir les ateliers comme des modules "
|
||||
"complémentaires au ticket de conférence. Avec cette configuration, les "
|
||||
"ateliers ne peuvent pas être achetés seuls, mais uniquement en combinaison "
|
||||
"avec un billet de conférence. Vous pouvez spécifier ici des catégories de "
|
||||
"produits qui peuvent être utilisés comme modules complémentaires à ce "
|
||||
"produit. Vous pouvez également spécifier le nombre minimal et maximal de "
|
||||
"modules complémentaires de la catégorie donnée qui peuvent ou doivent être "
|
||||
"choisis."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/item/include_addons.html:28
|
||||
#: pretix/control/templates/pretixcontrol/item/include_addons.html:62
|
||||
@@ -31761,7 +31762,7 @@ msgstr "Désolé, une erreur s’est produite dans le processus de paiement."
|
||||
#: pretix/plugins/ticketoutputpdf/apps.py:44
|
||||
#: pretix/plugins/ticketoutputpdf/apps.py:47
|
||||
msgid "PDF ticket output"
|
||||
msgstr "Génération de billets au format PDF"
|
||||
msgstr "Sortie du ticket PDF"
|
||||
|
||||
#: pretix/plugins/ticketoutputpdf/apps.py:52
|
||||
msgid ""
|
||||
|
||||
@@ -7,7 +7,7 @@ msgstr ""
|
||||
"Project-Id-Version: French\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-11-08 13:45+0000\n"
|
||||
"PO-Revision-Date: 2024-12-03 20:00+0000\n"
|
||||
"PO-Revision-Date: 2024-11-16 05:00+0000\n"
|
||||
"Last-Translator: CVZ-es <damien.bremont@casadevelazquez.org>\n"
|
||||
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
"fr/>\n"
|
||||
@@ -16,7 +16,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n > 1;\n"
|
||||
"X-Generator: Weblate 5.8.4\n"
|
||||
"X-Generator: Weblate 5.8.3\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||
@@ -150,7 +150,7 @@ msgstr "Méthode de paiement non disponible"
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:15
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
|
||||
msgid "Placed orders"
|
||||
msgstr "Commandes réalisées"
|
||||
msgstr "Commandes placées"
|
||||
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:15
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
|
||||
|
||||
@@ -92,8 +92,8 @@ var pretixpaypal = {
|
||||
}
|
||||
|
||||
// We are setting the cogwheel already here, as the renderAPM() method might take some time to get loaded.
|
||||
const apmtextselector = $("input[name=payment][value=paypal_apm]").closest("label").find(".accordion-label-text");
|
||||
apmtextselector.append(' <span aria-hidden="true" class="fa fa-cog fa-spin"></span>');
|
||||
let apmtextselector = $("label[for=input_payment_paypal_apm]");
|
||||
apmtextselector.prepend('<span class="fa fa-cog fa-spin"></span> ');
|
||||
|
||||
let sdk_url = 'https://www.paypal.com/sdk/js' +
|
||||
'?client-id=' + pretixpaypal.client_id +
|
||||
@@ -269,7 +269,11 @@ var pretixpaypal = {
|
||||
renderAPMs: function () {
|
||||
pretixpaypal.restore();
|
||||
let inputselector = $("input[name=payment][value=paypal_apm]");
|
||||
let textselector = inputselector.closest("label").find('.accordion-label-text');
|
||||
// The first selector is used on the regular payment-step of the checkout flow
|
||||
// The second selector is used for the payment method change view.
|
||||
// In the long run, the layout of both pages should be adjusted to be one.
|
||||
let textselector = $("label[for=input_payment_paypal_apm]");
|
||||
let textselector2 = inputselector.next("strong");
|
||||
let eligibles = [];
|
||||
|
||||
pretixpaypal.paypal.getFundingSources().forEach(function (fundingSource) {
|
||||
@@ -293,6 +297,10 @@ var pretixpaypal = {
|
||||
textselector.text(eligibles.join(', '));
|
||||
textselector.fadeIn(300);
|
||||
});
|
||||
textselector2.fadeOut(300, function () {
|
||||
textselector2[0].textContent = eligibles.join(', ');
|
||||
textselector2.fadeIn(300);
|
||||
});
|
||||
},
|
||||
|
||||
guessLocale: function() {
|
||||
|
||||
@@ -388,6 +388,21 @@ class StripeSettingsHolder(BasePaymentProvider):
|
||||
}
|
||||
),
|
||||
)),
|
||||
('method_sofort',
|
||||
forms.BooleanField(
|
||||
label=_('SOFORT'),
|
||||
disabled=self.event.currency != 'EUR',
|
||||
help_text=(
|
||||
_('Stripe is in the process of removing this payment method. If you created your Stripe '
|
||||
'account after November 2023, you cannot use this payment method.') +
|
||||
'<div class="alert alert-warning">%s</div>' % _(
|
||||
'Despite the name, Sofort payments via Stripe are <strong>not</strong> processed '
|
||||
'instantly but might take up to <strong>14 days</strong> to be confirmed in some cases. '
|
||||
'Please only activate this payment method if your payment term allows for this lag.'
|
||||
)
|
||||
),
|
||||
required=False,
|
||||
)),
|
||||
('method_eps',
|
||||
forms.BooleanField(
|
||||
label=_('EPS'),
|
||||
|
||||
@@ -8,118 +8,134 @@
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="panel-group" id="customer">
|
||||
<fieldset class="panel panel-default accordion-panel">
|
||||
<legend class="accordion-radio">
|
||||
<label class="panel-heading">
|
||||
<span class="panel-title">
|
||||
<div class="panel panel-default">
|
||||
<div class="accordion-radio">
|
||||
<div class="panel-heading">
|
||||
<p class="panel-title">
|
||||
<input type="radio" name="customer_mode" value="login"
|
||||
data-parent="#customer"
|
||||
{% if selected == "login" or not signup_allowed %}checked="checked"{% endif %}
|
||||
aria-controls="customer_login" />
|
||||
{% trans "Log in with a customer account" %}
|
||||
</span>
|
||||
</label>
|
||||
</legend>
|
||||
<div id="customer_login" class="panel-body form-horizontal">
|
||||
{% if customer %}
|
||||
<p>
|
||||
{% blocktrans trimmed with org=request.organizer.name %}
|
||||
You are currently logged in with the following credentials.
|
||||
{% endblocktrans %}
|
||||
id="input_customer_login"
|
||||
data-toggle="radiocollapse" data-target="#customer_login"/>
|
||||
<label for="input_customer_login"><strong>{% trans "Log in with a customer account" %}</strong></label>
|
||||
</p>
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "Email" %}</dt>
|
||||
<dd>
|
||||
{{ customer.email }}
|
||||
</dd>
|
||||
<dt>{% trans "Name" %}</dt>
|
||||
<dd>{{ customer.name }}</dd>
|
||||
<dt>{% trans "Customer ID" %}</dt>
|
||||
<dd>
|
||||
#{{ customer.identifier }}
|
||||
</dd>
|
||||
</dl>
|
||||
{% else %}
|
||||
<p>
|
||||
{% blocktrans trimmed with org=request.organizer.name %}
|
||||
If you created a customer account at {{ org }} before, you can log in now and connect
|
||||
your order to your account. This will allow you to see all your orders in one place
|
||||
and access them at any time.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% if request.organizer.settings.customer_accounts_native %}
|
||||
{% bootstrap_form login_form layout="checkout" %}
|
||||
</div>
|
||||
</div>
|
||||
<div id="customer_login"
|
||||
class="panel-collapse collapsed {% if selected == "login" or not signup_allowed %}in{% endif %}">
|
||||
<div class="panel-body form-horizontal">
|
||||
{% if customer %}
|
||||
<p>
|
||||
{% blocktrans trimmed with org=request.organizer.name %}
|
||||
You are currently logged in with the following credentials.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "Email" %}</dt>
|
||||
<dd>
|
||||
{{ customer.email }}
|
||||
</dd>
|
||||
<dt>{% trans "Name" %}</dt>
|
||||
<dd>{{ customer.name }}</dd>
|
||||
<dt>{% trans "Customer ID" %}</dt>
|
||||
<dd>
|
||||
#{{ customer.identifier }}
|
||||
</dd>
|
||||
</dl>
|
||||
{% else %}
|
||||
<p>
|
||||
{% blocktrans trimmed with org=request.organizer.name %}
|
||||
If you created a customer account at {{ org }} before, you can log in now and connect
|
||||
your order to your account. This will allow you to see all your orders in one place
|
||||
and access them at any time.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% if request.organizer.settings.customer_accounts_native %}
|
||||
{% bootstrap_form login_form layout="checkout" %}
|
||||
<div class="row">
|
||||
<div class="col-md-offset-3 col-md-9">
|
||||
<a
|
||||
href="{% abseventurl request.organizer "presale:organizer.customer.resetpw" %}"
|
||||
target="_blank">
|
||||
{% trans "Reset password" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
<div class="col-md-offset-3 col-md-9">
|
||||
<a
|
||||
href="{% abseventurl request.organizer "presale:organizer.customer.resetpw" %}"
|
||||
target="_blank">
|
||||
{% trans "Reset password" %}
|
||||
</a>
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
{% for provider in request.organizer.sso_providers.all %}
|
||||
{% if provider.is_active %}
|
||||
<a href="{% eventurl request.organizer "presale:organizer.customer.login" provider=provider.pk %}?next={% if request.event_domain %}{{ request.scheme }}://{{ request.get_host }}{% endif %}{{ request.get_full_path|urlencode }}"
|
||||
class="btn btn-primary btn-lg btn-block" data-open-in-popup-window>
|
||||
{{ provider.button_label }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="login-sso-data" id="login_sso_data">
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
{% for provider in request.organizer.sso_providers.all %}
|
||||
{% if provider.is_active %}
|
||||
<a href="{% eventurl request.organizer "presale:organizer.customer.login" provider=provider.pk %}?next={% if request.event_domain %}{{ request.scheme }}://{{ request.get_host }}{% endif %}{{ request.get_full_path|urlencode }}"
|
||||
class="btn btn-primary btn-lg btn-block" data-open-in-popup-window>
|
||||
{{ provider.button_label }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="login-sso-data" id="login_sso_data">
|
||||
{% endif %}
|
||||
</div>
|
||||
</fieldset>
|
||||
{% if signup_allowed %}
|
||||
<fieldset class="panel panel-default accordion-panel">
|
||||
<legend class="accordion-radio">
|
||||
<label class="panel-heading">
|
||||
<span class="panel-title">
|
||||
<input type="radio" name="customer_mode" value="register"
|
||||
{% if selected == "register" %}checked="checked"{% endif %}
|
||||
aria-controls="customer_register" />
|
||||
{% trans "Create a new customer account" %}
|
||||
</span>
|
||||
</label>
|
||||
</legend>
|
||||
<div id="customer_register" class="panel-body form-horizontal">
|
||||
{% bootstrap_form register_form layout="checkout" %}
|
||||
<p>
|
||||
{% blocktrans trimmed with org=request.organizer.name %}
|
||||
We will send you an email with a link to activate your account and set a password, so
|
||||
you can use the account for future orders at {{ org }}. You can still go ahead with this
|
||||
purchase before you received the email.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
{% if signup_allowed %}
|
||||
<div class="panel panel-default">
|
||||
<div class="accordion-radio">
|
||||
<div class="panel-heading">
|
||||
<p class="panel-title">
|
||||
<input type="radio" name="customer_mode" value="register"
|
||||
data-parent="#customer"
|
||||
{% if selected == "register" %}checked="checked"{% endif %}
|
||||
id="input_customer_register"
|
||||
data-toggle="radiocollapse" data-target="#customer_register"/>
|
||||
<label for="input_customer_register"><strong>{% trans "Create a new customer account" %}</strong></label>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="customer_register"
|
||||
class="panel-collapse collapsed {% if selected == "register" %}in{% endif %}">
|
||||
<div class="panel-body form-horizontal">
|
||||
{% bootstrap_form register_form layout="checkout" %}
|
||||
<p>
|
||||
{% blocktrans trimmed with org=request.organizer.name %}
|
||||
We will send you an email with a link to activate your account and set a password, so
|
||||
you can use the account for future orders at {{ org }}. You can still go ahead with this
|
||||
purchase before you received the email.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if guest_allowed %}
|
||||
<fieldset class="panel panel-default accordion-panel">
|
||||
<legend class="accordion-radio">
|
||||
<label class="panel-heading">
|
||||
<span class="panel-title">
|
||||
<div class="panel panel-default">
|
||||
<div class="accordion-radio">
|
||||
<div class="panel-heading">
|
||||
<p class="panel-title">
|
||||
<input type="radio" name="customer_mode" value="guest"
|
||||
data-parent="#customer"
|
||||
{% if selected == "guest" %}checked="checked"{% endif %}
|
||||
aria-controls="customer_guest" />
|
||||
{% trans "Continue as a guest" %}
|
||||
</span>
|
||||
</label>
|
||||
</legend>
|
||||
<div id="customer_guest" class="panel-body form-horizontal">
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
You are not required to create an account. If you proceed as a guest, you will be able
|
||||
to access the details and status of your order any time through the secret link we will
|
||||
send you via email once the order is complete.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
id="input_customer_guest"
|
||||
aria-describedby="customer_guest"
|
||||
data-toggle="radiocollapse" data-target="#customer_guest"/>
|
||||
<label for="input_customer_guest"><strong>{% trans "Continue as a guest" %}</strong></label>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div id="customer_guest"
|
||||
class="panel-collapse collapsed {% if selected == "guest" %}in{% endif %}">
|
||||
<div class="panel-body form-horizontal">
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
You are not required to create an account. If you proceed as a guest, you will be able
|
||||
to access the details and status of your order any time through the secret link we will
|
||||
send you via email once the order is complete.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="row checkout-button-row">
|
||||
|
||||
@@ -62,52 +62,58 @@
|
||||
{% endif %}
|
||||
<div class="panel-group" id="payment_accordion">
|
||||
{% for p in providers %}
|
||||
<fieldset class="panel panel-default accordion-panel">
|
||||
<legend class="accordion-radio">
|
||||
<label class="panel-heading">
|
||||
<span class="panel-title">
|
||||
<div class="panel panel-default">
|
||||
<div class="accordion-radio">
|
||||
<div class="panel-heading">
|
||||
<p class="panel-title">
|
||||
{% if show_fees %}
|
||||
<strong class="pull-right flip">{% if p.fee < 0 %}-{% else %}+{% endif %} {{ p.fee|money:event.currency|cut:"-" }}</strong>
|
||||
{% endif %}
|
||||
<input type="radio" name="payment" value="{{ p.provider.identifier }}"
|
||||
title="{{ p.provider.public_name }}"
|
||||
data-parent="#payment_accordion"
|
||||
{% if selected == p.provider.identifier %}checked="checked"{% endif %}
|
||||
aria-controls="payment_{{ p.provider.identifier }}"
|
||||
id="input_payment_{{ p.provider.identifier }}"
|
||||
aria-describedby="payment_{{ p.provider.identifier }}"
|
||||
data-toggle="radiocollapse" data-target="#payment_{{ p.provider.identifier }}"
|
||||
data-wallets="{{ p.provider.walletqueries|join:"|" }}" />
|
||||
<strong class="accordion-label-text">{{ p.provider.public_name }}</strong>
|
||||
</span>
|
||||
</label>
|
||||
</legend>
|
||||
<div id="payment_{{ p.provider.identifier }}" class="panel-body form-horizontal">
|
||||
{% if request.event.testmode %}
|
||||
{% if p.provider.test_mode_message %}
|
||||
<div class="alert alert-info">
|
||||
<p>{{ p.provider.test_mode_message }}</p>
|
||||
</div>
|
||||
{% if not request.sales_channel.type_instance.testmode_supported %}
|
||||
<div class="alert alert-danger">
|
||||
<label for="input_payment_{{ p.provider.identifier }}"><strong>{{ p.provider.public_name }}</strong></label>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="payment_{{ p.provider.identifier }}"
|
||||
class="panel-collapse collapsed {% if selected == p.provider.identifier %}in{% endif %}">
|
||||
<div class="panel-body form-horizontal">
|
||||
{% if request.event.testmode %}
|
||||
{% if p.provider.test_mode_message %}
|
||||
<div class="alert alert-info">
|
||||
<p>{{ p.provider.test_mode_message }}</p>
|
||||
</div>
|
||||
{% if not request.sales_channel.type_instance.testmode_supported %}
|
||||
<div class="alert alert-danger">
|
||||
<p>
|
||||
{% trans "This sales channel does not provide support for test mode." %}
|
||||
<strong>
|
||||
{% trans "If you continue, you might pay an actual order with non-existing money!" %}
|
||||
</strong>
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="alert alert-warning">
|
||||
<p>
|
||||
{% trans "This sales channel does not provide support for test mode." %}
|
||||
{% trans "This payment provider does not provide support for test mode." %}
|
||||
<strong>
|
||||
{% trans "If you continue, you might pay an actual order with non-existing money!" %}
|
||||
{% trans "If you continue, actual money might be transferred." %}
|
||||
</strong>
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="alert alert-warning">
|
||||
<p>
|
||||
{% trans "This payment provider does not provide support for test mode." %}
|
||||
<strong>
|
||||
{% trans "If you continue, actual money might be transferred." %}
|
||||
</strong>
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{{ p.form }}
|
||||
{{ p.form }}
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% if not providers %}
|
||||
<p><em>{% trans "There are no payment providers enabled." %}</em></p>
|
||||
|
||||
@@ -6,14 +6,13 @@
|
||||
{% load eventsignal %}
|
||||
{% load rich_text %}
|
||||
{% for c in form.categories %}
|
||||
{% with category_idx=forloop.counter %}
|
||||
<fieldset data-addon-max-count="{{ c.max_count }}"{% if c.multi_allowed %} data-addon-multi-allowed{% endif %}>
|
||||
<legend>{{ c.category.name }}</legend>
|
||||
{% if c.category.description %}
|
||||
{{ c.category.description|rich_text }}
|
||||
{% endif %}
|
||||
{% if c.min_count == c.max_count %}
|
||||
<p class="addon-count-desc" id="c-{{ form.pos.pk }}-{{ category_idx }}-addon-count-desc">
|
||||
<p class="addon-count-desc">
|
||||
{% blocktrans trimmed count min_count=c.min_count %}
|
||||
You need to choose exactly one option from this category.
|
||||
{% plural %}
|
||||
@@ -22,7 +21,7 @@
|
||||
</p>
|
||||
{% elif c.min_count == 0 and c.max_count >= c.items|length and not c.multi_allowed %}
|
||||
{% elif c.min_count == 0 %}
|
||||
<p class="addon-count-desc" id="c-{{ form.pos.pk }}-{{ category_idx }}-addon-count-desc">
|
||||
<p class="addon-count-desc">
|
||||
{% blocktrans trimmed count max_count=c.max_count %}
|
||||
You can choose {{ max_count }} option from this category.
|
||||
{% plural %}
|
||||
@@ -30,7 +29,7 @@
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% else %}
|
||||
<p class="addon-count-desc" id="c-{{ form.pos.pk }}-{{ category_idx }}-addon-count-desc">
|
||||
<p class="addon-count-desc">
|
||||
{% blocktrans trimmed with min_count=c.min_count max_count=c.max_count %}
|
||||
You can choose between {{ min_count }} and {{ max_count }} options from
|
||||
this category.
|
||||
@@ -59,7 +58,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if item.min_per_order and item.min_per_order > 1 %}
|
||||
<p id="cp-{{ form.pos.pk }}-item-{{ item.pk }}-min-order">
|
||||
<p>
|
||||
<small>
|
||||
{% blocktrans trimmed with num=item.min_per_order %}
|
||||
minimum amount to order: {{ num }}
|
||||
@@ -197,14 +196,12 @@
|
||||
{% endif %}
|
||||
id="cp_{{ form.pos.pk }}_variation_{{ item.id }}_{{ var.id }}"
|
||||
name="cp_{{ form.pos.pk }}_variation_{{ item.id }}_{{ var.id }}"
|
||||
aria-label="{% blocktrans with item=item.name var=var %}Add {{ item }}, {{ var }} to cart{% endblocktrans %}"
|
||||
aria-describedby="c-{{ form.pos.pk }}-{{ category_idx }}-addon-count-desc">
|
||||
aria-label="{% blocktrans with item=item.name var=var %}Add {{ item }}, {{ var }} to cart{% endblocktrans %}">
|
||||
<i class="fa fa-shopping-cart" aria-hidden="true"></i>
|
||||
{% trans "Select" context "checkbox" %}
|
||||
</label>
|
||||
{% else %}
|
||||
<fieldset class="input-item-count-group" aria-describedby="c-{{ form.pos.pk }}-{{ category_idx }}-addon-count-desc cp-{{ form.pos.pk }}-item-{{ item.pk }}-min-order">
|
||||
<legend class="sr-only">{% blocktrans with item=item.name %}Add {{ item }}, {{ var }} to cart{% endblocktrans %}</legend>
|
||||
<div class="input-item-count-group">
|
||||
<button type="button" data-step="-1" data-controls="cp_{{ form.pos.pk }}_variation_{{ item.id }}_{{ var.id }}" class="btn btn-default input-item-count-dec" aria-label="{% trans "Decrease quantity" %}">-</button>
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
{% if var.initial %}value="{{ var.initial }}"{% endif %}
|
||||
@@ -214,9 +211,9 @@
|
||||
max="{{ c.max_count }}"
|
||||
id="cp_{{ form.pos.pk }}_variation_{{ item.id }}_{{ var.id }}"
|
||||
name="cp_{{ form.pos.pk }}_variation_{{ item.id }}_{{ var.id }}"
|
||||
aria-label="{% trans "Quantity" %}">
|
||||
aria-label="{% blocktrans with item=item.name var=var %}Quantity of {{ item }}, {{ var }} to order{% endblocktrans %}">
|
||||
<button type="button" data-step="1" data-controls="cp_{{ form.pos.pk }}_variation_{{ item.id }}_{{ var.id }}" class="btn btn-default input-item-count-inc" aria-label="{% trans "Increase quantity" %}">+</button>
|
||||
</fieldset>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
@@ -253,7 +250,7 @@
|
||||
{% include "pretixpresale/event/fragment_quota_left.html" with avail=item.cached_availability %}
|
||||
{% endif %}
|
||||
{% if item.min_per_order and item.min_per_order > 1 %}
|
||||
<p id="cp-{{ form.pos.pk }}-item-{{ item.pk }}-min-order">
|
||||
<p>
|
||||
<small>
|
||||
{% blocktrans trimmed with num=item.min_per_order %}
|
||||
minimum amount to order: {{ num }}
|
||||
@@ -344,13 +341,12 @@
|
||||
name="cp_{{ form.pos.pk }}_item_{{ item.id }}"
|
||||
id="cp_{{ form.pos.pk }}_item_{{ item.id }}"
|
||||
aria-label="{% blocktrans with item=item.name %}Add {{ item }} to cart{% endblocktrans %}"
|
||||
aria-describedby="c-{{ form.pos.pk }}-{{ category_idx }}-addon-count-desc">
|
||||
{% if item.description %} aria-describedby="cp-{{ form.pos.pk }}-item-{{ item.id }}-description"{% endif %}>
|
||||
<i class="fa fa-shopping-cart" aria-hidden="true"></i>
|
||||
{% trans "Select" context "checkbox" %}
|
||||
</label>
|
||||
{% else %}
|
||||
<fieldset class="input-item-count-group" aria-describedby="c-{{ form.pos.pk }}-{{ category_idx }}-addon-count-desc cp-{{ form.pos.pk }}-item-{{ item.pk }}-min-order">
|
||||
<legend class="sr-only">{% blocktrans with item=item.name %}Add {{ item }} to cart{% endblocktrans %}</legend>
|
||||
<div class="input-item-count-group">
|
||||
<button type="button" data-step="-1" data-controls="cp_{{ form.pos.pk }}_item_{{ item.id }}" class="btn btn-default input-item-count-dec" aria-label="{% trans "Decrease quantity" %}">-</button>
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
{% if item.free_price %}
|
||||
@@ -360,9 +356,10 @@
|
||||
{% if item.initial %}value="{{ item.initial }}"{% endif %}
|
||||
name="cp_{{ form.pos.pk }}_item_{{ item.id }}"
|
||||
id="cp_{{ form.pos.pk }}_item_{{ item.id }}"
|
||||
aria-label="{% trans "Quantity" %}">
|
||||
aria-label="{% blocktrans with item=item.name %}Quantity of {{ item }} to order{% endblocktrans %}"
|
||||
{% if item.description %} aria-describedby="cp-{{ form.pos.pk }}-item-{{ item.id }}-description"{% endif %}>
|
||||
<button type="button" data-step="1" data-controls="cp_{{ form.pos.pk }}_item_{{ item.id }}" class="btn btn-default input-item-count-inc" aria-label="{% trans "Increase quantity" %}">+</button>
|
||||
</fieldset>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
@@ -373,7 +370,6 @@
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
{% endwith %}
|
||||
{% empty %}
|
||||
<em>
|
||||
{% trans "There are no add-ons available for this product." %}
|
||||
|
||||
@@ -10,41 +10,18 @@
|
||||
<i class="fa fa-shopping-cart" aria-hidden="true"></i>
|
||||
<strong>{% trans "Your cart" %}</strong>
|
||||
</span>
|
||||
{% if cart.positions %}
|
||||
<strong id="cart-deadline-short" data-expires="{{ cart.first_expiry|date:"Y-m-d H:i:sO" }}" aria-hidden="true">
|
||||
{% if cart.minutes_left > 0 or cart.seconds_left > 0 %}
|
||||
{{ cart.minutes_left|stringformat:"02d" }}:{{ cart.seconds_left|stringformat:"02d" }}
|
||||
{% else %}
|
||||
{% trans "Cart expired" %}
|
||||
{% endif %}
|
||||
</strong>
|
||||
{% endif %}
|
||||
<strong id="cart-deadline-short" data-expires="{{ cart.first_expiry|date:"Y-m-d H:i:sO" }}" aria-hidden="true">
|
||||
{% if cart.minutes_left > 0 or cart.seconds_left > 0 %}
|
||||
{{ cart.minutes_left|stringformat:"02d" }}:{{ cart.seconds_left|stringformat:"02d" }}
|
||||
{% else %}
|
||||
{% trans "Cart expired" %}
|
||||
{% endif %}
|
||||
</strong>
|
||||
</h2>
|
||||
</summary>
|
||||
<div>
|
||||
<div class="panel-body">
|
||||
{% if cart.positions %}
|
||||
{% include "pretixpresale/event/fragment_cart.html" with cart=cart event=request.event editable=True %}
|
||||
{% endif %}
|
||||
{% if cart.current_selected_payments %}
|
||||
<p>{% trans "You already selected the following payment methods:" %}</p>
|
||||
<div class="list-group">
|
||||
{% for p in cart.current_selected_payments %}
|
||||
<div class="list-group-item">
|
||||
<div class="row">
|
||||
<div class="col-xs-9">
|
||||
{{ p.provider_name }}
|
||||
</div>
|
||||
<div class="col-xs-3 text-right">
|
||||
{% if p.payment_amount %}
|
||||
{{ p.payment_amount|money:request.event.currency }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% include "pretixpresale/event/fragment_cart.html" with cart=cart event=request.event editable=True %}
|
||||
<div class="checkout-button-row">
|
||||
<form class="checkout-button-primary" method="get" action="{% eventurl request.event "presale:event.checkout.start" cart_namespace=cart_namespace %}">
|
||||
<p><button class="btn btn-primary btn-lg" type="submit"{% if has_addon_choices or cart.total == 0 %} aria-label="{% trans "Continue with order process" %}"{% endif %}>
|
||||
|
||||
@@ -219,8 +219,7 @@
|
||||
{% trans "Select" context "checkbox" %}
|
||||
</label>
|
||||
{% else %}
|
||||
<fieldset class="input-item-count-group">
|
||||
<legend class="sr-only">{% blocktrans with item=item.name %}Add {{ item }}, {{ var }} to cart{% endblocktrans %}</legend>
|
||||
<div class="input-item-count-group">
|
||||
<button type="button" data-step="-1" data-controls="{{ form_prefix }}variation_{{ item.id }}_{{ var.id }}" class="btn btn-default input-item-count-dec" aria-label="{% trans "Decrease quantity" %}"
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}>-</button>
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
@@ -231,10 +230,10 @@
|
||||
max="{{ var.order_max }}"
|
||||
id="{{ form_prefix }}variation_{{ item.id }}_{{ var.id }}"
|
||||
name="{{ form_prefix }}variation_{{ item.id }}_{{ var.id }}"
|
||||
aria-label="{% trans "Quantity" %}">
|
||||
aria-label="{% blocktrans with item=item.name var=var.name %}Quantity of {{ item }}, {{ var }} to order{% endblocktrans %}">
|
||||
<button type="button" data-step="1" data-controls="{{ form_prefix }}variation_{{ item.id }}_{{ var.id }}" class="btn btn-default input-item-count-inc" aria-label="{% trans "Increase quantity" %}"
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}>+</button>
|
||||
</fieldset>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
@@ -371,8 +370,7 @@
|
||||
{% trans "Select" context "checkbox" %}
|
||||
</label>
|
||||
{% else %}
|
||||
<fieldset class="input-item-count-group">
|
||||
<legend class="sr-only">{% blocktrans with item=item.name %}Add {{ item }} to cart{% endblocktrans %}</legend>
|
||||
<div class="input-item-count-group">
|
||||
<button type="button" data-step="-1" data-controls="{{ form_prefix }}item_{{ item.id }}" class="btn btn-default input-item-count-dec" aria-label="{% trans "Decrease quantity" %}"
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}>-</button>
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
@@ -384,10 +382,11 @@
|
||||
max="{{ item.order_max }}"
|
||||
name="{{ form_prefix }}item_{{ item.id }}"
|
||||
id="{{ form_prefix }}item_{{ item.id }}"
|
||||
aria-label="{% trans "Quantity" %}">
|
||||
aria-label="{% blocktrans with item=item.name %}Quantity of {{ item }} to order{% endblocktrans %}"
|
||||
{% if item.description %} aria-describedby="{{ form_prefix }}item-{{ item.id }}-description"{% endif %}>
|
||||
<button type="button" data-step="1" data-controls="{{ form_prefix }}item_{{ item.id }}" class="btn btn-default input-item-count-inc" aria-label="{% trans "Increase quantity" %}"
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}>+</button>
|
||||
</fieldset>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
|
||||
@@ -29,24 +29,29 @@
|
||||
{% endif %}
|
||||
<div class="panel-group" id="payment_accordion">
|
||||
{% for p in providers %}
|
||||
<fieldset class="panel panel-default accordion-panel" data-total="{{ p.total|money_numberfield:request.event.currency }}">
|
||||
<legend class="accordion-radio">
|
||||
<label class="panel-heading">
|
||||
<span class="panel-title">
|
||||
<div class="panel panel-default" data-total="{{ p.total|money_numberfield:request.event.currency }}">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<label class="radio">
|
||||
{% if show_fees %}
|
||||
<strong class="pull-right flip">{% if p.fee_diff >= 0 %}+{% else %}-{% endif %} {{ p.fee_diff_abs|money:event.currency }}</strong>
|
||||
{% endif %}
|
||||
<input type="radio" name="payment" value="{{ p.provider.identifier }}"
|
||||
data-parent="#payment_accordion"
|
||||
{% if selected == p.provider.identifier %}checked="checked"{% endif %}
|
||||
data-toggle="radiocollapse" data-target="#payment_{{ p.provider.identifier }}"
|
||||
data-wallets="{{ p.provider.walletqueries|join:"|" }}"/>
|
||||
<strong class="accordion-label-text">{{ p.provider.public_name }}</strong>
|
||||
</span>
|
||||
</label>
|
||||
</legend>
|
||||
<div id="payment_{{ p.provider.identifier }}" class="panel-body form-horizontal">
|
||||
{{ p.form }}
|
||||
<strong>{{ p.provider.public_name }}</strong>
|
||||
</label>
|
||||
</h4>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div id="payment_{{ p.provider.identifier }}"
|
||||
class="panel-collapse collapsed {% if selected == p.provider.identifier %}in{% endif %}">
|
||||
<div class="panel-body form-horizontal">
|
||||
{{ p.form }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="alert alert-info">
|
||||
{% trans "There are no alternative payment providers available for this order." %}
|
||||
|
||||
@@ -231,17 +231,16 @@
|
||||
{% trans "Select" context "checkbox" %}
|
||||
</label>
|
||||
{% else %}
|
||||
<fieldset class="input-item-count-group">
|
||||
<legend class="sr-only">{% blocktrans with item=item.name %}Add {{ item }}, {{ var }} to cart{% endblocktrans %}</legend>
|
||||
<div class="input-item-count-group">
|
||||
<button type="button" data-step="-1" data-controls="variation_{{ item.id }}_{{ var.id }}" class="btn btn-default input-item-count-dec" aria-label="{% trans "Decrease quantity" %}">-</button>
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
max="{{ var.order_max }}"
|
||||
id="variation_{{ item.id }}_{{ var.id }}"
|
||||
name="variation_{{ item.id }}_{{ var.id }}"
|
||||
{% if options == 1 %}value="1"{% endif %}
|
||||
aria-label="{% trans "Quantity" %}">
|
||||
aria-label="{% blocktrans with item=item.name var=var.name %}Quantity of {{ item }}, {{ var }} to order{% endblocktrans %}">
|
||||
<button type="button" data-step="1" data-controls="variation_{{ item.id }}_{{ var.id }}" class="btn btn-default input-item-count-inc" aria-label="{% trans "Increase quantity" %}">+</button>
|
||||
</fieldset>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<label>
|
||||
@@ -386,8 +385,7 @@
|
||||
{% trans "Select" context "checkbox" %}
|
||||
</label>
|
||||
{% else %}
|
||||
<fieldset class="input-item-count-group">
|
||||
<legend class="sr-only">{% blocktrans with item=item.name %}Add {{ item }} to cart{% endblocktrans %}</legend>
|
||||
<div class="input-item-count-group">
|
||||
<button type="button" data-step="-1" data-controls="item_{{ item.id }}" class="btn btn-default input-item-count-dec" aria-label="{% trans "Decrease quantity" %}">-</button>
|
||||
<input type="number" class="form-control input-item-count"
|
||||
placeholder="0" min="0"
|
||||
@@ -395,9 +393,10 @@
|
||||
id="item_{{ item.id }}"
|
||||
name="item_{{ item.id }}"
|
||||
{% if options == 1 %}value="1"{% endif %}
|
||||
aria-label="{% trans "Quantity" %}">
|
||||
aria-label="{% blocktrans with item=item.name %}Quantity of {{ item }} to order{% endblocktrans %}"
|
||||
{% if item.description %} aria-describedby="item-{{ item.id }}-description"{% endif %}>
|
||||
<button type="button" data-step="1" data-controls="item_{{ item.id }}" class="btn btn-default input-item-count-inc" aria-label="{% trans "Increase quantity" %}">+</button>
|
||||
</fieldset>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<label>
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
{% extends "pretixpresale/organizers/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load icon %}
|
||||
{% load rich_text %}
|
||||
{% load tz %}
|
||||
{% load eventurl %}
|
||||
{% load urlreplace %}
|
||||
{% load textbubble %}
|
||||
{% load thumb %}
|
||||
{% block title %}{% trans "Event list" %}{% endblock %}
|
||||
{% block custom_header %}
|
||||
@@ -88,73 +86,51 @@
|
||||
<td>
|
||||
</div>
|
||||
<div class="col-md-2 col-xs-6">
|
||||
<small>
|
||||
{% if e.has_subevents %}
|
||||
{% textbubble "info" icon="bars" %}
|
||||
{% trans "Event series" %}
|
||||
{% endtextbubble %}
|
||||
<span class="label label-default">{% trans "Event series" %}</span>
|
||||
{% elif e.presale_is_running and request.organizer.settings.event_list_availability %}
|
||||
{% if e.best_availability_state == 100 %}
|
||||
{% if e.best_availability_is_low %}
|
||||
{% textbubble "success-warning" icon="exclamation-triangle" %}
|
||||
{% trans "Few tickets left" %}
|
||||
{% endtextbubble %}
|
||||
<span class="label label-success-warning">{% trans "Few tickets left" %}</span>
|
||||
{% else %}
|
||||
{% textbubble "success" icon="check" %}
|
||||
{% if e.has_paid_item %}
|
||||
{% trans "Buy now" context "available_event_in_list" %}
|
||||
{% else %}
|
||||
{% trans "Book now" %}
|
||||
{% endif %}
|
||||
{% endtextbubble %}
|
||||
{% if e.has_paid_item %}
|
||||
<span class="label label-success">{% trans "Buy now" context "available_event_in_list" %}</span>
|
||||
{% else %}
|
||||
<span class="label label-success">{% trans "Book now" %}</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% elif e.waiting_list_active and e.best_availability_state >= 0 %}
|
||||
{% textbubble "warning" icon="ellipsis-h" %}
|
||||
{% trans "Waiting list" %}
|
||||
{% endtextbubble %}
|
||||
<span class="label label-warning">{% trans "Waiting list" %}</span>
|
||||
{% elif e.best_availability_state == 20 %}
|
||||
{% textbubble "danger" icon="minus" %}
|
||||
{% trans "Reserved" %}
|
||||
{% endtextbubble %}
|
||||
<span class="label label-danger">{% trans "Reserved" %}</span>
|
||||
{% elif e.best_availability_state < 20 %}
|
||||
{% textbubble "danger" icon="times" %}
|
||||
{% if e.has_paid_item %}
|
||||
{% trans "Sold out" %}
|
||||
{% else %}
|
||||
{% trans "Fully booked" %}
|
||||
{% endif %}
|
||||
{% endtextbubble %}
|
||||
{% if e.has_paid_item %}
|
||||
<span class="label label-danger">{% trans "Sold out" %}</span>
|
||||
{% else %}
|
||||
<span class="label label-danger">{% trans "Fully booked" %}</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% elif e.presale_is_running %}
|
||||
{% textbubble "success" icon="check" %}
|
||||
{% trans "Book now" %}
|
||||
{% endtextbubble %}
|
||||
<span class="label label-success">{% trans "Book now" %}</span>
|
||||
{% elif e.presale_has_ended %}
|
||||
{% textbubble "danger" icon="times" %}
|
||||
{% trans "Sale over" %}
|
||||
{% endtextbubble %}
|
||||
<span class="label label-danger">{% trans "Sale over" %}</span>
|
||||
{% elif e.settings.presale_start_show_date %}
|
||||
{% textbubble "warning" icon="clock-o" %}
|
||||
{% with date_iso=e.effective_presale_start.isoformat date_human=e.effective_presale_start|date:"SHORT_DATE_FORMAT" %}
|
||||
{% blocktrans trimmed with date='<time datetime="'|add:date_iso|add:'">'|add:date_human|add:"</time>"|safe %}
|
||||
<span class="label label-warning">
|
||||
{% blocktrans trimmed with date=e.effective_presale_start|date:"SHORT_DATE_FORMAT" %}
|
||||
Sale starts {{ date }}
|
||||
{% endblocktrans %}
|
||||
{% endwith %}
|
||||
{% endtextbubble %}
|
||||
</span>
|
||||
{% else %}
|
||||
{% textbubble "warning" icon="clock-o" %}
|
||||
{% trans "Not yet on sale" %}
|
||||
{% endtextbubble %}
|
||||
<span class="label label-warning">{% trans "Not yet on sale" %}</span>
|
||||
{% endif %}
|
||||
</small>
|
||||
</div>
|
||||
<div class="col-md-2 col-xs-6 text-right flip">
|
||||
<a class="btn btn-primary btn-block" href="{{ url }}{% if e.has_subevents and e.match_by_subevents %}{{ filterquery }}{% endif %}">
|
||||
{% if e.has_subevents %}{% icon "ticket" %} {% trans "Tickets" %}
|
||||
{% if e.has_subevents %}<span class="fa fa-ticket" aria-hidden="true"></span> {% trans "Tickets" %}
|
||||
{% elif e.presale_is_running and e.best_availability_state == 100 %}
|
||||
{% icon "ticket" %} {% trans "Tickets" %}
|
||||
<span class="fa fa-ticket" aria-hidden="true"></span> {% trans "Tickets" %}
|
||||
{% else %}
|
||||
{% icon "info" %} {% trans "More info" %}
|
||||
<span class="fa fa-info" aria-hidden="true"></span> {% trans "More info" %}
|
||||
{% endif %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -251,8 +251,7 @@ class CartMixin:
|
||||
'seconds_left': seconds_left,
|
||||
'first_expiry': first_expiry,
|
||||
'is_ordered': bool(order),
|
||||
'itemcount': sum(c.count for c in positions if not c.addon_to),
|
||||
'current_selected_payments': [p for p in self.current_selected_payments(total) if p.get('multi_use_supported')]
|
||||
'itemcount': sum(c.count for c in positions if not c.addon_to)
|
||||
}
|
||||
|
||||
def current_selected_payments(self, total, warn=False, total_includes_payment_fees=False):
|
||||
|
||||
@@ -42,7 +42,6 @@ from urllib.parse import quote
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.core.cache import caches
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db.models import Q
|
||||
from django.http import FileResponse, Http404, JsonResponse
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
@@ -58,7 +57,7 @@ from django.views.generic import TemplateView, View
|
||||
from django_scopes import scopes_disabled
|
||||
|
||||
from pretix.base.models import (
|
||||
CartPosition, GiftCard, InvoiceAddress, QuestionAnswer, SubEvent, Voucher,
|
||||
CartPosition, InvoiceAddress, QuestionAnswer, SubEvent, Voucher,
|
||||
)
|
||||
from pretix.base.services.cart import (
|
||||
CartError, add_items_to_cart, apply_voucher, clear_cart, error_messages,
|
||||
@@ -439,48 +438,8 @@ class CartApplyVoucher(EventViewMixin, CartActionMixin, AsyncAction, View):
|
||||
return _('We applied the voucher to as many products in your cart as we could.')
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
from pretix.base.payment import GiftCardPayment
|
||||
|
||||
if 'voucher' in request.POST:
|
||||
code = request.POST.get('voucher').strip()
|
||||
|
||||
if not self.request.event.vouchers.filter(code__iexact=code):
|
||||
try:
|
||||
gc = self.request.event.organizer.accepted_gift_cards.get(secret=code)
|
||||
gcp = GiftCardPayment(self.request.event)
|
||||
if not gcp.is_enabled or not gcp.is_allowed(self.request, Decimal("1.00")):
|
||||
raise ValidationError(error_messages['voucher_invalid'])
|
||||
else:
|
||||
cs = cart_session(request)
|
||||
gcp._add_giftcard_to_cart(cs, gc)
|
||||
messages.success(
|
||||
request,
|
||||
_("The gift card has been saved to your cart. Please continue your checkout.")
|
||||
)
|
||||
if "ajax" in self.request.POST or "ajax" in self.request.GET:
|
||||
return JsonResponse({
|
||||
'ready': True,
|
||||
'success': True,
|
||||
'redirect': self.get_success_url(),
|
||||
'message': str(
|
||||
_("The gift card has been saved to your cart. Please continue your checkout.")
|
||||
)
|
||||
})
|
||||
return redirect_to_url(self.get_success_url())
|
||||
except GiftCard.DoesNotExist:
|
||||
pass
|
||||
except ValidationError as e:
|
||||
messages.error(self.request, str(e.message))
|
||||
if "ajax" in self.request.POST or "ajax" in self.request.GET:
|
||||
return JsonResponse({
|
||||
'ready': True,
|
||||
'success': False,
|
||||
'redirect': self.get_success_url(),
|
||||
'message': str(e.message)
|
||||
})
|
||||
return redirect_to_url(self.get_error_url())
|
||||
|
||||
return self.do(self.request.event.id, code, get_or_create_cart_id(self.request),
|
||||
return self.do(self.request.event.id, request.POST.get('voucher'), get_or_create_cart_id(self.request),
|
||||
translation.get_language(), request.sales_channel.identifier,
|
||||
time_machine_now(default=None))
|
||||
else:
|
||||
@@ -672,8 +631,6 @@ class RedeemView(NoSearchIndexViewMixin, EventViewMixin, CartMixin, TemplateView
|
||||
return context
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
from pretix.base.payment import GiftCardPayment
|
||||
|
||||
err = None
|
||||
v = request.GET.get('voucher')
|
||||
|
||||
@@ -696,24 +653,10 @@ class RedeemView(NoSearchIndexViewMixin, EventViewMixin, CartMixin, TemplateView
|
||||
if v_avail < 1 and not err:
|
||||
err = error_messages['voucher_redeemed_cart'] % self.request.event.settings.reservation_time
|
||||
except Voucher.DoesNotExist:
|
||||
try:
|
||||
gc = self.request.event.organizer.accepted_gift_cards.get(secret=v.strip())
|
||||
gcp = GiftCardPayment(self.request.event)
|
||||
if not gcp.is_enabled or not gcp.is_allowed(self.request, Decimal("1.00")):
|
||||
err = error_messages['voucher_invalid']
|
||||
else:
|
||||
cs = cart_session(request)
|
||||
gcp._add_giftcard_to_cart(cs, gc)
|
||||
messages.success(
|
||||
request,
|
||||
_("The gift card has been saved to your cart. Please now select the products "
|
||||
"you want to purchase.")
|
||||
)
|
||||
return redirect_to_url(self.get_next_url())
|
||||
except GiftCard.DoesNotExist:
|
||||
if self.request.event.organizer.accepted_gift_cards.filter(secret__iexact=request.GET.get("voucher")).exists():
|
||||
err = error_messages['gift_card']
|
||||
else:
|
||||
err = error_messages['voucher_invalid']
|
||||
except ValidationError as e:
|
||||
err = str(e.message)
|
||||
else:
|
||||
context = {}
|
||||
context['cart'] = self.get_cart()
|
||||
|
||||
@@ -632,7 +632,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
|
||||
context['subevent_list_cache_key'] = self._subevent_list_cachekey()
|
||||
|
||||
context['show_cart'] = (
|
||||
(context['cart']['positions'] or context['cart'].get('current_selected_payments')) and (
|
||||
context['cart']['positions'] and (
|
||||
self.request.event.has_subevents or self.request.event.presale_is_running
|
||||
)
|
||||
)
|
||||
|
||||
@@ -275,12 +275,12 @@
|
||||
}
|
||||
|
||||
// Apply the mixin to the panel headings only
|
||||
.panel-default > .panel-heading, .panel-default > legend > .panel-heading { @include panel-heading-styles($panel-default-heading-bg); }
|
||||
.panel-primary > .panel-heading, .panel-primary > legend > .panel-heading { @include panel-heading-styles($panel-primary-heading-bg); }
|
||||
.panel-success > .panel-heading, .panel-success > legend > .panel-heading { @include panel-heading-styles($panel-success-heading-bg); }
|
||||
.panel-info > .panel-heading, .panel-info > legend > .panel-heading { @include panel-heading-styles($panel-info-heading-bg); }
|
||||
.panel-warning > .panel-heading, .panel-warning > legend > .panel-heading { @include panel-heading-styles($panel-warning-heading-bg); }
|
||||
.panel-danger > .panel-heading, .panel-danger > legend > .panel-heading { @include panel-heading-styles($panel-danger-heading-bg); }
|
||||
.panel-default > .panel-heading { @include panel-heading-styles($panel-default-heading-bg); }
|
||||
.panel-primary > .panel-heading { @include panel-heading-styles($panel-primary-heading-bg); }
|
||||
.panel-success > .panel-heading { @include panel-heading-styles($panel-success-heading-bg); }
|
||||
.panel-info > .panel-heading { @include panel-heading-styles($panel-info-heading-bg); }
|
||||
.panel-warning > .panel-heading { @include panel-heading-styles($panel-warning-heading-bg); }
|
||||
.panel-danger > .panel-heading { @include panel-heading-styles($panel-danger-heading-bg); }
|
||||
|
||||
|
||||
//
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
@mixin panel-variant($border, $heading-text-color, $heading-bg-color, $heading-border) {
|
||||
border-color: $border;
|
||||
|
||||
& > .panel-heading, & > legend > .panel-heading {
|
||||
& > .panel-heading {
|
||||
color: $heading-text-color;
|
||||
background-color: $heading-bg-color;
|
||||
border-color: $heading-border;
|
||||
|
||||
@@ -39,7 +39,7 @@ $(function () {
|
||||
required = 'required' in options && options.required && isRequired && visible;
|
||||
|
||||
dependent.closest(".form-group").toggle(visible).toggleClass('required', required);
|
||||
dependent.prop("required", required);
|
||||
dependent.prop("required", required && !dependent.is("[data-no-required-attr]"));
|
||||
}
|
||||
for (var k in dependents) dependents[k].prop("disabled", false);
|
||||
}).always(function() {
|
||||
@@ -52,7 +52,7 @@ $(function () {
|
||||
required = false;
|
||||
|
||||
dependent.closest(".form-group").toggle(visible).toggleClass('required', required);
|
||||
dependent.prop("required", required);
|
||||
dependent.prop("required", required && !dependent.is("[data-no-required-attr]"));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -543,7 +543,7 @@ table td > .checkbox input[type="checkbox"] {
|
||||
display: block;
|
||||
margin: 0;
|
||||
}
|
||||
.panel-default>.accordion-radio>.panel-heading, fieldset.accordion-panel>legend>.panel-heading {
|
||||
.panel-default>.accordion-radio>.panel-heading {
|
||||
color: #333;
|
||||
background-color: #f5f5f5;
|
||||
padding: 12px 15px;
|
||||
@@ -555,9 +555,6 @@ table td > .checkbox input[type="checkbox"] {
|
||||
top: 2px;
|
||||
}
|
||||
}
|
||||
fieldset.accordion-panel > legend {
|
||||
display: contents;
|
||||
}
|
||||
.maildesignpreview {
|
||||
label {
|
||||
display: block;
|
||||
|
||||
@@ -243,11 +243,6 @@ function setup_basics(el) {
|
||||
$($(this).attr("data-target")).collapse('show');
|
||||
}
|
||||
});
|
||||
$("fieldset.accordion-panel > legend input[type=radio]").change(function() {
|
||||
$(this).closest("fieldset").siblings("fieldset").prop('disabled', true).children('.panel-body').slideUp();
|
||||
$(this).closest("fieldset").prop('disabled', false).children('.panel-body').slideDown();
|
||||
}).filter(':not(:checked)').each(function() { $(this).closest("fieldset").prop('disabled', true).children('.panel-body').hide(); });
|
||||
|
||||
el.find(".js-only").removeClass("js-only");
|
||||
el.find(".js-hidden").hide();
|
||||
|
||||
|
||||
@@ -53,8 +53,8 @@ $(function () {
|
||||
});
|
||||
|
||||
wallets.forEach(function(wallet) {
|
||||
const labels = $('[data-wallets*='+wallet+'] + .accordion-label-text')
|
||||
.append('<span class="wallet wallet-loading" data-wallet="'+wallet+'"> <span aria-hidden="true" class="fa fa-cog fa-spin"></span></span>')
|
||||
const labels = $('[data-wallets*='+wallet+'] + label strong, [data-wallets*='+wallet+'] + strong')
|
||||
.append('<span class="wallet wallet-loading" data-wallet="'+wallet+'"> <i aria-hidden="true" class="fa fa-cog fa-spin"></i></span>')
|
||||
walletdetection[wallet]()
|
||||
.then(function(result) {
|
||||
const spans = labels.find(".wallet-loading[data-wallet=" + wallet + "]");
|
||||
|
||||
@@ -136,10 +136,6 @@ article.item-with-variations .product-row:last-child:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.panel-body:not(:has(*)) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.panel-body > *:last-child,
|
||||
.panel-body address:last-child {
|
||||
margin-bottom: 0;
|
||||
|
||||
@@ -134,14 +134,10 @@ a.btn, button.btn {
|
||||
display: block;
|
||||
margin: 0;
|
||||
}
|
||||
.panel-default>.accordion-radio>.panel-heading, fieldset.accordion-panel>legend>.panel-heading {
|
||||
display: block;
|
||||
.panel-default>.accordion-radio>.panel-heading {
|
||||
color: #333;
|
||||
background-color: #f5f5f5;
|
||||
padding: 8px 15px;
|
||||
margin: 0;
|
||||
line-height: 1.428571429;
|
||||
font-size: 16px;
|
||||
|
||||
input[type=radio] {
|
||||
margin-top: 0;
|
||||
@@ -151,13 +147,6 @@ a.btn, button.btn {
|
||||
.panel-default>.accordion-radio+.panel-collapse>.panel-body {
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
fieldset.accordion-panel > legend {
|
||||
display: contents;
|
||||
}
|
||||
fieldset[disabled] legend input[type="radio"],
|
||||
fieldset[disabled] legend input[type="checkbox"] {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.nav-tabs {
|
||||
border-bottom: 0px solid #ddd;
|
||||
|
||||
@@ -335,11 +335,6 @@ body.loading .container {
|
||||
}
|
||||
}
|
||||
|
||||
.font-normal {
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.blank-after {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
@@ -536,7 +531,7 @@ h2 .label {
|
||||
}
|
||||
|
||||
|
||||
.textbubble-success, .textbubble-success-warning, .textbubble-info, .textbubble-warning, .textbubble-danger {
|
||||
.textbubble-success, .textbubble-info, .textbubble-warning, .textbubble-danger {
|
||||
padding: 0 .4em;
|
||||
border-radius: $border-radius-base;
|
||||
font-weight: bold;
|
||||
@@ -553,13 +548,6 @@ h2 .label {
|
||||
color: var(--pretix-brand-success);
|
||||
}
|
||||
}
|
||||
.textbubble-success-warning {
|
||||
color: var(--pretix-brand-success-shade-42);
|
||||
background: linear-gradient(to right, var(--pretix-brand-warning-tint-85), var(--pretix-brand-success-tint-85) 50%);
|
||||
.fa {
|
||||
color: var(--pretix-brand-warning);
|
||||
}
|
||||
}
|
||||
.textbubble-info {
|
||||
color: var(--pretix-brand-info-shade-42);
|
||||
background: var(--pretix-brand-info-tint-85);
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-2021 rami.io GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from pretix.base.forms.questions import name_parts_is_empty
|
||||
|
||||
|
||||
def test_name_parts_is_empty():
|
||||
assert name_parts_is_empty({}) is True
|
||||
assert name_parts_is_empty({"_scheme": "foo"}) is True
|
||||
assert name_parts_is_empty({"_scheme": "foo", "full_name": ""}) is True
|
||||
assert name_parts_is_empty({"full_name": None}) is True
|
||||
assert name_parts_is_empty({"full_name": "Flora Nord"}) is False
|
||||
assert name_parts_is_empty({"_scheme": "foo", "given_name": "Alice"}) is False
|
||||
assert name_parts_is_empty({"_legacy": "Alice"}) is False
|
||||
@@ -77,7 +77,7 @@ def test_urlreplace_replace_parameter():
|
||||
|
||||
# rounding errors
|
||||
("de", Decimal("1.234"), "EUR", "1,23" + NBSP + "€"),
|
||||
("de", Decimal("1023.1"), "JPY", "JPY 1.023,10"),
|
||||
("de", Decimal("1023.1"), "JPY", "JPY 1023,10"),
|
||||
]
|
||||
)
|
||||
def test_money_filter(locale, amount, currency, expected):
|
||||
@@ -99,9 +99,9 @@ def test_money_filter(locale, amount, currency, expected):
|
||||
@pytest.mark.parametrize(
|
||||
"locale,amount,currency,expected",
|
||||
[
|
||||
("de", Decimal("1000.00"), "EUR", "1.000,00"),
|
||||
("en", Decimal("1000.00"), "EUR", "1,000.00"),
|
||||
("de", Decimal("1023.1"), "JPY", "1.023,10"),
|
||||
("de", Decimal("1000.00"), "EUR", "1000,00"),
|
||||
("en", Decimal("1000.00"), "EUR", "1000.00"),
|
||||
("de", Decimal("1023.1"), "JPY", "1023,10"),
|
||||
]
|
||||
)
|
||||
def test_money_filter_hidecurrency(locale, amount, currency, expected):
|
||||
|
||||
@@ -2306,25 +2306,6 @@ class CartTest(CartTestMixin, TestCase):
|
||||
assert cp1.voucher is None
|
||||
assert cp2.voucher is None
|
||||
|
||||
def test_voucher_apply_is_a_giftcard(self):
|
||||
with scopes_disabled():
|
||||
CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
price=23, listed_price=23, price_after_voucher=23, expires=now() + timedelta(minutes=10)
|
||||
)
|
||||
CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.shirt, variation=self.shirt_blue,
|
||||
price=8, expires=now() + timedelta(minutes=10),
|
||||
)
|
||||
gc = self.orga.issued_gift_cards.create(secret="GIFTCARD", currency=self.event.currency)
|
||||
gc.transactions.create(value=Decimal("12.24"), acceptor=self.orga)
|
||||
|
||||
html = self.client.post('/%s/%s/cart/voucher' % (self.orga.slug, self.event.slug), {
|
||||
'voucher': 'GIFTCARD',
|
||||
}, follow=True)
|
||||
assert "alert-success" in html.rendered_content
|
||||
assert "€12.24" in html.rendered_content
|
||||
|
||||
def test_discount(self):
|
||||
with scopes_disabled():
|
||||
Discount.objects.create(event=self.event, condition_min_count=2, benefit_discount_matching_percent=20,
|
||||
|
||||
@@ -55,7 +55,6 @@ from pretix.base.models import (
|
||||
)
|
||||
from pretix.base.models.items import SubEventItem, SubEventItemVariation
|
||||
from pretix.base.reldate import RelativeDate, RelativeDateWrapper
|
||||
from pretix.testutils.sessions import get_cart_session_key
|
||||
|
||||
|
||||
class EventTestMixin:
|
||||
@@ -1004,25 +1003,6 @@ class VoucherRedeemItemDisplayTest(EventTestMixin, SoupTest):
|
||||
assert 'name="variation_%d_%d' % (self.item.pk, var1.pk) not in html.rendered_content
|
||||
assert 'name="variation_%d_%d' % (self.item.pk, var2.pk) not in html.rendered_content
|
||||
|
||||
def test_voucher_is_a_gift_card(self):
|
||||
gc = self.orga.issued_gift_cards.create(secret="GIFTCARD", currency=self.event.currency)
|
||||
gc.transactions.create(value=Decimal("12.00"), acceptor=self.orga)
|
||||
|
||||
html = self.client.get('/%s/%s/redeem?voucher=%s' % (self.orga.slug, self.event.slug, 'GIFTCARD'), follow=True)
|
||||
assert "alert-success" in html.rendered_content
|
||||
assert "€12.00" in html.rendered_content
|
||||
|
||||
payments = self.client.session['carts'][get_cart_session_key(self.client, self.event)]["payments"]
|
||||
assert payments[0]["info_data"]["gift_card_secret"] == "GIFTCARD"
|
||||
|
||||
def test_voucher_is_a_gift_card_but_invalid(self):
|
||||
gc = self.orga.issued_gift_cards.create(secret="GIFTCARD", currency=self.event.currency, expires=now() - datetime.timedelta(days=1))
|
||||
gc.transactions.create(value=Decimal("12.00"), acceptor=self.orga)
|
||||
|
||||
html = self.client.get('/%s/%s/redeem?voucher=%s' % (self.orga.slug, self.event.slug, 'GIFTCARD'), follow=True)
|
||||
assert "alert-danger" in html.rendered_content
|
||||
assert "This gift card is no longer valid" in html.rendered_content
|
||||
|
||||
|
||||
class WaitingListTest(EventTestMixin, SoupTest):
|
||||
@scopes_disabled()
|
||||
|
||||
Reference in New Issue
Block a user