mirror of
https://github.com/pretix/pretix.git
synced 2026-05-08 15:44:02 +00:00
Allow to enter gift cards into the voucher input (Z#23171961) (#4670)
This commit is contained in:
@@ -1419,50 +1419,51 @@ class GiftCardPayment(BasePaymentProvider):
|
||||
def payment_refund_supported(self, payment: OrderPayment) -> bool:
|
||||
return True
|
||||
|
||||
def checkout_prepare(self, request: HttpRequest, cart: Dict[str, Any]) -> Union[bool, str, None]:
|
||||
from pretix.base.services.cart import add_payment_to_cart
|
||||
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]:
|
||||
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()
|
||||
)
|
||||
if gc.currency != self.event.currency:
|
||||
messages.error(request, _("This gift card does not support this currency."))
|
||||
cs = cart_session(request)
|
||||
try:
|
||||
self._add_giftcard_to_cart(cs, gc)
|
||||
return True
|
||||
except ValidationError as e:
|
||||
messages.error(request, str(e.message))
|
||||
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,6 +1426,28 @@ 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.
|
||||
@@ -1440,16 +1462,7 @@ 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)
|
||||
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 {},
|
||||
})
|
||||
add_payment_to_cart_session(cs, provider, min_value, max_value, info_data)
|
||||
|
||||
|
||||
def get_fees(event, request, total, invoice_address, payments, positions):
|
||||
|
||||
@@ -10,18 +10,41 @@
|
||||
<i class="fa fa-shopping-cart" aria-hidden="true"></i>
|
||||
<strong>{% trans "Your cart" %}</strong>
|
||||
</span>
|
||||
<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>
|
||||
{% 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 %}
|
||||
</h2>
|
||||
</summary>
|
||||
<div>
|
||||
<div class="panel-body">
|
||||
{% include "pretixpresale/event/fragment_cart.html" with cart=cart event=request.event editable=True %}
|
||||
{% 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 %}
|
||||
<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 %}>
|
||||
|
||||
@@ -251,7 +251,8 @@ 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)
|
||||
'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')]
|
||||
}
|
||||
|
||||
def current_selected_payments(self, total, warn=False, total_includes_payment_fees=False):
|
||||
|
||||
@@ -42,6 +42,7 @@ 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
|
||||
@@ -57,7 +58,7 @@ from django.views.generic import TemplateView, View
|
||||
from django_scopes import scopes_disabled
|
||||
|
||||
from pretix.base.models import (
|
||||
CartPosition, InvoiceAddress, QuestionAnswer, SubEvent, Voucher,
|
||||
CartPosition, GiftCard, InvoiceAddress, QuestionAnswer, SubEvent, Voucher,
|
||||
)
|
||||
from pretix.base.services.cart import (
|
||||
CartError, add_items_to_cart, apply_voucher, clear_cart, error_messages,
|
||||
@@ -438,8 +439,48 @@ 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:
|
||||
return self.do(self.request.event.id, request.POST.get('voucher'), get_or_create_cart_id(self.request),
|
||||
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),
|
||||
translation.get_language(), request.sales_channel.identifier,
|
||||
time_machine_now(default=None))
|
||||
else:
|
||||
@@ -631,6 +672,8 @@ 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')
|
||||
|
||||
@@ -653,10 +696,24 @@ 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:
|
||||
if self.request.event.organizer.accepted_gift_cards.filter(secret__iexact=request.GET.get("voucher")).exists():
|
||||
err = error_messages['gift_card']
|
||||
else:
|
||||
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:
|
||||
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'] and (
|
||||
(context['cart']['positions'] or context['cart'].get('current_selected_payments')) and (
|
||||
self.request.event.has_subevents or self.request.event.presale_is_running
|
||||
)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user