Event cancellation: Add safety and security checks (#5565)

* Event cancellation: Add safety and security checks

When cancelling an event, a large sum of money might be refunded
instantly. This PR adds safety features around this by

- doing a dry-run first that shows a preview of the expected refund sum

- sending a confirmation mode via email for any automatic refunds of more than 100 currency units

- keeping a more detailed log of the settings this was executed with

* Update src/pretix/control/views/orders.py

Co-authored-by: luelista <weller@rami.io>

---------

Co-authored-by: luelista <weller@rami.io>
This commit is contained in:
Raphael Michel
2025-10-29 08:53:48 +01:00
committed by GitHub
parent e386ed4352
commit 1e0ede529c
9 changed files with 422 additions and 103 deletions

View File

@@ -79,9 +79,15 @@
{% bootstrap_field form.send_waitinglist_message layout="horizontal" %}
</fieldset>
<div class="form-group submit-group">
<button type="submit" class="btn btn-danger btn-save">
{% trans "Cancel all orders" %}
</button>
{% if dry_run_supported %}
<button type="submit" class="btn btn-default btn-save">
{% trans "Preview refund amount" %}
</button>
{% else %}
<button type="submit" class="btn btn-danger btn-save">
{% trans "Cancel all orders" %}
</button>
{% endif %}
</div>
</form>
{% endblock %}

View File

@@ -0,0 +1,85 @@
{% extends "pretixcontrol/event/base.html" %}
{% load i18n %}
{% load eventsignal %}
{% load bootstrap3 %}
{% load money %}
{% block title %}{% trans "Cancel event" %}{% endblock %}
{% block content %}
<h1>{% trans "Cancel event" %}</h1>
<form action="" method="post" class="form-horizontal" data-asynctask data-asynctask-download data-asynctask-long>
{% csrf_token %}
{% bootstrap_form_errors form %}
<p>
{% blocktrans trimmed %}
If you proceed, the system will do the following:
{% endblocktrans %}
</p>
<ul>
<li>
{% blocktrans trimmed count count=dryrun_result.cancel_full_total %}
{{ count }} order will be canceled fully
{% plural %}
{{ count }} orders will be canceled fully
{% endblocktrans %}
</li>
<li>
{% blocktrans trimmed count count=dryrun_result.cancel_partial_total %}
{{ count }} order will be canceled partially
{% plural %}
{{ count }} orders will be canceled partially
{% endblocktrans %}
</li>
<li>
{% if dryrun_result.kwargs.auto_refund and dryrun_result.refund_total %}
<strong>
{% endif %}
{% blocktrans trimmed with amount=dryrun_result.refund_total|money:request.event.currency %}
{{ amount }} are eligible for refunds.
{% endblocktrans %}
{% if dryrun_result.kwargs.auto_refund %}
{% trans "The system will attempt to refund the money automatically if supported by the payment method." %}
{% elif dryrun_result.kwargs.manual_refund %}
{% trans "The system will create manual refunds that you need to execute." %}
{% else %}
{% trans "Refunds will not happen automatically." %}
{% endif %}
{% if dryrun_result.kwargs.auto_refund %}
</strong>
{% endif %}
</li>
{% if dryrun_result.kwargs.send %}
<li>
{% trans "Inform all customers via email." %}
</li>
{% endif %}
{% if dryrun_result.kwargs.send_waitinglist %}
<li>
{% trans "Inform all waiting list contacts via email." %}
</li>
{% endif %}
</ul>
<p>
{% blocktrans trimmed %}
These numbers are estimates and may change if the data in your event recently changed.
{% endblocktrans %}
</p>
{% bootstrap_form_errors form %}
{% if form.confirmation_code %}
{% bootstrap_field form.confirm layout="control" %}
{% bootstrap_field form.confirmation_code layout="control" %}
{% else %}
{% bootstrap_field form.confirm layout="inline" form_group_class="" %}
{% endif %}
<div class="form-group submit-group">
<button type="submit" class="btn btn-danger btn-save">
{% if dryrun_result.kwargs.auto_refund and dryrun_result.refund_total %}
{% blocktrans trimmed with amount=dryrun_result.refund_total|money:request.event.currency %}
Proceed and refund approx. {{ amount }}
{% endblocktrans %}
{% else %}
{% trans "Proceed and cancel orders" %}
{% endif %}
</button>
</div>
</form>
{% endblock %}