forked from CGM_Public/pretix_original
Compare commits
2 Commits
reldatetim
...
voucher-wi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
673df7df80 | ||
|
|
9e9fc36b50 |
@@ -8,6 +8,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
|
|
||||||
from pretix.base.forms import I18nModelForm
|
from pretix.base.forms import I18nModelForm
|
||||||
from pretix.base.models import Item, ItemVariation, Quota, Voucher
|
from pretix.base.models import Item, ItemVariation, Quota, Voucher
|
||||||
|
from pretix.base.models.vouchers import _generate_random_code
|
||||||
|
|
||||||
|
|
||||||
class VoucherForm(I18nModelForm):
|
class VoucherForm(I18nModelForm):
|
||||||
@@ -90,9 +91,8 @@ class VoucherForm(I18nModelForm):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if 'codes' in data:
|
if 'number' in data:
|
||||||
data['codes'] = [a.strip() for a in data.get('codes', '').strip().split("\n") if a]
|
cnt = data['number'] * data['max_usages']
|
||||||
cnt = len(data['codes']) * data['max_usages']
|
|
||||||
else:
|
else:
|
||||||
cnt = data['max_usages']
|
cnt = data['max_usages']
|
||||||
|
|
||||||
@@ -174,14 +174,30 @@ class VoucherForm(I18nModelForm):
|
|||||||
|
|
||||||
|
|
||||||
class VoucherBulkForm(VoucherForm):
|
class VoucherBulkForm(VoucherForm):
|
||||||
codes = forms.CharField(
|
number = forms.IntegerField(
|
||||||
widget=forms.Textarea,
|
label=_("Number"),
|
||||||
label=_("Codes"),
|
|
||||||
help_text=_(
|
|
||||||
"Add one voucher code per line. We suggest that you copy this list and save it into a file."
|
|
||||||
),
|
|
||||||
required=True
|
required=True
|
||||||
)
|
)
|
||||||
|
itemvar = forms.ChoiceField(
|
||||||
|
label=_("Product"),
|
||||||
|
widget=forms.RadioSelect
|
||||||
|
)
|
||||||
|
price_mode = forms.ChoiceField(
|
||||||
|
choices=Voucher.PRICE_MODES,
|
||||||
|
)
|
||||||
|
has_valid_until = forms.BooleanField()
|
||||||
|
value_percent = forms.DecimalField(
|
||||||
|
required=False,
|
||||||
|
max_digits=10, decimal_places=2
|
||||||
|
)
|
||||||
|
value_subtract = forms.DecimalField(
|
||||||
|
required=False,
|
||||||
|
max_digits=10, decimal_places=2
|
||||||
|
)
|
||||||
|
value_set = forms.DecimalField(
|
||||||
|
required=False,
|
||||||
|
max_digits=10, decimal_places=2
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Voucher
|
model = Voucher
|
||||||
@@ -203,21 +219,35 @@ class VoucherBulkForm(VoucherForm):
|
|||||||
def clean(self):
|
def clean(self):
|
||||||
data = super().clean()
|
data = super().clean()
|
||||||
|
|
||||||
if Voucher.objects.filter(code__in=data['codes'], event=self.instance.event).exists():
|
if data.get('has_valid_until', False) and not data.get('valid_until'):
|
||||||
raise ValidationError(_('A voucher with one of this codes already exists.'))
|
raise ValidationError(_('You did not specify an expiration date for the vouchers.'))
|
||||||
|
|
||||||
|
if data.get('price_mode', 'none') != 'none':
|
||||||
|
if data.get('value_%s' % data['price_mode']) is None:
|
||||||
|
raise ValidationError(_('You specified that the vouchers should modify the products price '
|
||||||
|
'but did not specify a value.'))
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def save(self, event, *args, **kwargs):
|
def save(self, event, *args, **kwargs):
|
||||||
objs = []
|
objs = []
|
||||||
for code in self.cleaned_data['codes']:
|
|
||||||
|
codes = set()
|
||||||
|
while len(codes) < self.cleaned_data['number']:
|
||||||
|
new_codes = set()
|
||||||
|
for i in range(min(self.cleaned_data['number'] - len(codes), 500)):
|
||||||
|
# Work around SQLite's SQLITE_MAX_VARIABLE_NUMBER
|
||||||
|
new_codes.add(_generate_random_code())
|
||||||
|
new_codes -= set([v['code'] for v in Voucher.objects.filter(code__in=new_codes).values('code')])
|
||||||
|
codes |= new_codes
|
||||||
|
|
||||||
|
for code in codes:
|
||||||
obj = copy.copy(self.instance)
|
obj = copy.copy(self.instance)
|
||||||
obj.event = event
|
obj.event = event
|
||||||
obj.code = code
|
obj.code = code
|
||||||
data = dict(self.cleaned_data)
|
data = dict(self.cleaned_data)
|
||||||
data['code'] = code
|
data['code'] = code
|
||||||
data['bulk'] = True
|
data['bulk'] = True
|
||||||
del data['codes']
|
|
||||||
obj.save()
|
obj.save()
|
||||||
objs.append(obj)
|
objs.append(obj)
|
||||||
return objs
|
return objs
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
<script type="text/javascript" src="{% static "pretixcontrol/js/sb-admin-2.js" %}"></script>
|
<script type="text/javascript" src="{% static "pretixcontrol/js/sb-admin-2.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/main.js" %}"></script>
|
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/main.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/quota.js" %}"></script>
|
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/quota.js" %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/voucher.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/question.js" %}"></script>
|
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/question.js" %}"></script>
|
||||||
{% endcompress %}
|
{% endcompress %}
|
||||||
{{ html_head|safe }}
|
{{ html_head|safe }}
|
||||||
|
|||||||
@@ -2,67 +2,139 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load eventsignal %}
|
{% load eventsignal %}
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
|
{% load capture_tags %}
|
||||||
{% block title %}{% trans "Voucher" %}{% endblock %}
|
{% block title %}{% trans "Voucher" %}{% endblock %}
|
||||||
{% block inside %}
|
{% block inside %}
|
||||||
<h1>{% trans "Create multiple voucher" %}</h1>
|
<h1>{% trans "Create new vouchers" %}</h1>
|
||||||
<form action="" method="post" class="form-horizontal">
|
<form action="" method="post" class="form-inline" id="voucher-create">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
{% capture as number_field silent %} {% bootstrap_field form.number layout="inline" %} {% endcapture %}
|
||||||
|
{% capture as max_usages_field silent %} {% bootstrap_field form.max_usages layout="inline" %} {% endcapture %}
|
||||||
|
{% capture as valid_until_field silent %} {% bootstrap_field form.valid_until layout="inline" %} {% endcapture %}
|
||||||
|
{% capture as value_field_percent silent %} {% bootstrap_field form.value_percent layout="inline" %} {% endcapture %}
|
||||||
|
{% capture as value_field_subtract silent %} {% bootstrap_field form.value_subtract layout="inline" %} {% endcapture %}
|
||||||
|
{% capture as value_field_set silent %} {% bootstrap_field form.value_set layout="inline" %} {% endcapture %}
|
||||||
|
|
||||||
{% bootstrap_form_errors form %}
|
{% bootstrap_form_errors form %}
|
||||||
<fieldset>
|
|
||||||
<legend>{% trans "Voucher codes" %}</legend>
|
<div class="wizard-step" id="step-number">
|
||||||
<div class="form-group">
|
<div class="wizard-step-inner">
|
||||||
<div class="col-md-6 col-md-offset-3">
|
<h4>{% trans "How many vouchers do you want to create?" %}</h4>
|
||||||
<div class="input-group">
|
<div class="form-line">
|
||||||
<input type="text" class="form-control input-xs"
|
{% blocktrans trimmed %}
|
||||||
id="voucher-bulk-codes-num"
|
Create {{ number_field }} voucher codes. Each of them can be redeemed {{ max_usages_field }}
|
||||||
placeholder="{% trans "Number" %}">
|
times.
|
||||||
<div class="input-group-btn">
|
{% endblocktrans %}
|
||||||
<button class="btn btn-default" type="button" id="voucher-bulk-codes-generate"
|
</div>
|
||||||
data-rng-url="{% url 'control:event.vouchers.rng' organizer=request.event.organizer.slug event=request.event.slug %}">
|
</div>
|
||||||
{% trans "Generate random codes" %}
|
</div>
|
||||||
</button>
|
|
||||||
</div>
|
<div class="wizard-step" id="step-valid">
|
||||||
|
<div class="wizard-step-inner">
|
||||||
|
<h4>{% trans "How long should the vouchers be valid?" %}</h4>
|
||||||
|
<div class="radio radio-alt">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="has_valid_until" value="" {% if request.POST and not "has_valid_until" in request.POST %}checked="checked"{% endif %}>
|
||||||
|
{% trans "The whole presale period" %}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio radio-alt">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="has_valid_until" value="on" {% if "on" == request.POST.has_valid_until %}checked="checked"{% endif %}>
|
||||||
|
{% blocktrans trimmed %}
|
||||||
|
Only valid until {{ valid_until_field }}
|
||||||
|
{% endblocktrans %}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wizard-step" id="step-products">
|
||||||
|
<div class="wizard-step-inner">
|
||||||
|
<h4>{% trans "For which products should the vouchers be applicable?" %}</h4>
|
||||||
|
{% bootstrap_field form.itemvar layout="inline" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wizard-step" id="step-price">
|
||||||
|
<div class="wizard-step-inner">
|
||||||
|
<h4>{% trans "Should the vouchers modify the product's price?" %}</h4>
|
||||||
|
<div class="radio radio-alt">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="price_mode" value="none" {% if "none" == request.POST.price_mode %}checked="checked"{% endif %}>
|
||||||
|
{% trans "No, just allow to buy this product" %}
|
||||||
|
<span class="help-block">
|
||||||
|
{% trans "This is useful if you have products that can only be bought using vouchers. It also allows you to just block quota for someone (see next step)." %}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio radio-alt">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="price_mode" value="percent" {% if "percent" == request.POST.price_mode %}checked="checked"{% endif %}>
|
||||||
|
{% blocktrans trimmed %}
|
||||||
|
Reduce price by {{ value_field_percent }} %
|
||||||
|
{% endblocktrans %}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio radio-alt">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="price_mode" value="subtract" {% if "subtract" == request.POST.price_mode %}checked="checked"{% endif %}>
|
||||||
|
{% blocktrans trimmed with currency=request.event.currency %}
|
||||||
|
Reduce price by {{ value_field_subtract }} {{ currency }}
|
||||||
|
{% endblocktrans %}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio radio-alt">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="price_mode" value="set" {% if "set" == request.POST.price_mode %}checked="checked"{% endif %}>
|
||||||
|
{% blocktrans trimmed with currency=request.event.currency %}
|
||||||
|
Change price to {{ value_field_set }} {{ currency }}
|
||||||
|
{% endblocktrans %}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wizard-step" id="step-block">
|
||||||
|
<div class="wizard-step-inner">
|
||||||
|
<h4>{% trans "Should the vouchers block quota?" %}</h4>
|
||||||
|
<div class="radio radio-alt">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="block_quota" value="" {% if request.POST and not "block_quota" in request.POST %}checked="checked"{% endif %}>
|
||||||
|
{% trans "No" %}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio radio-alt">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="block_quota" value="on" {% if "on" == request.POST.block_quota %}checked="checked"{% endif %}>
|
||||||
|
{% trans "Yes" %}
|
||||||
|
<span class="help-block">
|
||||||
|
{% trans "If you select this option, these vouchers will be guaranteed, i.e. the applicable quotas will be reduced in a way that these vouchers can be redeemed as long as they are valid, even if your event sells out otherwise." %}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wizard-step" id="step-advanced">
|
||||||
|
<div class="wizard-step-inner">
|
||||||
|
<a href="#" id="wizard-advanced-show">Show advanced options</a>
|
||||||
|
<div class="wizard-advanced">
|
||||||
|
<h4>{% trans "Advanced options" %}</h4>
|
||||||
|
{% bootstrap_field form.allow_ignore_quota %}
|
||||||
|
<p><strong>{% trans "Comment" %}</strong></p>
|
||||||
|
{% bootstrap_field form.comment layout="inline" form_group_class="comment" %}
|
||||||
|
<div class="help-block">
|
||||||
|
{% trans "The text entered in this field will not be visible to the user and is available for your convenience." %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% bootstrap_field form.codes layout="horizontal" %}
|
</div>
|
||||||
{% bootstrap_field form.max_usages layout="horizontal" %}
|
|
||||||
</fieldset>
|
|
||||||
<fieldset>
|
|
||||||
<legend>{% trans "Voucher details" %}</legend>
|
|
||||||
{% bootstrap_field form.valid_until layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.block_quota layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.allow_ignore_quota layout="horizontal" %}
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-md-3 control-label" for="id_tag">{% trans "Price effect" %}</label>
|
|
||||||
<div class="col-md-5">
|
|
||||||
{% bootstrap_field form.price_mode show_label=False form_group_class="" %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
{% bootstrap_field form.value show_label=False form_group_class="" %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% bootstrap_field form.itemvar layout="horizontal" %}
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-md-9 col-md-offset-3">
|
|
||||||
<div class="controls">
|
|
||||||
<div class="alert alert-info">
|
|
||||||
{% blocktrans trimmed %}
|
|
||||||
If you choose "any product" for a specific quota and choose to reserve quota for this
|
|
||||||
voucher above, the product can still be unavailable to the voucher holder if another quota
|
|
||||||
associated with the product is sold out!
|
|
||||||
{% endblocktrans %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% bootstrap_field form.tag layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.comment layout="horizontal" %}
|
|
||||||
</fieldset>
|
|
||||||
{% eventsignal request.event "pretix.control.signals.voucher_form_html" form=form %}
|
{% eventsignal request.event "pretix.control.signals.voucher_form_html" form=form %}
|
||||||
<div class="form-group submit-group">
|
<div class="submit-group" id="step-save">
|
||||||
<button type="submit" class="btn btn-primary btn-save">
|
<button type="submit" class="btn btn-primary btn-save">
|
||||||
{% trans "Save" %}
|
{% trans "Create" %}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -37,17 +37,12 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<a href="{% url "control:event.vouchers.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
<a href="{% url "control:event.vouchers.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new voucher" %}</a>
|
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create new vouchers" %}</a>
|
||||||
<a href="{% url "control:event.vouchers.bulk" organizer=request.event.organizer.slug event=request.event.slug %}"
|
|
||||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create multiple new vouchers" %}</a>
|
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>
|
<p>
|
||||||
<a href="{% url "control:event.vouchers.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
<a href="{% url "control:event.vouchers.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||||
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new voucher" %}</a>
|
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create new vouchers" %}</a>
|
||||||
<a href="{% url "control:event.vouchers.bulk" organizer=request.event.organizer.slug event=request.event.slug %}"
|
|
||||||
class="btn btn-default"><i class="fa fa-plus"></i>
|
|
||||||
{% trans "Create multiple new vouchers" %}</a>
|
|
||||||
</p>
|
</p>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-hover table-quotas">
|
<table class="table table-hover table-quotas">
|
||||||
|
|||||||
@@ -90,7 +90,6 @@ urlpatterns = [
|
|||||||
url(r'^vouchers/(?P<voucher>\d+)/delete$', vouchers.VoucherDelete.as_view(),
|
url(r'^vouchers/(?P<voucher>\d+)/delete$', vouchers.VoucherDelete.as_view(),
|
||||||
name='event.voucher.delete'),
|
name='event.voucher.delete'),
|
||||||
url(r'^vouchers/add$', vouchers.VoucherCreate.as_view(), name='event.vouchers.add'),
|
url(r'^vouchers/add$', vouchers.VoucherCreate.as_view(), name='event.vouchers.add'),
|
||||||
url(r'^vouchers/bulk_add$', vouchers.VoucherBulkCreate.as_view(), name='event.vouchers.bulk'),
|
|
||||||
url(r'^orders/(?P<code>[0-9A-Z]+)/transition$', orders.OrderTransition.as_view(),
|
url(r'^orders/(?P<code>[0-9A-Z]+)/transition$', orders.OrderTransition.as_view(),
|
||||||
name='event.order.transition'),
|
name='event.order.transition'),
|
||||||
url(r'^orders/(?P<code>[0-9A-Z]+)/resend$', orders.OrderResendLink.as_view(),
|
url(r'^orders/(?P<code>[0-9A-Z]+)/resend$', orders.OrderResendLink.as_view(),
|
||||||
|
|||||||
@@ -189,44 +189,6 @@ class VoucherUpdate(EventPermissionRequiredMixin, UpdateView):
|
|||||||
|
|
||||||
|
|
||||||
class VoucherCreate(EventPermissionRequiredMixin, CreateView):
|
class VoucherCreate(EventPermissionRequiredMixin, CreateView):
|
||||||
model = Voucher
|
|
||||||
template_name = 'pretixcontrol/vouchers/detail.html'
|
|
||||||
permission = 'can_change_vouchers'
|
|
||||||
context_object_name = 'voucher'
|
|
||||||
|
|
||||||
def get_form_class(self):
|
|
||||||
form_class = VoucherForm
|
|
||||||
for receiver, response in voucher_form_class.send(self.request.event, cls=form_class):
|
|
||||||
if response:
|
|
||||||
form_class = response
|
|
||||||
return form_class
|
|
||||||
|
|
||||||
def get_success_url(self) -> str:
|
|
||||||
return reverse('control:event.vouchers', kwargs={
|
|
||||||
'organizer': self.request.event.organizer.slug,
|
|
||||||
'event': self.request.event.slug,
|
|
||||||
})
|
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
|
||||||
kwargs = super().get_form_kwargs()
|
|
||||||
kwargs['instance'] = Voucher(event=self.request.event)
|
|
||||||
return kwargs
|
|
||||||
|
|
||||||
@transaction.atomic
|
|
||||||
def form_valid(self, form):
|
|
||||||
form.instance.event = self.request.event
|
|
||||||
messages.success(self.request, _('The new voucher has been created.'))
|
|
||||||
ret = super().form_valid(form)
|
|
||||||
form.instance.log_action('pretix.voucher.added', data=dict(form.cleaned_data), user=self.request.user)
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
# TODO: Transform this into an asynchronous call?
|
|
||||||
with request.event.lock():
|
|
||||||
return super().post(request, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class VoucherBulkCreate(EventPermissionRequiredMixin, CreateView):
|
|
||||||
model = Voucher
|
model = Voucher
|
||||||
template_name = 'pretixcontrol/vouchers/bulk.html'
|
template_name = 'pretixcontrol/vouchers/bulk.html'
|
||||||
permission = 'can_change_vouchers'
|
permission = 'can_change_vouchers'
|
||||||
@@ -241,6 +203,11 @@ class VoucherBulkCreate(EventPermissionRequiredMixin, CreateView):
|
|||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
kwargs['instance'] = Voucher(event=self.request.event)
|
kwargs['instance'] = Voucher(event=self.request.event)
|
||||||
|
initial = {
|
||||||
|
}
|
||||||
|
if 'initial' in kwargs:
|
||||||
|
initial.update(kwargs['initial'])
|
||||||
|
kwargs['initial'] = initial
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
|
|||||||
@@ -193,6 +193,7 @@ INSTALLED_APPS = [
|
|||||||
'django_otp.plugins.otp_totp',
|
'django_otp.plugins.otp_totp',
|
||||||
'django_otp.plugins.otp_static',
|
'django_otp.plugins.otp_static',
|
||||||
'statici18n',
|
'statici18n',
|
||||||
|
'capture_tag',
|
||||||
]
|
]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ python-u2flib-server==4.*
|
|||||||
# https://github.com/celery/celery/pull/3199
|
# https://github.com/celery/celery/pull/3199
|
||||||
git+https://github.com/pretix/celery.git@pretix#egg=celery
|
git+https://github.com/pretix/celery.git@pretix#egg=celery
|
||||||
django-statici18n==1.2.*
|
django-statici18n==1.2.*
|
||||||
|
django-capture-tag==1.0
|
||||||
|
|
||||||
# Deployment / static file compilation requirements
|
# Deployment / static file compilation requirements
|
||||||
BeautifulSoup4
|
BeautifulSoup4
|
||||||
|
|||||||
@@ -62,7 +62,8 @@ setup(
|
|||||||
'easy-thumbnails>=2.2,<3'
|
'easy-thumbnails>=2.2,<3'
|
||||||
'PyPDF2', 'BeautifulSoup4', 'html5lib',
|
'PyPDF2', 'BeautifulSoup4', 'html5lib',
|
||||||
'slimit', 'lxml', 'static3==0.6.1', 'dj-static', 'chardet',
|
'slimit', 'lxml', 'static3==0.6.1', 'dj-static', 'chardet',
|
||||||
'csscompressor', 'mt-940', 'django-markup', 'markdown'
|
'csscompressor', 'mt-940', 'django-markup', 'markdown',
|
||||||
|
'django-capture-tag'
|
||||||
],
|
],
|
||||||
extras_require={
|
extras_require={
|
||||||
'dev': ['django-debug-toolbar>=1.3.0,<2.0'],
|
'dev': ['django-debug-toolbar>=1.3.0,<2.0'],
|
||||||
|
|||||||
82
src/static/pretixcontrol/js/ui/voucher.js
Normal file
82
src/static/pretixcontrol/js/ui/voucher.js
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/*globals $, Morris, gettext*/
|
||||||
|
$(function () {
|
||||||
|
if (!$("#voucher-create").length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function show_step(state_el) {
|
||||||
|
var was_visible = state_el.is(':visible');
|
||||||
|
state_el.animate({
|
||||||
|
'height': 'show',
|
||||||
|
'opacity': 'show',
|
||||||
|
'padding-top': 'show',
|
||||||
|
'padding-bottom': 'show',
|
||||||
|
'margin-top': 'show',
|
||||||
|
'margin-bottom': 'show'
|
||||||
|
}, 400);
|
||||||
|
var offset = state_el.offset();
|
||||||
|
var body = $("html, body");
|
||||||
|
if (!was_visible && offset.top > $("body").scrollTop() + $(window).height() - 160) {
|
||||||
|
body.animate({scrollTop: offset.top + 200}, '400', 'swing');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($(".alert-danger").length === 0) {
|
||||||
|
$(".wizard-step, .wizard-advanced, #step-save").hide();
|
||||||
|
$(".wizard-step").first().show();
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#id_number, #id_max_usages").on("change keydown keyup", function () {
|
||||||
|
if ($("#id_number").val() && $("#id_max_usages").val()) {
|
||||||
|
show_step($("#step-valid"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#id_valid_until").on("focus change", function () {
|
||||||
|
$("input[name=has_valid_until][value=no]").prop("checked", false);
|
||||||
|
$("input[name=has_valid_until][value=yes]").prop("checked", true);
|
||||||
|
}).on("change dp.change", function () {
|
||||||
|
if ($("input[name=has_valid_until][value=no]").prop("checked") || $("#id_valid_until").val()) {
|
||||||
|
show_step($("#step-products"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$("input[name=has_valid_until]").on("change", function () {
|
||||||
|
if ($("input[name=has_valid_until]").not("[value=on]").prop("checked") || $("#id_valid_until").val()) {
|
||||||
|
show_step($("#step-products"));
|
||||||
|
} else {
|
||||||
|
$("#id_valid_until").focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$("input[name=itemvar]").on("change", function () {
|
||||||
|
show_step($("#step-price"));
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#step-price input").on("change keydown keyup", function () {
|
||||||
|
var mode = $("input[name=price_mode]:checked").val();
|
||||||
|
var show_next = (mode === 'none' || $("input[name='value_" + mode + "']").val());
|
||||||
|
if (show_next) {
|
||||||
|
show_step($("#step-block"));
|
||||||
|
} else {
|
||||||
|
$("input[name='value_" + mode + "']").focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$("#step-price input[type=text]").on("focus change keyup keydown", function () {
|
||||||
|
$("#step-price input[type=radio]").prop("checked", false);
|
||||||
|
$(this).closest(".radio").find("input[type=radio]").prop("checked", true);
|
||||||
|
});
|
||||||
|
|
||||||
|
$("input[name=block_quota]").on("change", function () {
|
||||||
|
show_step($("#step-advanced"));
|
||||||
|
show_step($("#step-save"));
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#wizard-advanced-show").on("click", function (e) {
|
||||||
|
show_step($(".wizard-advanced"));
|
||||||
|
$(this).animate({'opacity': '0'}, 400);
|
||||||
|
e.preventDefault();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -117,3 +117,4 @@ div[data-formset-body], div[data-formset-form], div[data-nested-formset-form], d
|
|||||||
.ticketoutput-panel .panel-title {
|
.ticketoutput-panel .panel-title {
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
35
src/static/pretixcontrol/scss/_vouchers.scss
Normal file
35
src/static/pretixcontrol/scss/_vouchers.scss
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#voucher-create {
|
||||||
|
.wizard-step {
|
||||||
|
}
|
||||||
|
.wizard-step-inner {
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
#id_number, #id_max_usages {
|
||||||
|
width: 75px;
|
||||||
|
}
|
||||||
|
.price {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
.form-options {
|
||||||
|
list-style: none;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.radio {
|
||||||
|
display: block;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
.radio-alt {
|
||||||
|
line-height: 40px;
|
||||||
|
}
|
||||||
|
.radio .help-block {
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 17px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.comment {
|
||||||
|
display: block;
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ $fa-font-path: static("fontawesome/fonts");
|
|||||||
@import "_flags.scss";
|
@import "_flags.scss";
|
||||||
@import "_orders.scss";
|
@import "_orders.scss";
|
||||||
@import "_dashboard.scss";
|
@import "_dashboard.scss";
|
||||||
|
@import "_vouchers.scss";
|
||||||
@import "../../pretixbase/scss/webfont.scss";
|
@import "../../pretixbase/scss/webfont.scss";
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
|
|||||||
Reference in New Issue
Block a user