mirror of
https://github.com/pretix/pretix.git
synced 2026-05-06 15:24:02 +00:00
Allow users to increase cancellation fees (#1622)
* Allow users to increase cancellation fees * Fix typo
This commit is contained in:
@@ -611,6 +611,7 @@ class EventSettingsSerializer(serializers.Serializer):
|
||||
'cancel_allow_user_paid_keep',
|
||||
'cancel_allow_user_paid_keep_fees',
|
||||
'cancel_allow_user_paid_keep_percentage',
|
||||
'cancel_allow_user_paid_adjust_fees',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
@@ -907,6 +907,16 @@ DEFAULTS = {
|
||||
label=_("Keep a percentual cancellation fee"),
|
||||
)
|
||||
},
|
||||
'cancel_allow_user_paid_adjust_fees': {
|
||||
'default': 'False',
|
||||
'type': bool,
|
||||
'form_class': forms.BooleanField,
|
||||
'serializer_class': serializers.BooleanField,
|
||||
'form_kwargs': dict(
|
||||
label=_("Allow customers to voluntarily choose a lower refund"),
|
||||
help_text=_("With this option enabled, your customers can choose to get a smaller refund to support you.")
|
||||
)
|
||||
},
|
||||
'cancel_allow_user_paid_until': {
|
||||
'default': None,
|
||||
'type': RelativeDateWrapper,
|
||||
|
||||
@@ -565,6 +565,7 @@ class CancelSettingsForm(SettingsForm):
|
||||
'cancel_allow_user_paid_keep',
|
||||
'cancel_allow_user_paid_keep_fees',
|
||||
'cancel_allow_user_paid_keep_percentage',
|
||||
'cancel_allow_user_paid_adjust_fees',
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
{% bootstrap_field form.cancel_allow_user_paid_keep_percentage layout="control" %}
|
||||
{% bootstrap_field form.cancel_allow_user_paid_keep_fees layout="control" %}
|
||||
{% bootstrap_field form.cancel_allow_user_paid_until layout="control" %}
|
||||
{% bootstrap_field form.cancel_allow_user_paid_adjust_fees layout="control" %}
|
||||
{% if not gets_notification %}
|
||||
<div class="alert alert-warning">
|
||||
{% blocktrans trimmed %}
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
<meta name="msapplication-config" content="{% url "presale:browserconfig.xml" %}">
|
||||
<meta name="theme-color" content="{{ settings.primary_color|default:"#3b1c4a" }}">
|
||||
</head>
|
||||
<body class="nojs" data-locale="{{ request.LANGUAGE_CODE }}" data-now="{% now "U.u" %}" data-datetimeformat="{{ js_datetime_format }}" data-timeformat="{{ js_time_format }}" data-dateformat="{{ js_date_format }}" data-datetimelocale="{{ js_locale }}">
|
||||
<body class="nojs" data-locale="{{ request.LANGUAGE_CODE }}" data-now="{% now "U.u" %}" data-datetimeformat="{{ js_datetime_format }}" data-timeformat="{{ js_time_format }}" data-dateformat="{{ js_date_format }}" data-datetimelocale="{{ js_locale }}" data-currency="{{ request.event.currency }}">
|
||||
{{ html_page_header|safe }}
|
||||
{% block above %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
{% load i18n %}
|
||||
{% load money %}
|
||||
{% load eventurl %}
|
||||
{% load l10n %}
|
||||
{% block title %}{% trans "Cancel order" %}{% endblock %}
|
||||
{% block content %}
|
||||
<h2>
|
||||
@@ -9,38 +10,79 @@
|
||||
Cancel order: {{ code }}
|
||||
{% endblocktrans %}
|
||||
</h2>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Do you really want to cancel this order? You cannot revert this action.
|
||||
{% endblocktrans %}
|
||||
{% trans "This will invalidate all tickets in this order." %}
|
||||
</p>
|
||||
{% if refund_amount %}
|
||||
{% if can_auto_refund %}
|
||||
<p>
|
||||
<strong>
|
||||
{% blocktrans trimmed with amount=refund_amount|money:request.event.currency %}
|
||||
The refund amount of {{ amount }} will automatically be sent back to your original payment method. Depending on the payment method,
|
||||
please allow for up to two weeks before this appears on your statement.
|
||||
{% endblocktrans %}
|
||||
</strong>
|
||||
</p>
|
||||
{% else %}
|
||||
<div class="alert alert-warning">
|
||||
{% blocktrans trimmed with amount=refund_amount|money:request.event.currency %}
|
||||
With to the payment method you used, the refund amount of {{ amount }} <strong>can not be sent back to you automatically</strong>. Instead, the
|
||||
event organizer will need to initiate the transfer manually. Please be patient as this might take a bit longer.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<form method="post" action="{% eventurl request.event "presale:event.order.cancel.do" secret=order.secret order=order.code %}" data-asynctask>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Do you really want to cancel this order? You cannot revert this action.
|
||||
{% endblocktrans %}
|
||||
{% trans "This will invalidate all tickets in this order." %}
|
||||
</p>
|
||||
|
||||
{% if request.event.settings.cancel_allow_user_paid_adjust_fees %}
|
||||
<p>
|
||||
{% if cancel_fee %}
|
||||
{% blocktrans trimmed with fee=order.user_cancel_fee|money:request.event.currency %}
|
||||
If you want, you can request a refund for the full amount minus a cancellation fee of
|
||||
{{ fee }}.
|
||||
{% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans trimmed with fee=order.user_cancel_fee|money:request.event.currency %}
|
||||
If you want, you can request a full refund.
|
||||
{% endblocktrans %}
|
||||
{% endif %}
|
||||
{% trans "However, if you want us to help keep the lights on here, please consider using the slider below to request a smaller refund. Thank you!" %}
|
||||
</p>
|
||||
<div class="cancel-fee-slider">
|
||||
<div id="cancel-fee-keep"></div>
|
||||
<input
|
||||
id="cancel-fee-slider"
|
||||
type="text"
|
||||
name="cancel_fee"
|
||||
class="col-md-6 col-sm-12"
|
||||
value="{{ cancel_fee|stringformat:".2f" }}"
|
||||
data-slider-min="{{ cancel_fee|stringformat:".2f" }}"
|
||||
data-slider-value="{{ cancel_fee|stringformat:".2f" }}"
|
||||
data-slider-step="0.01"
|
||||
data-slider-max="{{ order.payment_refund_sum|stringformat:".2f" }}"
|
||||
data-slider-tooltip="hide"/>
|
||||
<div id="cancel-fee-refund">AS</div>
|
||||
</div>
|
||||
<div class="text-center" id="cancel-fee-custom-link">
|
||||
<a id="cancel-fee-custom"><small>Enter custom amount</small></a>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>
|
||||
<strong>{% trans "Refund amount:" %}</strong> {{ refund_amount|money:request.event.currency }}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if refund_amount %}
|
||||
{% if can_auto_refund %}
|
||||
<p>
|
||||
<strong>
|
||||
{% blocktrans trimmed %}
|
||||
The refund amount will automatically be sent back to your original payment method. Depending
|
||||
on the payment method, please allow for up to two weeks before this appears on your
|
||||
statement.
|
||||
{% endblocktrans %}
|
||||
</strong>
|
||||
</p>
|
||||
{% else %}
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
With the payment method you used, the refund amount <strong>can not be sent back to you
|
||||
automatically</strong>. Instead, the event organizer will need to initiate the transfer
|
||||
manually. Please be patient as this might take a bit longer.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% csrf_token %}
|
||||
<div class="row checkout-button-row">
|
||||
<div class="col-md-4">
|
||||
<a class="btn btn-block btn-default btn-lg"
|
||||
href="{% eventurl request.event "presale:event.order" secret=order.secret order=order.code %}">
|
||||
href="{% eventurl request.event "presale:event.order" secret=order.secret order=order.code %}">
|
||||
{% trans "No, take me back" %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
<script type="text/javascript" src="{% static "js/jquery.formset.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "bootstrap/js/bootstrap.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "datetimepicker/bootstrap-datetimepicker.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "slider/bootstrap-slider.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "pretixcontrol/js/jquery.qrcode.min.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "pretixpresale/js/widget/floatformat.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "pretixpresale/js/ui/questions.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "pretixpresale/js/ui/main.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "pretixbase/js/asynctask.js" %}"></script>
|
||||
|
||||
@@ -723,8 +723,10 @@ class OrderCancel(EventViewMixin, OrderDetailMixin, TemplateView):
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['order'] = self.order
|
||||
refund_amount = self.order.payment_refund_sum - self.order.user_cancel_fee
|
||||
fee = self.order.user_cancel_fee
|
||||
refund_amount = self.order.payment_refund_sum - fee
|
||||
proposals = self.order.propose_auto_refunds(refund_amount)
|
||||
ctx['cancel_fee'] = fee
|
||||
ctx['refund_amount'] = refund_amount
|
||||
ctx['can_auto_refund'] = sum(proposals.values()) == refund_amount
|
||||
return ctx
|
||||
@@ -750,6 +752,13 @@ class OrderCancelDo(EventViewMixin, OrderDetailMixin, AsyncAction, View):
|
||||
fee = None
|
||||
if self.order.status == Order.STATUS_PAID and self.order.total != Decimal('0.00'):
|
||||
fee = self.order.user_cancel_fee
|
||||
if 'cancel_fee' in request.POST and self.request.event.settings.cancel_allow_user_paid_adjust_fees:
|
||||
custom_fee = Decimal(request.POST.get('cancel_fee'))
|
||||
if fee <= custom_fee <= self.order.payment_refund_sum:
|
||||
fee = custom_fee
|
||||
else:
|
||||
messages.error(request, _('You chose an invalid cancellation fee.'))
|
||||
return redirect(self.get_order_url())
|
||||
return self.do(self.order.pk, cancellation_fee=fee, try_auto_refund=True)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
@@ -14,6 +14,14 @@ function ngettext(singular, plural, count) {
|
||||
return plural;
|
||||
}
|
||||
|
||||
function interpolate(fmt, object, named) {
|
||||
if (named) {
|
||||
return fmt.replace(/%\(\w+\)s/g, function(match){return String(obj[match.slice(2,-2)])});
|
||||
} else {
|
||||
return fmt.replace(/%s/g, function(match){return String(obj.shift())});
|
||||
}
|
||||
}
|
||||
|
||||
var form_handlers = function (el) {
|
||||
el.find(".datetimepicker").each(function () {
|
||||
$(this).datetimepicker({
|
||||
@@ -300,6 +308,44 @@ $(function () {
|
||||
|
||||
form_handlers($("body"));
|
||||
|
||||
var cancel_fee_slider_update = function () {
|
||||
if (typeof django === "undefined") {
|
||||
window.setTimeout(cancel_fee_slider_update, 100);
|
||||
return;
|
||||
}
|
||||
$("#cancel-fee-keep").text(interpolate(
|
||||
gettext("We keep %(currency)s %(amount)s"),
|
||||
{
|
||||
'currency': $("body").attr("data-currency"),
|
||||
'amount': floatformat(cancel_fee_slider.getValue(), 2)
|
||||
},
|
||||
true
|
||||
));
|
||||
$("#cancel-fee-refund").text(interpolate(
|
||||
gettext("You get %(currency)s %(amount)s back"),
|
||||
{
|
||||
'currency': $("body").attr("data-currency"),
|
||||
'amount': floatformat((cancel_fee_slider.getAttribute("max") - cancel_fee_slider.getValue()), 2)
|
||||
},
|
||||
true
|
||||
));
|
||||
}
|
||||
var cancel_fee_slider = $('#cancel-fee-slider').slider({
|
||||
}).on('slide', function () {
|
||||
cancel_fee_slider_update();
|
||||
}).data('slider');
|
||||
if (cancel_fee_slider) {
|
||||
cancel_fee_slider_update();
|
||||
$("#cancel-fee-custom").click(function () {
|
||||
try {
|
||||
var newinp = parseFloat(prompt(gettext("Please enter the amount we can keep."), cancel_fee_slider.getValue().toString()).replace(',', '.'));
|
||||
cancel_fee_slider.setValue(newinp);
|
||||
cancel_fee_slider_update();
|
||||
} catch {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Lightbox
|
||||
lightbox.init();
|
||||
});
|
||||
|
||||
@@ -196,3 +196,34 @@ section.front-page {
|
||||
h2.subevent-head {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.cancel-fee-slider {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.slider-selection {
|
||||
background: $brand-success;
|
||||
}
|
||||
.slider-handle {
|
||||
@include slider_background-image($brand-success, darken($brand-success, 5%), mix($brand-success, darken($brand-success, 5%)));
|
||||
}
|
||||
|
||||
input {
|
||||
flex: 0;
|
||||
}
|
||||
& > div {
|
||||
flex: 1;
|
||||
font-weight: bold;
|
||||
}
|
||||
& > div:first-child {
|
||||
text-align: right;
|
||||
padding-right: 20px;
|
||||
}
|
||||
& > div:last-child {
|
||||
text-align: left;
|
||||
padding-left: 20px;
|
||||
}
|
||||
}
|
||||
#cancel-fee-custom-link {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
@import "../../pretixbase/scss/_theme.scss";
|
||||
@import "../../lightbox/css/lightbox.scss";
|
||||
@import "../../datetimepicker/_bootstrap-datetimepicker.scss";
|
||||
@import "../../slider/_bootstrap-slider.scss";
|
||||
@import "../../fontawesome/scss/font-awesome.scss";
|
||||
|
||||
@import "_theme.scss";
|
||||
|
||||
387
src/pretix/static/slider/_bootstrap-slider.scss
Normal file
387
src/pretix/static/slider/_bootstrap-slider.scss
Normal file
@@ -0,0 +1,387 @@
|
||||
/*! =========================================================
|
||||
* bootstrap-slider.js
|
||||
*
|
||||
* Maintainers:
|
||||
* Kyle Kemp
|
||||
* - Twitter: @seiyria
|
||||
* - Github: seiyria
|
||||
* Rohit Kalkur
|
||||
* - Twitter: @Rovolutionary
|
||||
* - Github: rovolution
|
||||
*
|
||||
* =========================================================
|
||||
*
|
||||
* bootstrap-slider is released under the MIT License
|
||||
* Copyright (c) 2019 Kyle Kemp, Rohit Kalkur, and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* ========================================================= */
|
||||
|
||||
$slider-line-height: 20px !default;
|
||||
$slider-border-radius: 4px !default;
|
||||
$slider-horizontal-width: 210px !default;
|
||||
$slider-vertical-height: 210px !default;
|
||||
|
||||
// Primary colors
|
||||
$slider-primary: null !default;
|
||||
@if variable-exists(brand-primary) {
|
||||
$slider-primary: $brand-primary !default;
|
||||
} @else {
|
||||
$slider-primary: #0480BE !default;
|
||||
}
|
||||
|
||||
$slider-primary-top: $slider-primary !default;
|
||||
$slider-primary-bottom: darken($slider-primary, 5%) !default;
|
||||
$slider-secondary-top: saturate(lighten($slider-primary, 28%), 20%) !default;
|
||||
$slider-secondary-bottom: saturate(lighten($slider-primary, 23%), 2%) !default;
|
||||
|
||||
// grays for slider channel and disabled states
|
||||
$slider-gray-1: #BEBEBE !default;
|
||||
$slider-gray-2: #DFDFDF !default;
|
||||
$slider-gray-3: #E5E5E5 !default;
|
||||
$slider-gray-4: #E9E9E9 !default;
|
||||
$slider-gray-5: #F5F5F5 !default;
|
||||
$slider-gray-6: #F9F9F9 !default;
|
||||
|
||||
// unicode color for demo page
|
||||
$slider-unicode-color: #726204 !default;
|
||||
|
||||
@mixin slider_background-image($colorstart:#F5F5F5, $colorend:#F9F9F9, $backcolor: #F7F7F7) {
|
||||
background-color: $backcolor;
|
||||
background-image: -moz-linear-gradient(top, $colorstart, $colorend);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from($colorstart), to($colorend));
|
||||
background-image: -webkit-linear-gradient(top, $colorstart, $colorend);
|
||||
background-image: -o-linear-gradient(top, $colorstart, $colorend);
|
||||
background-image: linear-gradient(to bottom, $colorstart, $colorend);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#{$colorstart}', endColorstr='#{$colorend}', GradientType=0);
|
||||
}
|
||||
|
||||
@mixin slider_box-sizing($value) {
|
||||
-webkit-box-sizing: $value;
|
||||
-moz-box-sizing: $value;
|
||||
box-sizing: $value;
|
||||
}
|
||||
|
||||
@mixin slider_box-shadow($value...) {
|
||||
-webkit-box-shadow: $value;
|
||||
-moz-box-shadow: $value;
|
||||
box-shadow: $value;
|
||||
}
|
||||
|
||||
@mixin slider_border-radius($value) {
|
||||
-webkit-border-radius: $value;
|
||||
-moz-border-radius: $value;
|
||||
border-radius: $value;
|
||||
}
|
||||
|
||||
.slider {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
|
||||
&.slider-horizontal {
|
||||
width: $slider-horizontal-width;
|
||||
height: $slider-line-height;
|
||||
|
||||
.slider-track {
|
||||
height: $slider-line-height/2;
|
||||
width: 100%;
|
||||
margin-top: -$slider-line-height/4;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.slider-selection, .slider-track-low, .slider-track-high {
|
||||
height: 100%;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.slider-tick,
|
||||
.slider-handle {
|
||||
margin-left: -$slider-line-height/2;
|
||||
|
||||
&.triangle {
|
||||
position: relative;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
border-width: 0 $slider-line-height/2 $slider-line-height/2 $slider-line-height/2;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-bottom-color: $slider-primary-bottom;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.slider-tick-container {
|
||||
white-space: nowrap;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.slider-tick-label-container {
|
||||
white-space: nowrap;
|
||||
margin-top: $slider-line-height;
|
||||
|
||||
.slider-tick-label {
|
||||
display: inline-block;
|
||||
padding-top: $slider-line-height * 1.2;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
&.slider-rtl {
|
||||
.slider-track {
|
||||
left: initial;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.slider-tick,
|
||||
.slider-handle {
|
||||
margin-left: initial;
|
||||
margin-right: -$slider-line-height/2;
|
||||
}
|
||||
|
||||
.slider-tick-container {
|
||||
left: initial;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.slider-vertical {
|
||||
height: $slider-vertical-height;
|
||||
width: $slider-line-height;
|
||||
|
||||
.slider-track {
|
||||
width: $slider-line-height/2;
|
||||
height: 100%;
|
||||
left: 25%;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.slider-selection {
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.slider-track-low, .slider-track-high {
|
||||
width: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.slider-tick,
|
||||
.slider-handle {
|
||||
margin-top: -$slider-line-height/2;
|
||||
|
||||
&.triangle {
|
||||
border-width: $slider-line-height/2 0 $slider-line-height/2 $slider-line-height/2;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
border-left-color: $slider-primary-bottom;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.slider-tick-label-container {
|
||||
white-space: nowrap;
|
||||
|
||||
.slider-tick-label {
|
||||
padding-left: $slider-line-height * .2;
|
||||
}
|
||||
}
|
||||
|
||||
&.slider-rtl {
|
||||
.slider-track {
|
||||
left: initial;
|
||||
right: 25%;
|
||||
}
|
||||
|
||||
.slider-selection {
|
||||
left: initial;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.slider-tick,
|
||||
.slider-handle {
|
||||
&.triangle {
|
||||
border-width: $slider-line-height/2 $slider-line-height/2 $slider-line-height/2 0;
|
||||
}
|
||||
}
|
||||
|
||||
.slider-tick-label-container {
|
||||
.slider-tick-label {
|
||||
padding-left: initial;
|
||||
padding-right: $slider-line-height * .2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.slider-disabled {
|
||||
.slider-handle {
|
||||
@include slider_background-image($slider-gray-2, $slider-gray-1, mix($slider-gray-2, $slider-gray-1));
|
||||
}
|
||||
|
||||
.slider-track {
|
||||
@include slider_background-image($slider-gray-3, $slider-gray-4, mix($slider-gray-3, $slider-gray-4));
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tooltip-inner {
|
||||
white-space: nowrap;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
pointer-events: none;
|
||||
|
||||
&.top {
|
||||
margin-top: -36px;
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip-inner {
|
||||
white-space: nowrap;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.slider-track {
|
||||
@include slider_background-image($slider-gray-5, $slider-gray-6, mix($slider-gray-5, $slider-gray-6));
|
||||
@include slider_box-shadow(inset 0 1px 2px rgba(0, 0, 0, 0.1));
|
||||
@include slider_border-radius($slider-border-radius);
|
||||
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.slider-selection {
|
||||
@include slider_background-image($slider-gray-6, $slider-gray-5, mix($slider-gray-6, $slider-gray-5));
|
||||
@include slider_box-shadow(inset 0 -1px 0 rgba(0, 0, 0, 0.15));
|
||||
@include slider_box-sizing(border-box);
|
||||
@include slider_border-radius($slider-border-radius);
|
||||
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.slider-selection.tick-slider-selection {
|
||||
@include slider_background-image($slider-secondary-top, $slider-secondary-bottom, mix($slider-secondary-top, $slider-secondary-bottom));
|
||||
}
|
||||
|
||||
.slider-track-low, .slider-track-high {
|
||||
@include slider_box-sizing(border-box);
|
||||
@include slider_border-radius($slider-border-radius);
|
||||
|
||||
position: absolute;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.slider-handle {
|
||||
@include slider_background-image($slider-primary-top, $slider-primary-bottom, mix($slider-primary-top, $slider-primary-bottom));
|
||||
@include slider_box-shadow(inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05));
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: $slider-line-height;
|
||||
height: $slider-line-height;
|
||||
background-color: $slider-primary;
|
||||
border: 0px solid transparent;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.round {
|
||||
@include slider_border-radius($slider-line-height);
|
||||
}
|
||||
|
||||
&.triangle {
|
||||
background: transparent none;
|
||||
}
|
||||
|
||||
&.custom {
|
||||
background: transparent none;
|
||||
|
||||
&::before {
|
||||
line-height: $slider-line-height;
|
||||
font-size: 20px;
|
||||
content: '\2605'; //unicode star character
|
||||
color: $slider-unicode-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.slider-tick {
|
||||
@include slider_background-image($slider-gray-5, $slider-gray-6, mix($slider-gray-5, $slider-gray-6));
|
||||
@include slider_box-shadow(inset 0 -1px 0 rgba(0, 0, 0, 0.15));
|
||||
@include slider_box-sizing(border-box);
|
||||
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
width: $slider-line-height;
|
||||
height: $slider-line-height;
|
||||
filter: none;
|
||||
opacity: 0.8;
|
||||
border: 0px solid transparent;
|
||||
|
||||
&.round {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&.triangle {
|
||||
background: transparent none;
|
||||
}
|
||||
|
||||
&.custom {
|
||||
background: transparent none;
|
||||
|
||||
&::before {
|
||||
line-height: $slider-line-height;
|
||||
font-size: 20px;
|
||||
content: '\2605'; //unicode star character
|
||||
color: $slider-unicode-color;
|
||||
}
|
||||
}
|
||||
|
||||
&.in-selection {
|
||||
@include slider_background-image($slider-secondary-top, $slider-secondary-bottom, mix($slider-secondary-top, $slider-secondary-bottom));
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
2061
src/pretix/static/slider/bootstrap-slider.js
vendored
Normal file
2061
src/pretix/static/slider/bootstrap-slider.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -410,7 +410,7 @@ class OrdersTest(BaseOrdersTest):
|
||||
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert 'alert-warning' not in response.rendered_content
|
||||
assert 'manually' not in response.rendered_content
|
||||
response = self.client.post(
|
||||
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||
}, follow=True)
|
||||
@@ -424,6 +424,58 @@ class OrdersTest(BaseOrdersTest):
|
||||
with scopes_disabled():
|
||||
assert self.order.refunds.count() == 1
|
||||
|
||||
def test_orders_cancel_paid_custom_fee_autorefund(self):
|
||||
self.order.status = Order.STATUS_PAID
|
||||
self.order.save()
|
||||
with scopes_disabled():
|
||||
self.order.payments.create(provider='testdummy_partialrefund', amount=self.order.total, state=OrderPayment.PAYMENT_STATE_CONFIRMED)
|
||||
self.event.settings.cancel_allow_user_paid = True
|
||||
self.event.settings.cancel_allow_user_paid_keep = Decimal('3.00')
|
||||
self.event.settings.cancel_allow_user_paid_adjust_fees = True
|
||||
response = self.client.get(
|
||||
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
||||
)
|
||||
assert response.status_code == 200
|
||||
response = self.client.post(
|
||||
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||
'cancel_fee': '6.00'
|
||||
}, follow=True)
|
||||
self.assertRedirects(response,
|
||||
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
||||
self.order.secret),
|
||||
target_status_code=200)
|
||||
self.order.refresh_from_db()
|
||||
assert self.order.status == Order.STATUS_PAID
|
||||
assert self.order.total == Decimal('6.00')
|
||||
with scopes_disabled():
|
||||
assert self.order.refunds.count() == 1
|
||||
|
||||
def test_orders_cancel_paid_custom_fee_limit(self):
|
||||
self.order.status = Order.STATUS_PAID
|
||||
self.order.save()
|
||||
with scopes_disabled():
|
||||
self.order.payments.create(provider='testdummy_partialrefund', amount=self.order.total, state=OrderPayment.PAYMENT_STATE_CONFIRMED)
|
||||
self.event.settings.cancel_allow_user_paid = True
|
||||
self.event.settings.cancel_allow_user_paid_keep = Decimal('3.00')
|
||||
self.event.settings.cancel_allow_user_paid_adjust_fees = True
|
||||
response = self.client.get(
|
||||
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
||||
)
|
||||
assert response.status_code == 200
|
||||
response = self.client.post(
|
||||
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||
'cancel_fee': '2.00'
|
||||
}, follow=True)
|
||||
self.assertRedirects(response,
|
||||
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
||||
self.order.secret),
|
||||
target_status_code=200)
|
||||
self.order.refresh_from_db()
|
||||
assert self.order.status == Order.STATUS_PAID
|
||||
assert self.order.total == Decimal('23.00')
|
||||
with scopes_disabled():
|
||||
assert self.order.refunds.count() == 0
|
||||
|
||||
def test_orders_cancel_paid_fee_no_autorefund(self):
|
||||
self.order.status = Order.STATUS_PAID
|
||||
self.order.save()
|
||||
@@ -436,13 +488,12 @@ class OrdersTest(BaseOrdersTest):
|
||||
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
||||
)
|
||||
assert response.status_code == 200
|
||||
print(response.rendered_content)
|
||||
assert 'cancellation fee of <strong>€3.00</strong>' in response.rendered_content
|
||||
response = self.client.get(
|
||||
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert 'alert-warning' in response.rendered_content
|
||||
assert 'manually' in response.rendered_content
|
||||
assert '20.00' in response.rendered_content
|
||||
response = self.client.post(
|
||||
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||
|
||||
Reference in New Issue
Block a user