mirror of
https://github.com/pretix/pretix.git
synced 2026-05-07 15:34:02 +00:00
Queueing and mapping utilities for outbound data sync (#4881)
Add a registry for datasync providers and an associated sync queue, to be used by plugins that transfer data from pretix orders to external systems. Additionally, provide a generic data mapping interface to be used in settings pages of such plugins, to let users configure which information from pretix to fill into which data fields of the external system. --------- Co-authored-by: Raphael Michel <michel@rami.io>
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
{% load i18n %}
|
||||
{% load eventurl %}
|
||||
{% load bootstrap3 %}
|
||||
{% load escapejson %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
{% trans "Data transfer to external systems" %}
|
||||
</h3>
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
{% for identifier, display_name, pending, objects in providers %}
|
||||
<li class="list-group-item">
|
||||
<form action="{% url "control:event.order.sync_job" organizer=event.organizer.slug event=event.slug code=order.code provider=identifier %}" method="post" class="form-inline pull-right">
|
||||
{% csrf_token %}
|
||||
{% if pending %}
|
||||
{% if pending.not_before > now or pending.need_manual_retry %}
|
||||
<button type="submit" name="run_job_now" value="{{ pending.pk }}" class="btn btn-default"><i class="fa fa-refresh"></i> {% trans "Retry now" %}</button>
|
||||
{% endif %}
|
||||
<button type="submit" name="cancel_job" value="{{ pending.pk }}" class="btn btn-danger"><i class="fa fa-times"></i> {% trans "Cancel" %}</button>
|
||||
{% else %}
|
||||
<button type="submit" class="btn btn-default"><i class="fa fa-refresh"></i> {% trans "Sync now" %}</button>
|
||||
<input type="hidden" name="queue_sync" value="true">
|
||||
{% endif %}
|
||||
</form>
|
||||
<p><b>{{ display_name }}</b></p>
|
||||
{% if pending %}
|
||||
<p>
|
||||
{% if pending.need_manual_retry %}
|
||||
<i class="fa fa-warning"></i>
|
||||
{% trans "Error" %}: {{ pending.get_need_manual_retry_display }}
|
||||
{% elif pending.failed_attempts %}
|
||||
<i class="fa fa-warning"></i>
|
||||
{% blocktrans trimmed with num=pending.failed_attempts max=pending.max_retry_attempts %}
|
||||
Error. Retry {{ num }} of {{ max }}.
|
||||
{% endblocktrans %}
|
||||
{% if pending.not_before %}
|
||||
{% blocktrans trimmed with datetime=pending.not_before|date:"SHORT_DATETIME_FORMAT" %}
|
||||
Waiting until {{ datetime }}
|
||||
{% endblocktrans %}
|
||||
{% endif %}
|
||||
{% elif pending.not_before > now %}
|
||||
{% blocktrans trimmed with datetime=pending.not_before|date:"SHORT_DATETIME_FORMAT" %}
|
||||
Waiting until {{ datetime }}
|
||||
{% endblocktrans %}
|
||||
{% else %}
|
||||
<i class="fa fa-hourglass"></i> {% trans "Pending" %}
|
||||
{% endif %}
|
||||
<span class="text-muted">({% blocktrans trimmed with datetime=pending.triggered|date:"SHORT_DATETIME_FORMAT" %}triggered at {{ datetime }}
|
||||
{% endblocktrans %})</span>
|
||||
<!-- {{ pending.triggered_by }} / {{ pending.triggered }} -->
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<ul>
|
||||
{% for obj in objects %}
|
||||
<li>
|
||||
{% if obj.external_link_html %}
|
||||
{{ obj.external_link_html }}
|
||||
{% else %}
|
||||
{{ obj.external_object_type }}
|
||||
{% trans "identified by" %} {{ obj.external_id_field }}
|
||||
<em>{{ obj.id_value }}</em>
|
||||
{% endif %}
|
||||
<time class="text-muted" datetime="{{ obj.transmitted.isoformat }}">{{ obj.transmitted|date:"SHORT_DATETIME_FORMAT" }}</time>
|
||||
</li>
|
||||
{% empty %}
|
||||
<li>{% trans "No data transmitted." %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -0,0 +1,86 @@
|
||||
{% extends "pretixcontrol/event/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h2>{% trans "Sync problems" %}</h2>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
On this page, we provide a list of orders where data synchronisation to an external system has failed.
|
||||
You can start another attempt to sync them manually.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
{% if queue_items %}
|
||||
<label aria-label="{% trans "select all rows for batch-operation" %}"
|
||||
class="batch-select-label"><input type="checkbox" data-toggle-table/></label>
|
||||
{% endif %}
|
||||
</th>
|
||||
<th>{% trans "Order" %}</th>
|
||||
<th>{% trans "Sync provider" %}</th>
|
||||
<th>{% trans "Date" %}</th>
|
||||
<th>{% trans "Failure mode" %}</th>
|
||||
{% if staff_session %}
|
||||
<th>in_flight</th><th>retry</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in queue_items %}
|
||||
<tr>
|
||||
<td><input type="checkbox" name="idlist" value="{{ item.pk }}"></td>
|
||||
<td>
|
||||
{% if staff_session %}{{ item.order.event.organizer.slug }} -{% endif %}
|
||||
<a href="{% url "control:event.order" event=item.order.event.slug organizer=item.order.event.organizer.slug code=item.order.code %}">
|
||||
{{ item.order.full_code }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ item.provider_display_name }}</td>
|
||||
<td>
|
||||
{{ item.triggered }}
|
||||
{% if staff_session %}({{ item.triggered_by }}){% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if item.need_manual_retry %}
|
||||
{{ item.get_need_manual_retry_display }}
|
||||
{% else %}
|
||||
{% blocktrans trimmed with datetime=item.not_before|date:"SHORT_DATETIME_FORMAT" %}
|
||||
Temporary error, will retry after {{ datetime }}
|
||||
{% endblocktrans %}
|
||||
{% endif %}
|
||||
{% if staff_session %}({{ item.need_manual_retry }}){% endif %}
|
||||
</td>
|
||||
{% if staff_session %}
|
||||
<td>{{ item.in_flight }} ({{ item.in_flight_since }})</td><td>{{ item.failed_attempts }} / {{ item.max_retry_attempts }} ({{ item.not_before }})</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="5" class="text-center">{% trans "No problems." %}</td>
|
||||
{% if staff_session %}
|
||||
<td></td><td></td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
{% if queue_items %}
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
<button type="submit" name="action" value="retry" class="btn btn-primary"><i class="fa fa-refresh"></i> {% trans "Retry selected" %}</button>
|
||||
<button type="submit" name="action" value="cancel" class="btn btn-danger"><i class="fa fa-times"></i> {% trans "Cancel selected" %}</button>
|
||||
</td>
|
||||
{% if staff_session %}
|
||||
<td></td><td></td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</tfoot>
|
||||
{% endif %}
|
||||
</table>
|
||||
</form>
|
||||
{% include "pretixcontrol/pagination.html" %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,81 @@
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
{% load escapejson %}
|
||||
{% load formset_tags %}
|
||||
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}">
|
||||
{{ formset.management_form }}
|
||||
{% bootstrap_formset_errors formset %}
|
||||
<div data-formset-body>
|
||||
{% for f in formset %}
|
||||
{% bootstrap_form_errors f %}
|
||||
<div class="row formset-row" data-formset-form>
|
||||
<div class="sr-only">
|
||||
{{ f.id }}
|
||||
{% bootstrap_field f.DELETE form_group_class="" layout="inline" %}
|
||||
{% bootstrap_field f.ORDER form_group_class="" layout="inline" %}
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
{% bootstrap_field f.pretix_field layout='inline' form_group_class="" %}
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
{% bootstrap_field f.external_field layout='inline' form_group_class="" %}
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
{% bootstrap_field f.overwrite layout='inline' form_group_class="" %}
|
||||
</div>
|
||||
{{ f.value_map.as_hidden }}
|
||||
<div class="col-md-2 text-right flip">
|
||||
<i class="fa fa-warning hidden" data-toggle="tooltip" title=""></i>
|
||||
|
||||
<button type="button" class="btn btn-default hidden" data-edit-value-map data-toggle="modal"
|
||||
data-target="#editValueMapModal" title="{% trans "Edit value mapping" %}">
|
||||
<i class="fa fa-edit"></i></button>
|
||||
<button type="button" class="btn btn-default" data-formset-move-up-button>
|
||||
<i class="fa fa-arrow-up"></i></button>
|
||||
<button type="button" class="btn btn-default" data-formset-move-down-button>
|
||||
<i class="fa fa-arrow-down"></i></button>
|
||||
<button type="button" class="btn btn-danger" data-formset-delete-button>
|
||||
<i class="fa fa-trash"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<script type="form-template" data-formset-empty-form>
|
||||
{% escapescript %}
|
||||
<div class="row formset-row" data-formset-form>
|
||||
<div class="sr-only">
|
||||
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
{% bootstrap_field formset.empty_form.pretix_field layout='inline' form_group_class="" %}
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
{% bootstrap_field formset.empty_form.external_field layout='inline' form_group_class="" %}
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
{% bootstrap_field formset.empty_form.overwrite layout='inline' form_group_class="" %}
|
||||
</div>
|
||||
{{ f.value_map.as_hidden }}
|
||||
<div class="col-md-2 text-right flip">
|
||||
<i class="fa fa-warning hidden" data-toggle="tooltip" title=""></i>
|
||||
<button type="button" class="btn btn-default hidden" data-edit-value-map data-toggle="modal"
|
||||
data-target="#editValueMapModal" title="{% trans "Edit value mapping" %}">
|
||||
<i class="fa fa-edit"></i></button>
|
||||
<button type="button" class="btn btn-default" data-formset-move-up-button>
|
||||
<i class="fa fa-arrow-up"></i></button>
|
||||
<button type="button" class="btn btn-default" data-formset-move-down-button>
|
||||
<i class="fa fa-arrow-down"></i></button>
|
||||
<button type="button" class="btn btn-danger" data-formset-delete-button>
|
||||
<i class="fa fa-trash"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
{% endescapescript %}
|
||||
</script>
|
||||
<p>
|
||||
<button type="button" class="btn btn-default" data-formset-add>
|
||||
<i class="fa fa-plus"></i> {% trans "Add property" %}</button>
|
||||
</p>
|
||||
</div>
|
||||
{% if external_fields %}
|
||||
{{ external_fields|json_script:external_fields_id }}
|
||||
{% endif %}
|
||||
@@ -79,6 +79,15 @@
|
||||
class="btn btn-primary">{% trans "Show affected orders" %}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if has_sync_problems %}
|
||||
<div class="alert alert-warning">
|
||||
{% blocktrans trimmed %}
|
||||
Orders in this event could not be <strong>synced to an external system</strong> as configured.
|
||||
{% endblocktrans %}
|
||||
<a href="{% url "control:event.datasync.failedjobs" event=request.event.slug organizer=request.event.organizer.slug %}"
|
||||
class="btn btn-primary">{% trans "Show sync problems" %}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% eventsignal request.event "pretix.control.signals.event_dashboard_top" request=request %}
|
||||
|
||||
{% if request.event.has_subevents %}
|
||||
|
||||
Reference in New Issue
Block a user