Connect giftcards with customer accounts (#5126)

Connect giftcards with customer accounts, show giftcards during checkout and in account , show giftcard list in backend customer view
This commit is contained in:
Phin Wolkwitz
2025-10-16 13:20:00 +02:00
committed by GitHub
parent 71f2c8093f
commit 8a3da37b45
22 changed files with 490 additions and 6 deletions

View File

@@ -9,6 +9,34 @@
{% endblock %}
{% block inner %}
<h3 class="sr-only">{% trans "Payment" %}</h3>
{% if customer_gift_cards %}
<p><strong>
<span class="sr-only">{% trans "Information" %}</span>
{% trans "The following gift cards are available in your customer account:" %}
</strong></p>
<form method="post">
{% csrf_token %}
<ul class="list-group">
{% for c in customer_gift_cards %}
<li class="list-group-item row">
<div class="col-xs-9" id="gc-code-{{ forloop.counter }}">
{{ c }}
</div>
<div class="col-xs-2 text-right" id="gc-value-{{ forloop.counter }}">
{{ c.value|money:c.currency }}
</div>
<div class="col-xs-1 text-right">
<button name="use_giftcard" value="{{ c.secret }}" title="{% trans "Use gift card" %}"
aria-describedby="gc-code-{{ forloop.counter }} gc-value-{{ forloop.counter }}"
class="btn btn-primary btn-xs">
{% trans "Apply" %}
</button>
</div>
</li>
{% endfor %}
</ul>
</form>
{% endif %}
{% if current_payments %}
<p>{% trans "You already selected the following payment methods:" %}</p>
<form method="post">
@@ -77,6 +105,15 @@
aria-controls="payment_{{ p.provider.identifier }}"
data-wallets="{{ p.provider.walletqueries|join:"|" }}" />
<strong class="accordion-label-text">{{ p.provider.public_name }}</strong>
{% if p.provider.identifier == 'giftcard' and p.provider.customer_gift_cards %}
<small>
{% blocktrans trimmed count count=p.provider.customer_gift_cards|length %}
({{ count }} available)
{% plural %}
({{ count }} available)
{% endblocktrans %}
</small>
{% endif %}
</span>
</label>
</legend>

View File

@@ -40,6 +40,15 @@
{% if selected == p.provider.identifier %}checked="checked"{% endif %}
data-wallets="{{ p.provider.walletqueries|join:"|" }}"/>
<strong class="accordion-label-text">{{ p.provider.public_name }}</strong>
{% if p.provider.identifier == 'giftcard' and p.provider.customer_gift_cards %}
<small>
{% blocktrans trimmed count count=p.provider.customer_gift_cards|length %}
({{ count }} available)
{% plural %}
({{ count }} available)
{% endblocktrans %}
</small>
{% endif %}
</span>
</label>
</legend>

View File

@@ -0,0 +1,83 @@
{% extends "pretixpresale/organizers/customer_base.html" %}
{% load money %}
{% load i18n %}
{% load icon %}
{% load eventurl %}
{% load textbubble %}
{% block title %}{% trans "Gift cards" %}{% endblock %}
{% block inner %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
{% icon "gift" %}
<strong>{% trans "Gift cards" %}</strong> ({{ page_obj.paginator.count }})
</h3>
</div>
<div class="panel-body">
{% if gift_cards %}
<div class="event-list full-width-list alternating-rows">
{% for gc in gift_cards %}
<article class="row">
<div class="col-xs-6">
<h4>
{% icon "gift" %} {{ gc }}
</h4>
{% if gc.issuance %}
<p class="text-muted">
{% icon "calendar" %}
{% blocktrans trimmed with date=gc.issuance|date:"SHORT_DATE_FORMAT" %}
Issued on {{ date }}
{% endblocktrans %}
</p>
{% endif %}
<p class="text-muted">
<small>
{% if gc.expired %}
{% icon "clock-o" %}
{% if gc.expires %}
{% blocktrans trimmed with date=gc.expires|date:"SHORT_DATETIME_FORMAT" %}
Expired since {{ date }}
{% endblocktrans %}
{% else %}
{% trans "Expired" %}
{% endif %}
{% elif gc.expires %}
{% icon "check" %}
{% blocktrans trimmed with date=gc.expires|date:"SHORT_DATETIME_FORMAT" %}
Valid until {{ date }}
{% endblocktrans %}
{% else %}
{% icon "check" %}
{% trans "Valid" %}
{% endif %}
</small>
</p>
{% if gc.testmode %}
<p>
<small>
{% textbubble "warning" %}
{% trans "TEST MODE" %}
{% endtextbubble %}
</small>
</p>
{% endif %}
</div>
<div class="col-xs-6 text-right">
{% blocktrans trimmed with value=gc.value|money:gc.currency %}
Remaining value:
{% endblocktrans %}
<p class="text-right">{{ value }}</p>
</div>
</article>
{% endfor %}
</div>
{% else %}
<p class="text-center">
{% trans "You dont have any gift cards in your account currently." %}
{% trans "Currently, only gift cards resulting from refunds show up here, any purchased gift cards show up under the orders tab." %}
</p>
{% endif %}
</div>
</div>
{% include "pretixcontrol/pagination.html" %}
{% endblock %}

View File

@@ -214,6 +214,8 @@ organizer_patterns = [
re_path(r'^account/confirmchange$', pretix.presale.views.customer.ConfirmChangeView.as_view(), name='organizer.customer.change.confirm'),
re_path(r'^account/memberships$', pretix.presale.views.customer.MembershipView.as_view(), name='organizer.customer.memberships'),
re_path(r'^account/memberships/(?P<id>\d+)/$', pretix.presale.views.customer.MembershipUsageView.as_view(), name='organizer.customer.membership'),
re_path(r'^account/giftcards$', pretix.presale.views.customer.GiftcardView.as_view(),
name='organizer.customer.giftcards'),
re_path(r'^account/addresses$', pretix.presale.views.customer.AddressView.as_view(), name='organizer.customer.addresses'),
re_path(r'^account/addresses/(?P<id>\d+)/delete$', pretix.presale.views.customer.AddressDeleteView.as_view(), name='organizer.customer.address.delete'),
re_path(r'^account/profiles$', pretix.presale.views.customer.ProfileView.as_view(), name='organizer.customer.profiles'),

View File

@@ -368,6 +368,12 @@ class CustomerAccountBaseMixin(CustomerRequiredMixin):
'active': url_name.startswith('organizer.customer.membership'),
'icon': 'id-badge',
},
{
'label': _('Gift cards'),
'url': eventreverse(self.request.organizer, 'presale:organizer.customer.giftcards', kwargs={}),
'active': url_name.startswith('organizer.customer.giftcard'),
'icon': 'gift',
},
{
'label': _('Addresses'),
'url': eventreverse(self.request.organizer, 'presale:organizer.customer.addresses', kwargs={}),
@@ -461,6 +467,15 @@ class MembershipUsageView(CustomerAccountBaseMixin, ListView):
return ctx
class GiftcardView(CustomerAccountBaseMixin, ListView):
template_name = 'pretixpresale/organizers/customer_giftcards.html'
context_object_name = 'gift_cards'
paginate_by = 20
def get_queryset(self):
return self.request.customer.customer_gift_cards.all()
class AddressView(CustomerAccountBaseMixin, ListView):
template_name = 'pretixpresale/organizers/customer_addresses.html'
context_object_name = 'invoice_addresses'