Add expiry dates and individual conditions to gift cards (#1656)

* Add expiry dates and individual conditions to gift cards

* Display refund gift cards with more details and prettier interface

* Allow to set gift card expiry and conditions when cancelling event

* Extend gift card search

* Fix #1565 -- Some gift card filters

* Improve list of gift cards

* Allow to edit gift cards

* Note on validity
This commit is contained in:
Raphael Michel
2020-04-21 15:57:02 +02:00
committed by GitHub
parent d9fd4b33a0
commit f2844ac686
31 changed files with 450 additions and 70 deletions

View File

@@ -10,7 +10,7 @@ from django.urls import reverse
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.timezone import get_current_timezone_name
from django.utils.translation import gettext_lazy as _, pgettext_lazy
from django.utils.translation import gettext, gettext_lazy as _, pgettext_lazy
from django_countries import Countries
from django_countries.fields import LazyTypedChoiceField
from i18nfield.forms import (
@@ -577,6 +577,13 @@ class CancelSettingsForm(SettingsForm):
'cancel_allow_user_paid_require_approval',
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.obj.settings.giftcard_expiry_years is not None:
self.fields['cancel_allow_user_paid_refund_as_giftcard'].help_text = gettext(
'You have configured gift cards to be valid {} years plus the year the gift card is issued in.'
).format(self.obj.settings.giftcard_expiry_years)
class PaymentSettingsForm(SettingsForm):
auto_fields = [

View File

@@ -78,7 +78,7 @@ class FilterForm(forms.Form):
def get_order_by(self):
o = self.cleaned_data.get('ordering')
if o.startswith('-'):
if o.startswith('-') and o not in self.orders:
return '-' + self.orders[o[1:]]
else:
return self.orders[o]
@@ -530,6 +530,33 @@ class OrganizerFilterForm(FilterForm):
class GiftCardFilterForm(FilterForm):
orders = {
'issuance': 'issuance',
'expires': F('expires').asc(nulls_last=True),
'-expires': F('expires').desc(nulls_first=True),
'secret': 'secret',
'value': 'cached_value',
}
testmode = forms.ChoiceField(
label=_('Test mode'),
choices=(
('', _('All')),
('yes', _('Test mode')),
('no', _('Live')),
),
required=False
)
state = forms.ChoiceField(
label=_('Empty'),
choices=(
('', _('All')),
('empty', _('Empty')),
('valid_value', _('Valid and with value')),
('expired_value', _('Expired and with value')),
('expired', _('Expired')),
),
required=False
)
query = forms.CharField(
label=_('Search query'),
widget=forms.TextInput(attrs={
@@ -548,8 +575,30 @@ class GiftCardFilterForm(FilterForm):
if fdata.get('query'):
query = fdata.get('query')
qs = qs.filter(secret__icontains=query)
return qs
qs = qs.filter(
Q(secret__icontains=query)
| Q(transactions__text__icontains=query)
| Q(transactions__order__code__icontains=query)
)
if fdata.get('testmode') == 'yes':
qs = qs.filter(testmode=True)
elif fdata.get('testmode') == 'no':
qs = qs.filter(testmode=False)
if fdata.get('state') == 'empty':
qs = qs.filter(cached_value=0)
elif fdata.get('state') == 'valid_value':
qs = qs.exclude(cached_value=0).filter(Q(expires__isnull=True) | Q(expires__gte=now()))
elif fdata.get('state') == 'expired_value':
qs = qs.exclude(cached_value=0).filter(expires__lt=now())
elif fdata.get('state') == 'expired':
qs = qs.filter(expires__lt=now())
if fdata.get('ordering'):
qs = qs.order_by(self.get_order_by())
else:
qs = qs.order_by('-issuance')
return qs.distinct()
class EventFilterForm(FilterForm):

View File

@@ -15,12 +15,15 @@ from i18nfield.strings import LazyI18nString
from pretix.base.email import get_available_placeholders
from pretix.base.forms import I18nModelForm, PlaceholderValidator
from pretix.base.forms.widgets import DatePickerWidget
from pretix.base.forms.widgets import (
DatePickerWidget, SplitDateTimePickerWidget,
)
from pretix.base.models import (
InvoiceAddress, ItemAddOn, Order, OrderFee, OrderPosition,
)
from pretix.base.models.event import SubEvent
from pretix.base.services.pricing import get_price
from pretix.control.forms import SplitDateTimeField
from pretix.control.forms.widgets import Select2
from pretix.helpers.money import change_decimal_field
@@ -565,6 +568,20 @@ class EventCancelForm(forms.Form):
initial=False,
required=False,
)
gift_card_expires = SplitDateTimeField(
label=_('Gift card validity'),
required=False,
widget=SplitDateTimePickerWidget(
attrs={'data-display-dependency': '#id_refund_as_giftcard'},
)
)
gift_card_conditions = forms.CharField(
label=_('Special terms and conditions'),
required=False,
widget=forms.Textarea(
attrs={'rows': 2, 'data-display-dependency': '#id_refund_as_giftcard'},
)
)
keep_fee_fixed = forms.DecimalField(
label=_("Keep a fixed cancellation fee"),
max_digits=10, decimal_places=2,
@@ -615,6 +632,8 @@ class EventCancelForm(forms.Form):
def __init__(self, *args, **kwargs):
self.event = kwargs.pop('event')
kwargs.setdefault('initial', {})
kwargs['initial']['gift_card_expires'] = self.event.organizer.default_gift_card_expiry
super().__init__(*args, **kwargs)
self.fields['send_subject'] = I18nFormField(
label=_("Subject"),

View File

@@ -14,9 +14,10 @@ from i18nfield.forms import I18nFormField, I18nTextarea
from pretix.api.models import WebHook
from pretix.api.webhooks import get_all_webhook_events
from pretix.base.forms import I18nModelForm, SettingsForm
from pretix.base.forms.widgets import SplitDateTimePickerWidget
from pretix.base.models import Device, GiftCard, Organizer, Team
from pretix.control.forms import (
ExtFileField, FontSelect, MultipleLanguagesWidget,
ExtFileField, FontSelect, MultipleLanguagesWidget, SplitDateTimeField,
)
from pretix.control.forms.event import SafeEventMultipleChoiceField
from pretix.multidomain.models import KnownDomain
@@ -330,6 +331,12 @@ class OrganizerSettingsForm(SettingsForm):
'is required, it can be set here.'.format(settings.ENTROPY['giftcard_secret'])),
required=False
)
giftcard_expiry_years = forms.IntegerField(
label=_('Validity of gift card codes in years'),
help_text=_('If you set a number here, gift cards will by default expire at the end of the year after this '
'many years. If you keep it empty, gift cards do not have an explicit expiry date.'),
required=False
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -378,12 +385,15 @@ class GiftCardCreateForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.organizer = kwargs.pop('organizer')
initial = kwargs.pop('initial', {})
initial['expires'] = self.organizer.default_gift_card_expiry
kwargs['initial'] = initial
super().__init__(*args, **kwargs)
def clean_secret(self):
s = self.cleaned_data['secret']
if GiftCard.objects.filter(
secret__iexact=s
secret__iexact=s
).filter(
Q(issuer=self.organizer) | Q(issuer__gift_card_collector_acceptance__collector=self.organizer)
).exists():
@@ -394,4 +404,24 @@ class GiftCardCreateForm(forms.ModelForm):
class Meta:
model = GiftCard
fields = ['secret', 'currency', 'testmode']
fields = ['secret', 'currency', 'testmode', 'expires', 'conditions']
field_classes = {
'expires': SplitDateTimeField
}
widgets = {
'expires': SplitDateTimePickerWidget,
'conditions': forms.Textarea(attrs={"rows": 2})
}
class GiftCardUpdateForm(forms.ModelForm):
class Meta:
model = GiftCard
fields = ['expires', 'conditions']
field_classes = {
'expires': SplitDateTimeField
}
widgets = {
'expires': SplitDateTimePickerWidget,
'conditions': forms.Textarea(attrs={"rows": 2})
}