Backend UX: Restructure payment settings

This commit is contained in:
Raphael Michel
2018-03-23 16:38:06 +01:00
parent 073860cd5b
commit 3a7e0da80b
13 changed files with 239 additions and 189 deletions

View File

@@ -411,7 +411,10 @@ class EventSettingsForm(SettingsForm):
class PaymentSettingsForm(SettingsForm):
payment_term_days = forms.IntegerField(
label=_('Payment term in days'),
help_text=_("The number of days after placing an order the user has to pay to preserve their reservation."),
help_text=_("The number of days after placing an order the user has to pay to preserve their reservation. If "
"you use slow payment methods like bank transfer, we recommend 14 days. If you only use real-time "
"payment methods, we recommend still setting two or three days to allow people to retry failed "
"payments."),
)
payment_term_last = RelativeDateField(
label=_('Last date of payments'),

View File

@@ -5,49 +5,53 @@
<form action="" method="post" class="form-horizontal form-plugins">
{% csrf_token %}
<fieldset>
<legend>{% trans "Payment settings" %}</legend>
{% bootstrap_field sform.payment_term_days layout="control" %}
{% bootstrap_field sform.payment_term_last layout="control" %}
{% bootstrap_field sform.payment_term_weekdays layout="control" %}
{% bootstrap_field sform.payment_term_expire_automatically layout="control" %}
{% bootstrap_field sform.payment_term_accept_late layout="control" %}
{% bootstrap_field sform.tax_rate_default layout="control" %}
<legend>{% trans "Payment providers" %}</legend>
<table class="table table-payment-providers">
<tbody>
{% for provider in providers %}
<tr>
<td>
<strong>{{ provider.verbose_name }}</strong>
</td>
<td>
{% if provider.is_enabled %}
<span class="text-success">
<span class="fa fa-check"></span>
{% trans "Enabled" %}
</span>
{% else %}
<span class="text-danger">
<span class="fa fa-times"></span>
{% trans "Disabled" %}
</span>
{% endif %}
</td>
<td class="text-right">
<a href="{% url 'control:event.settings.payment.provider' event=request.event.slug organizer=request.organizer.slug provider=provider.identifier %}"
class="btn btn-default">
<span class="fa fa-cog"></span>
{% trans "Settings" %}
</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="3">
{% trans "There are no payment providers available. Please go to the plugin settings and activate one or more payment plugins." %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</fieldset>
<fieldset>
<legend>{% trans "Payment providers" %}</legend>
<div class="alert alert-warning">
<span class="fa fa-w fa-legal fa-4x pull-left"></span>
<strong>{% trans "Warning:" %}</strong>
{% blocktrans trimmed %}
Please note that EU Directive 2015/2366 bans surcharging payment fees for most common payment
methods within the European Union. Depending on the payment method, this might affect
selling to consumers only or to business customers as well. Depending on your country, this
legislation might already be in effect or become relevant from January 2018 at the latest. This
is not legal advice. If in doubt, consult a lawyer or refrain from charging payment fees.
{% endblocktrans %}
</div>
{% for provider in providers %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
<a class="collapsed" data-toggle="collapse" href="#{{ provider.identifier }}">
{{ provider.verbose_name }}
<i class="fa fa-angle-down collapse-indicator"></i>
</a>
</h3>
</div>
<div id="{{ provider.identifier }}" class="panel-collapse collapse">
<div class="panel-body">
{% bootstrap_form provider.form layout='control' %}
{% with c=provider.settings_content %}
{% if c %}{{ c|safe }}{% endif %}
{% endwith %}
</div>
</div>
</div>
{% empty %}
<em>{% trans "There are no payment providers available. Please go to the plugin settings and activate one or more payment plugins." %}</em>
{% endfor %}
<legend>{% trans "General payment settings" %}</legend>
{% bootstrap_field form.payment_term_days layout="control" %}
{% bootstrap_field form.payment_term_last layout="control" %}
{% bootstrap_field form.payment_term_weekdays layout="control" %}
{% bootstrap_field form.payment_term_expire_automatically layout="control" %}
{% bootstrap_field form.payment_term_accept_late layout="control" %}
{% bootstrap_field form.tax_rate_default layout="control" %}
</fieldset>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">

View File

@@ -0,0 +1,39 @@
{% extends "pretixcontrol/event/settings_base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block inside %}
<form action="" method="post" class="form-horizontal form-plugins">
{% csrf_token %}
<fieldset>
<legend>
<a href="{% url 'control:event.settings.payment' event=request.event.slug organizer=request.organizer.slug %}"
class="btn btn-default btn-sm btn-link">
<span class="fa fa-caret-left"></span>
{% trans "Back" %}
</a>
{% trans "Payment provider:" %} {{ provider.verbose_name }}
</legend>
<div class="alert alert-warning">
<span class="fa fa-w fa-legal fa-2x pull-left"></span>
<strong>{% trans "Warning:" %}</strong>
{% blocktrans trimmed %}
Please note that EU Directive 2015/2366 bans surcharging payment fees for most common payment
methods within the European Union. If in doubt, consult a lawyer or refrain from charging payment
fees.
{% endblocktrans %}
<br>
{% blocktrans trimmed %}
In simple terms, this means you need to pay any fees imposed by the payment providers and cannot
pass it on to your customers.
{% endblocktrans %}
</div>
{% bootstrap_form form layout='control' %}
{% if settings_content %}{{ settings_content|safe }}{% endif %}
</fieldset>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
</button>
</div>
</form>
{% endblock %}

View File

@@ -76,6 +76,8 @@ urlpatterns = [
url(r'^settings/$', event.EventUpdate.as_view(), name='event.settings'),
url(r'^settings/plugins$', event.EventPlugins.as_view(), name='event.settings.plugins'),
url(r'^settings/permissions$', event.EventPermissions.as_view(), name='event.settings.permissions'),
url(r'^settings/payment/(?P<provider>[^/]+)$', event.PaymentProviderSettings.as_view(),
name='event.settings.payment.provider'),
url(r'^settings/payment$', event.PaymentSettings.as_view(), name='event.settings.payment'),
url(r'^settings/tickets$', event.TicketSettings.as_view(), name='event.settings.tickets'),
url(r'^settings/tickets/preview/(?P<output>[^/]+)$', event.TicketSettingsPreview.as_view(),

View File

@@ -237,83 +237,11 @@ class EventPlugins(EventSettingsViewMixin, EventPermissionRequiredMixin, Templat
})
class PaymentSettings(EventSettingsViewMixin, EventPermissionRequiredMixin, TemplateView, SingleObjectMixin):
class PaymentProviderSettings(EventSettingsViewMixin, EventPermissionRequiredMixin, TemplateView, SingleObjectMixin):
model = Event
context_object_name = 'event'
permission = 'can_change_event_settings'
template_name = 'pretixcontrol/event/payment.html'
def get_object(self, queryset=None) -> Event:
return self.request.event
@cached_property
def provider_forms(self) -> list:
providers = []
for provider in self.request.event.get_payment_providers().values():
provider.form = ProviderForm(
obj=self.request.event,
settingspref=provider.settings.get_prefix(),
data=(self.request.POST if self.request.method == 'POST' else None)
)
provider.form.fields = OrderedDict(
[
('%s%s' % (provider.settings.get_prefix(), k), v)
for k, v in provider.settings_form_fields.items()
]
)
provider.settings_content = provider.settings_content_render(self.request)
provider.form.prepare_fields()
if provider.settings_content or provider.form.fields:
# Exclude providers which do not provide any settings
providers.append(provider)
return providers
def get_context_data(self, *args, **kwargs) -> dict:
context = super().get_context_data(*args, **kwargs)
context['sform'] = self.sform
return context
@cached_property
def sform(self):
return PaymentSettingsForm(
obj=self.object,
prefix='settings',
data=self.request.POST if self.request.method == 'POST' else None
)
def get(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data(object=self.object)
context['providers'] = self.provider_forms
return self.render_to_response(context)
@transaction.atomic
def post(self, request, *args, **kwargs):
self.object = self.get_object()
success = self.sform.is_valid()
if success:
self.sform.save()
if self.sform.has_changed():
self.request.event.log_action('pretix.event.settings', user=self.request.user, data={
k: self.request.event.settings.get(k) for k in self.sform.changed_data
})
for provider in self.provider_forms:
if provider.form.is_valid():
if provider.form.has_changed():
self.request.event.log_action(
'pretix.event.payment.provider.' + provider.identifier, user=self.request.user, data={
k: provider.form.cleaned_data.get(k) for k in provider.form.changed_data
}
)
provider.form.save()
else:
success = False
if success:
messages.success(self.request, _('Your changes have been saved.'))
return redirect(self.get_success_url())
else:
messages.error(self.request, _('We could not save your changes. See below for details.'))
return self.get(request)
template_name = 'pretixcontrol/event/payment_provider.html'
def get_success_url(self) -> str:
return reverse('control:event.settings.payment', kwargs={
@@ -321,6 +249,63 @@ class PaymentSettings(EventSettingsViewMixin, EventPermissionRequiredMixin, Temp
'event': self.get_object().slug,
})
@cached_property
def object(self):
return self.request.event
def get_object(self, queryset=None):
return self.object
@cached_property
def provider(self):
provider = self.request.event.get_payment_providers()[self.kwargs['provider']]
if not provider:
raise Http404()
return provider
@cached_property
def form(self):
form = ProviderForm(
obj=self.request.event,
settingspref=self.provider.settings.get_prefix(),
data=(self.request.POST if self.request.method == 'POST' else None)
)
form.fields = OrderedDict(
[
('%s%s' % (self.provider.settings.get_prefix(), k), v)
for k, v in self.provider.settings_form_fields.items()
]
)
form.prepare_fields()
return form
@cached_property
def settings_content(self):
return self.provider.settings_content_render(self.request)
def get_context_data(self, *args, **kwargs) -> dict:
context = super().get_context_data(*args, **kwargs)
context['form'] = self.form
context['provider'] = self.provider
context['settings_content'] = self.settings_content
return context
@transaction.atomic
def post(self, request, *args, **kwargs):
if self.form.is_valid():
if self.form.has_changed():
self.request.event.log_action(
'pretix.event.payment.provider.' + self.provider.identifier, user=self.request.user, data={
k: self.form.cleaned_data.get(k) for k in self.form.changed_data
}
)
self.form.save()
messages.success(self.request, _('Your changes have been saved.'))
return redirect(self.get_success_url())
else:
messages.error(self.request, _('We could not save your changes. See below for details.'))
return self.get(request)
class EventSettingsFormView(EventPermissionRequiredMixin, FormView):
model = Event
@@ -368,6 +353,27 @@ class EventSettingsFormView(EventPermissionRequiredMixin, FormView):
return self.get(request)
class PaymentSettings(EventSettingsViewMixin, EventSettingsFormView):
template_name = 'pretixcontrol/event/payment.html'
form_class = PaymentSettingsForm
permission = 'can_change_event_settings'
def get_success_url(self) -> str:
return reverse('control:event.settings.payment', kwargs={
'organizer': self.request.organizer.slug,
'event': self.request.event.slug,
})
def get_context_data(self, *args, **kwargs) -> dict:
context = super().get_context_data(*args, **kwargs)
context['providers'] = sorted(
[p for p in self.request.event.get_payment_providers().values()
if not p.is_implicit and (p.settings_form_fields or p.settings_content_render(self.request))],
key=lambda s: s.verbose_name
)
return context
class InvoiceSettings(EventSettingsViewMixin, EventSettingsFormView):
model = Event
form_class = InvoiceSettingsForm