mirror of
https://github.com/pretix/pretix.git
synced 2026-05-04 15:04:03 +00:00
Organizer-level bank import
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.2 on 2017-06-19 11:25
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0062_auto_20170602_0948'),
|
||||
('banktransfer', '0003_banktransaction_comment'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='bankimportjob',
|
||||
name='organizer',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='pretixbase.Organizer'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='banktransaction',
|
||||
name='organizer',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='pretixbase.Organizer'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='bankimportjob',
|
||||
name='event',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='pretixbase.Event'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='banktransaction',
|
||||
name='event',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='pretixbase.Event'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='banktransaction',
|
||||
unique_together=set([('event', 'organizer', 'checksum')]),
|
||||
),
|
||||
]
|
||||
@@ -16,10 +16,18 @@ class BankImportJob(models.Model):
|
||||
(STATE_COMPLETED, 'completed'),
|
||||
)
|
||||
|
||||
event = models.ForeignKey('pretixbase.Event')
|
||||
event = models.ForeignKey('pretixbase.Event', null=True)
|
||||
organizer = models.ForeignKey('pretixbase.Organizer', null=True)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
state = models.CharField(max_length=32, choices=STATES, default=STATE_PENDING)
|
||||
|
||||
@property
|
||||
def owner_kwargs(self):
|
||||
if self.event:
|
||||
return {'event': self.event}
|
||||
else:
|
||||
return {'organizer': self.organizer}
|
||||
|
||||
|
||||
class BankTransaction(models.Model):
|
||||
STATE_UNCHECKED = 'imported'
|
||||
@@ -40,7 +48,8 @@ class BankTransaction(models.Model):
|
||||
(STATE_DISCARDED, 'manually discarded'),
|
||||
)
|
||||
|
||||
event = models.ForeignKey('pretixbase.Event')
|
||||
event = models.ForeignKey('pretixbase.Event', null=True)
|
||||
organizer = models.ForeignKey('pretixbase.Organizer', null=True)
|
||||
import_job = models.ForeignKey('BankImportJob', related_name='transactions')
|
||||
state = models.CharField(max_length=32, choices=STATES, default=STATE_UNCHECKED)
|
||||
message = models.TextField()
|
||||
@@ -66,4 +75,4 @@ class BankTransaction(models.Model):
|
||||
self.reference = ""
|
||||
|
||||
class Meta:
|
||||
unique_together = ('event', 'checksum')
|
||||
unique_together = ('event', 'organizer', 'checksum')
|
||||
|
||||
@@ -4,7 +4,7 @@ from django.template.loader import get_template
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from pretix.base.signals import register_payment_providers
|
||||
from pretix.control.signals import html_head, nav_event
|
||||
from pretix.control.signals import html_head, nav_event, nav_organizer
|
||||
|
||||
from .payment import BankTransfer
|
||||
|
||||
@@ -32,6 +32,25 @@ def control_nav_import(sender, request=None, **kwargs):
|
||||
]
|
||||
|
||||
|
||||
@receiver(nav_organizer, dispatch_uid="payment_banktransfer_organav")
|
||||
def control_nav_orga_import(sender, request=None, **kwargs):
|
||||
url = resolve(request.path_info)
|
||||
if not request.user.has_organizer_permission(request.organizer, 'can_change_orders'):
|
||||
return []
|
||||
if not request.organizer.events.filter(plugins__icontains='pretix.plugins.banktransfer'):
|
||||
return []
|
||||
return [
|
||||
{
|
||||
'label': _('Import bank data'),
|
||||
'url': reverse('plugins:banktransfer:import', kwargs={
|
||||
'organizer': request.organizer.slug,
|
||||
}),
|
||||
'active': (url.namespace == 'plugins:banktransfer' and url.url_name == 'import'),
|
||||
'icon': 'upload',
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@receiver(html_head, dispatch_uid="banktransfer_html_head")
|
||||
def html_head_presale(sender, request=None, **kwargs):
|
||||
url = resolve(request.path_info)
|
||||
|
||||
@@ -6,10 +6,11 @@ from decimal import Decimal
|
||||
from celery.exceptions import MaxRetriesExceededError
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from django.db.models import Q
|
||||
from django.utils.translation import ugettext_noop
|
||||
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import Event, Order, Quota
|
||||
from pretix.base.models import Event, Order, Organizer, Quota
|
||||
from pretix.base.services.async import TransactionAwareTask
|
||||
from pretix.base.services.locking import LockTimeoutException
|
||||
from pretix.base.services.mail import SendMailException
|
||||
@@ -21,17 +22,33 @@ from .models import BankImportJob, BankTransaction
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _handle_transaction(event: Event, trans: BankTransaction, code: str):
|
||||
try:
|
||||
trans.order = event.orders.get(code=code)
|
||||
except Order.DoesNotExist:
|
||||
normalized_code = Order.normalize_code(code)
|
||||
def _handle_transaction(trans: BankTransaction, code: str, event: Event=None, organizer: Organizer=None,
|
||||
slug: str=None):
|
||||
if event:
|
||||
try:
|
||||
trans.order = event.orders.get(code=normalized_code)
|
||||
trans.order = event.orders.get(code=code)
|
||||
except Order.DoesNotExist:
|
||||
trans.state = BankTransaction.STATE_NOMATCH
|
||||
trans.save()
|
||||
return
|
||||
normalized_code = Order.normalize_code(code)
|
||||
try:
|
||||
trans.order = event.orders.get(code=normalized_code)
|
||||
except Order.DoesNotExist:
|
||||
trans.state = BankTransaction.STATE_NOMATCH
|
||||
trans.save()
|
||||
return
|
||||
else:
|
||||
qs = Order.objects.filter(event__organizer=organizer)
|
||||
if slug:
|
||||
qs = qs.filter(event__slug__iexact=slug)
|
||||
try:
|
||||
trans.order = qs.get(code=code)
|
||||
except Order.DoesNotExist:
|
||||
normalized_code = Order.normalize_code(code)
|
||||
try:
|
||||
trans.order = qs.get(code=normalized_code)
|
||||
except Order.DoesNotExist:
|
||||
trans.state = BankTransaction.STATE_NOMATCH
|
||||
trans.save()
|
||||
return
|
||||
|
||||
if trans.order.status == Order.STATUS_PAID:
|
||||
trans.state = BankTransaction.STATE_DUPLICATE
|
||||
@@ -63,9 +80,11 @@ def _handle_transaction(event: Event, trans: BankTransaction, code: str):
|
||||
trans.save()
|
||||
|
||||
|
||||
def _get_unknown_transactions(event: Event, job: BankImportJob, data: list):
|
||||
def _get_unknown_transactions(job: BankImportJob, data: list, event: Event=None, organizer: Organizer=None):
|
||||
amount_pattern = re.compile("[^0-9.-]")
|
||||
known_checksums = set(t['checksum'] for t in BankTransaction.objects.filter(event=event).values('checksum'))
|
||||
known_checksums = set(t['checksum'] for t in BankTransaction.objects.filter(
|
||||
Q(event=event) if event else Q(organizer=organizer)
|
||||
).values('checksum'))
|
||||
|
||||
transactions = []
|
||||
for row in data:
|
||||
@@ -83,7 +102,7 @@ def _get_unknown_transactions(event: Event, job: BankImportJob, data: list):
|
||||
logger.exception('Could not parse amount of transaction: {}'.format(amount))
|
||||
amount = Decimal("0.00")
|
||||
|
||||
trans = BankTransaction(event=event, import_job=job,
|
||||
trans = BankTransaction(event=event, organizer=organizer, import_job=job,
|
||||
payer=row.get('payer', ''),
|
||||
reference=row['reference'],
|
||||
amount=amount,
|
||||
@@ -99,29 +118,41 @@ def _get_unknown_transactions(event: Event, job: BankImportJob, data: list):
|
||||
|
||||
|
||||
@app.task(base=TransactionAwareTask, bind=True, max_retries=5, default_retry_delay=1)
|
||||
def process_banktransfers(self, event: int, job: int, data: list) -> None:
|
||||
def process_banktransfers(self, job: int, data: list) -> None:
|
||||
with language("en"): # We'll translate error messages at display time
|
||||
event = Event.objects.get(pk=event)
|
||||
job = BankImportJob.objects.get(pk=job)
|
||||
job.state = BankImportJob.STATE_RUNNING
|
||||
job.save()
|
||||
prefixes = []
|
||||
|
||||
try:
|
||||
# Delete left-over transactions from a failed run before so they can reimported
|
||||
BankTransaction.objects.filter(event=event, state=BankTransaction.STATE_UNCHECKED).delete()
|
||||
BankTransaction.objects.filter(state=BankTransaction.STATE_UNCHECKED, **job.owner_kwargs).delete()
|
||||
|
||||
transactions = _get_unknown_transactions(event, job, data)
|
||||
transactions = _get_unknown_transactions(job, data, **job.owner_kwargs)
|
||||
|
||||
code_len = settings.ENTROPY['order_code']
|
||||
pattern = re.compile(event.slug.upper() + "[ \-_]*([A-Z0-9]{%s})" % code_len)
|
||||
if job.event:
|
||||
pattern = re.compile(job.event.slug.upper() + "[ \-_]*([A-Z0-9]{%s})" % code_len)
|
||||
else:
|
||||
if not prefixes:
|
||||
prefixes = [e.slug.upper().replace(".", r"\.").replace("-", r"\-")
|
||||
for e in job.organizer.events.all()]
|
||||
pattern = re.compile("(%s)[ \-_]*([A-Z0-9]{%s})" % ("|".join(prefixes), code_len))
|
||||
|
||||
for trans in transactions:
|
||||
match = pattern.search(trans.reference.upper())
|
||||
|
||||
if match:
|
||||
code = match.group(1)
|
||||
with transaction.atomic():
|
||||
_handle_transaction(event, trans, code)
|
||||
if job.event:
|
||||
code = match.group(1)
|
||||
with transaction.atomic():
|
||||
_handle_transaction(trans, code, event=job.event)
|
||||
else:
|
||||
slug = match.group(1)
|
||||
code = match.group(2)
|
||||
with transaction.atomic():
|
||||
_handle_transaction(trans, code, organizer=job.organizer, slug=slug)
|
||||
else:
|
||||
trans.state = BankTransaction.STATE_NOMATCH
|
||||
trans.save()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "pretixplugins/banktransfer/import_base.html" %}
|
||||
{% extends basetpl %}
|
||||
{% load i18n %}
|
||||
{% block inner %}
|
||||
<p>{% blocktrans trimmed %}
|
||||
|
||||
@@ -6,5 +6,4 @@
|
||||
<h1>{% trans "Import bank data" %}</h1>
|
||||
{% block inner %}
|
||||
{% endblock %}
|
||||
<script type="application/javascript" src="{% static "pretixplugins/banktransfer/ui.js" %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{% extends "pretixcontrol/organizers/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% block title %}{% trans "Import bank data" %}{% endblock %}
|
||||
@@ -1,6 +1,7 @@
|
||||
{% extends "pretixplugins/banktransfer/import_base.html" %}
|
||||
{% extends basetpl %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
{% load static %}
|
||||
{% block inner %}
|
||||
{% if no_more_payments %}
|
||||
<div class="alert alert-danger">
|
||||
@@ -47,6 +48,16 @@
|
||||
<h3 class="panel-title">{% trans "Unresolved transactions" %}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{% if request.event %}
|
||||
<p>
|
||||
{% blocktrans %}
|
||||
On this page, you can import banking data on a per-event level. You also only see
|
||||
unmatched transactions imported directly for this event.
|
||||
{% endblocktrans %}
|
||||
<a href="{% url "plugins:banktransfer:import" organizer=request.organizer.slug %}"
|
||||
class="btn btn-default btn-xs">{% trans "Go to organizer-level import" %}</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
<p>
|
||||
<form class="form-inline helper-display-inline" action="" method="get">
|
||||
<input type="text" name="search" class="form-control" placeholder="{% trans "Search" %}"
|
||||
@@ -74,4 +85,5 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<script type="application/javascript" src="{% static "pretixplugins/banktransfer/ui.js" %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{% extends "pretixplugins/banktransfer/import_base.html" %}
|
||||
{% extends basetpl %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% block inner %}
|
||||
<h2>{% trans "Import result" %}</h2>
|
||||
{% if job.state == "running" or job.state == "pending" %}
|
||||
@@ -37,11 +38,12 @@
|
||||
</tr>
|
||||
</table>
|
||||
{% if transactions_ignored or transactions_invalid %}
|
||||
<a href="{% url "plugins:banktransfer:import" event=request.event.slug organizer=request.organizer.slug %}"
|
||||
<a href="{% if request.event %}{% url "plugins:banktransfer:import" event=request.event.slug organizer=request.organizer.slug %}{% else %}{% url "plugins:banktransfer:import" organizer=request.organizer.slug %}{% endif %}"
|
||||
class="btn btn-primary">
|
||||
{% trans "Review invalid and ignored payments" %} »
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
<script type="application/javascript" src="{% static "pretixplugins/banktransfer/ui.js" %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{% load staticfiles %}
|
||||
<div class="table-responsive">
|
||||
{% csrf_token %}
|
||||
<table class="table table-condensed transaction-list" data-url="{% url "plugins:banktransfer:import.action" event=request.event.slug organizer=request.event.organizer.slug %}">
|
||||
<table class="table table-condensed transaction-list" data-url="{% if request.event %}{% url "plugins:banktransfer:import.action" event=request.event.slug organizer=request.organizer.slug %}{% else %}{% url "plugins:banktransfer:import.action" organizer=request.organizer.slug %}{% endif %}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
@@ -83,9 +83,13 @@
|
||||
</td>
|
||||
<td>
|
||||
{% if trans.order %}
|
||||
<a href="{% url "control:event.order" event=request.event.slug organizer=request.event.organizer.slug code=trans.order.code %}"
|
||||
data-toggle="tooltip" title="{{ trans.order.total|floatformat:2 }} {{ request.event.currency }}">
|
||||
{{ trans.order.code }}
|
||||
<a href="{% url "control:event.order" event=trans.order.event.slug organizer=request.organizer.slug code=trans.order.code %}"
|
||||
data-toggle="tooltip" title="{{ trans.order.total|floatformat:2 }} {{ trans.order.event.currency }}">
|
||||
{% if not request.event %}
|
||||
{{ trans.order.event.slug|upper }}-{{ trans.order.code }}
|
||||
{% else %}
|
||||
{{ trans.order.code }}
|
||||
{% endif %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
@@ -3,10 +3,19 @@ from django.conf.urls import url
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/banktransfer/import/', views.ImportView.as_view(),
|
||||
url(r'^control/organizer/(?P<organizer>[^/]+)/banktransfer/import/',
|
||||
views.OrganizerImportView.as_view(),
|
||||
name='import'),
|
||||
url(r'^control/organizer/(?P<organizer>[^/]+)/banktransfer/job/(?P<job>\d+)/',
|
||||
views.OrganizerJobDetailView.as_view(), name='import.job'),
|
||||
url(r'^control/organizer/(?P<organizer>[^/]+)/banktransfer/action/',
|
||||
views.OrganizerActionView.as_view(), name='import.action'),
|
||||
|
||||
url(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/banktransfer/import/',
|
||||
views.EventImportView.as_view(),
|
||||
name='import'),
|
||||
url(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/banktransfer/job/(?P<job>\d+)/',
|
||||
views.JobDetailView.as_view(), name='import.job'),
|
||||
views.EventJobDetailView.as_view(), name='import.job'),
|
||||
url(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/banktransfer/action/',
|
||||
views.ActionView.as_view(), name='import.action'),
|
||||
views.EventActionView.as_view(), name='import.action'),
|
||||
]
|
||||
|
||||
@@ -17,7 +17,10 @@ from pretix.base.models import Order, Quota
|
||||
from pretix.base.services.mail import SendMailException
|
||||
from pretix.base.services.orders import mark_order_paid
|
||||
from pretix.base.settings import SettingsSandbox
|
||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||
from pretix.control.permissions import (
|
||||
EventPermissionRequiredMixin, OrganizerPermissionRequiredMixin,
|
||||
)
|
||||
from pretix.control.views.organizer import OrganizerDetailViewMixin
|
||||
from pretix.plugins.banktransfer import csvimport, mt940import
|
||||
from pretix.plugins.banktransfer.models import BankImportJob, BankTransaction
|
||||
from pretix.plugins.banktransfer.tasks import process_banktransfers
|
||||
@@ -25,7 +28,7 @@ from pretix.plugins.banktransfer.tasks import process_banktransfers
|
||||
logger = logging.getLogger('pretix.plugins.banktransfer')
|
||||
|
||||
|
||||
class ActionView(EventPermissionRequiredMixin, View):
|
||||
class ActionView(View):
|
||||
permission = 'can_change_orders'
|
||||
|
||||
def _discard(self, trans):
|
||||
@@ -89,7 +92,10 @@ class ActionView(EventPermissionRequiredMixin, View):
|
||||
|
||||
def _assign(self, trans, code):
|
||||
try:
|
||||
trans.order = self.request.event.orders.get(code=code)
|
||||
if '-' in code:
|
||||
trans.order = self.order_qs().get(code=code.split('-')[1], event__slug__iexact=code.split('-')[0])
|
||||
else:
|
||||
trans.order = self.order_qs().get(code=code.split('-')[-1])
|
||||
except Order.DoesNotExist:
|
||||
return JsonResponse({
|
||||
'status': 'error',
|
||||
@@ -109,7 +115,10 @@ class ActionView(EventPermissionRequiredMixin, View):
|
||||
for k, v in request.POST.items():
|
||||
if not k.startswith('action_'):
|
||||
continue
|
||||
trans = get_object_or_404(BankTransaction, id=k.split('_')[1], event=self.request.event)
|
||||
if 'event' in kwargs:
|
||||
trans = get_object_or_404(BankTransaction, id=k.split('_')[1], event=request.event)
|
||||
else:
|
||||
trans = get_object_or_404(BankTransaction, id=k.split('_')[1], organizer=request.organizer)
|
||||
|
||||
if v == 'discard' and trans.state in (BankTransaction.STATE_INVALID, BankTransaction.STATE_ERROR,
|
||||
BankTransaction.STATE_NOMATCH, BankTransaction.STATE_DUPLICATE):
|
||||
@@ -140,52 +149,64 @@ class ActionView(EventPermissionRequiredMixin, View):
|
||||
if len(query) < 2:
|
||||
return JsonResponse({'results': []})
|
||||
|
||||
qs = self.request.event.orders.filter(Q(code__icontains=query) | Q(code__icontains=Order.normalize_code(query)))
|
||||
qs = self.order_qs().filter(Q(code__icontains=query) | Q(code__icontains=Order.normalize_code(query))).select_related('event')
|
||||
return JsonResponse({
|
||||
'results': [
|
||||
{
|
||||
'code': o.code,
|
||||
'code': o.event.slug.upper() + '-' + o.code,
|
||||
'status': o.get_status_display(),
|
||||
'total': localize(o.total) + ' ' + self.request.event.currency
|
||||
'total': localize(o.total) + ' ' + o.event.currency
|
||||
} for o in qs
|
||||
]
|
||||
})
|
||||
|
||||
def order_qs(self):
|
||||
return self.request.event.orders
|
||||
|
||||
class JobDetailView(EventPermissionRequiredMixin, DetailView):
|
||||
|
||||
class JobDetailView(DetailView):
|
||||
template_name = 'pretixplugins/banktransfer/job_detail.html'
|
||||
permission = 'can_change_orders'
|
||||
context_objectname = 'job'
|
||||
|
||||
def redirect_form(self):
|
||||
return redirect(reverse('plugins:banktransfer:import', kwargs={
|
||||
'event': self.request.event.slug,
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
}))
|
||||
kwargs = {
|
||||
'organizer': self.request.organizer.slug,
|
||||
}
|
||||
if 'event' in self.kwargs:
|
||||
kwargs['event'] = self.kwargs['event']
|
||||
return redirect(reverse('plugins:banktransfer:import', kwargs=kwargs))
|
||||
|
||||
def redirect_back(self):
|
||||
return redirect(reverse('plugins:banktransfer:import.job', kwargs={
|
||||
'event': self.request.event.slug,
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
kwargs = {
|
||||
'organizer': self.request.organizer.slug,
|
||||
'job': self.kwargs['job']
|
||||
}))
|
||||
}
|
||||
if 'event' in self.kwargs:
|
||||
kwargs['event'] = self.kwargs['event']
|
||||
return redirect(reverse('plugins:banktransfer:import.job', kwargs=kwargs))
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return get_object_or_404(BankImportJob, id=self.kwargs['job'], event=self.request.event)
|
||||
@cached_property
|
||||
def job(self):
|
||||
if 'event' in self.kwargs:
|
||||
kwargs = {'event': self.request.event}
|
||||
else:
|
||||
kwargs = {'organizer': self.request.organizer}
|
||||
return get_object_or_404(BankImportJob, id=self.kwargs['job'], **kwargs)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if 'ajax' in request.GET:
|
||||
self.object = self.get_object()
|
||||
return JsonResponse({
|
||||
'state': self.object.state
|
||||
'state': self.job.state
|
||||
})
|
||||
|
||||
return super().get(request, *args, **kwargs)
|
||||
context = self.get_context_data()
|
||||
return self.render_to_response(context)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data()
|
||||
ctx = {}
|
||||
|
||||
qs = self.object.transactions.select_related('order')
|
||||
qs = self.job.transactions.select_related('order', 'order__event')
|
||||
|
||||
ctx['transactions_valid'] = qs.filter(state=BankTransaction.STATE_VALID).count()
|
||||
ctx['transactions_invalid'] = qs.filter(state__in=[
|
||||
@@ -194,21 +215,33 @@ class JobDetailView(EventPermissionRequiredMixin, DetailView):
|
||||
ctx['transactions_ignored'] = qs.filter(state__in=[
|
||||
BankTransaction.STATE_DUPLICATE, BankTransaction.STATE_NOMATCH
|
||||
]).count()
|
||||
ctx['job'] = self.object
|
||||
ctx['job'] = self.job
|
||||
ctx['organizer'] = self.request.organizer
|
||||
|
||||
if 'event' in self.kwargs:
|
||||
ctx['basetpl'] = 'pretixplugins/banktransfer/import_base.html'
|
||||
else:
|
||||
ctx['basetpl'] = 'pretixplugins/banktransfer/import_base_organizer.html'
|
||||
|
||||
return ctx
|
||||
|
||||
|
||||
class ImportView(EventPermissionRequiredMixin, ListView):
|
||||
class ImportView(ListView):
|
||||
template_name = 'pretixplugins/banktransfer/import_form.html'
|
||||
permission = 'can_change_orders'
|
||||
context_object_name = 'transactions_unhandled'
|
||||
paginate_by = 30
|
||||
|
||||
def get_queryset(self):
|
||||
qs = BankTransaction.objects.filter(
|
||||
event=self.request.event
|
||||
).select_related('order').filter(state__in=[
|
||||
if 'event' in self.kwargs:
|
||||
qs = BankTransaction.objects.filter(
|
||||
Q(event=self.request.event)
|
||||
)
|
||||
else:
|
||||
qs = BankTransaction.objects.filter(
|
||||
Q(organizer=self.request.organizer)
|
||||
)
|
||||
qs = qs.select_related('order').filter(state__in=[
|
||||
BankTransaction.STATE_INVALID, BankTransaction.STATE_ERROR,
|
||||
BankTransaction.STATE_DUPLICATE, BankTransaction.STATE_NOMATCH
|
||||
])
|
||||
@@ -245,12 +278,12 @@ class ImportView(EventPermissionRequiredMixin, ListView):
|
||||
|
||||
else:
|
||||
messages.error(self.request, _('We were unable to detect the file type of this import. Please '
|
||||
'contact support for help.'))
|
||||
'contact support for help.'))
|
||||
return self.redirect_back()
|
||||
|
||||
@cached_property
|
||||
def settings(self):
|
||||
return SettingsSandbox('payment', 'banktransfer', self.request.event)
|
||||
return SettingsSandbox('payment', 'banktransfer', getattr(self.request, 'event', self.request.organizer))
|
||||
|
||||
def process_mt940(self):
|
||||
try:
|
||||
@@ -261,6 +294,7 @@ class ImportView(EventPermissionRequiredMixin, ListView):
|
||||
return self.redirect_back()
|
||||
|
||||
def process_csv_file(self):
|
||||
o = getattr(self.request, 'event', self.request.organizer)
|
||||
try:
|
||||
data = csvimport.get_rows_from_file(self.request.FILES['file'])
|
||||
except csv.Error as e: # TODO: narrow down
|
||||
@@ -273,8 +307,8 @@ class ImportView(EventPermissionRequiredMixin, ListView):
|
||||
messages.error(self.request, _('I\'m sorry, but we detected this file as empty. Please '
|
||||
'contact support for help.'))
|
||||
|
||||
if self.request.event.settings.get('banktransfer_csvhint') is not None:
|
||||
hint = self.request.event.settings.get('banktransfer_csvhint', as_type=dict)
|
||||
if o.settings.get('banktransfer_csvhint') is not None:
|
||||
hint = o.settings.get('banktransfer_csvhint', as_type=dict)
|
||||
|
||||
try:
|
||||
parsed, good = csvimport.parse(data, hint)
|
||||
@@ -304,8 +338,9 @@ class ImportView(EventPermissionRequiredMixin, ListView):
|
||||
logger.error('Parsing hint failed: ' + str(e))
|
||||
messages.error(self.request, _('We were unable to process your input.'))
|
||||
return self.assign_view(data)
|
||||
o = getattr(self.request, 'event', self.request.organizer)
|
||||
try:
|
||||
self.request.event.settings.set('banktransfer_csvhint', hint)
|
||||
o.settings.set('banktransfer_csvhint', hint)
|
||||
except Exception as e: # TODO: narrow down
|
||||
logger.error('Import using stored hint failed: ' + str(e))
|
||||
pass
|
||||
@@ -321,44 +356,117 @@ class ImportView(EventPermissionRequiredMixin, ListView):
|
||||
return super().get(self.request)
|
||||
|
||||
def assign_view(self, parsed):
|
||||
return render(self.request, 'pretixplugins/banktransfer/import_assign.html', {
|
||||
'rows': parsed
|
||||
})
|
||||
ctx = {'rows': parsed}
|
||||
if 'event' in self.kwargs:
|
||||
ctx['basetpl'] = 'pretixplugins/banktransfer/import_base.html'
|
||||
else:
|
||||
ctx['basetpl'] = 'pretixplugins/banktransfer/import_base_organizer.html'
|
||||
ctx['organizer'] = self.request.organizer
|
||||
return render(self.request, 'pretixplugins/banktransfer/import_assign.html', ctx)
|
||||
|
||||
@cached_property
|
||||
def job_running(self):
|
||||
return BankImportJob.objects.filter(
|
||||
event=self.request.event, state=BankImportJob.STATE_RUNNING,
|
||||
if 'event' in self.kwargs:
|
||||
qs = BankImportJob.objects.filter(
|
||||
Q(event=self.request.event) | Q(organizer=self.request.organizer)
|
||||
)
|
||||
else:
|
||||
qs = BankImportJob.objects.filter(
|
||||
Q(organizer=self.request.organizer)
|
||||
)
|
||||
return qs.filter(
|
||||
state=BankImportJob.STATE_RUNNING,
|
||||
created__lte=now() - timedelta(minutes=30) # safety timeout
|
||||
).first()
|
||||
|
||||
def redirect_back(self):
|
||||
return redirect(reverse('plugins:banktransfer:import', kwargs={
|
||||
'event': self.request.event.slug,
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
}))
|
||||
kwargs = {
|
||||
'organizer': self.request.organizer.slug
|
||||
}
|
||||
if 'event' in self.kwargs:
|
||||
kwargs['event'] = self.kwargs['event']
|
||||
return redirect(reverse('plugins:banktransfer:import', kwargs=kwargs))
|
||||
|
||||
def start_processing(self, parsed):
|
||||
if self.job_running:
|
||||
messages.error(self.request, _('An import is currently being processed, please try again in a few minutes.'))
|
||||
messages.error(self.request,
|
||||
_('An import is currently being processed, please try again in a few minutes.'))
|
||||
return self.redirect_back()
|
||||
job = BankImportJob.objects.create(event=self.request.event)
|
||||
if 'event' in self.kwargs:
|
||||
job = BankImportJob.objects.create(event=self.request.event, organizer=self.request.organizer)
|
||||
else:
|
||||
job = BankImportJob.objects.create(organizer=self.request.organizer)
|
||||
process_banktransfers.apply_async(kwargs={
|
||||
'event': self.request.event.pk,
|
||||
'job': job.pk,
|
||||
'data': parsed
|
||||
})
|
||||
return redirect(reverse('plugins:banktransfer:import.job', kwargs={
|
||||
'event': self.request.event.slug,
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
kwargs = {
|
||||
'organizer': self.request.organizer.slug,
|
||||
'job': job.pk
|
||||
}))
|
||||
}
|
||||
if 'event' in self.kwargs:
|
||||
kwargs['event'] = self.kwargs['event']
|
||||
return redirect(reverse('plugins:banktransfer:import.job', kwargs=kwargs))
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data()
|
||||
ctx['job_running'] = self.job_running
|
||||
ctx['no_more_payments'] = False
|
||||
if self.request.event.settings.get('payment_term_last'):
|
||||
if now() > self.request.event.payment_term_last:
|
||||
ctx['no_more_payments'] = True
|
||||
|
||||
if 'event' in self.kwargs:
|
||||
ctx['basetpl'] = 'pretixplugins/banktransfer/import_base.html'
|
||||
if self.request.event.settings.get('payment_term_last'):
|
||||
if now() > self.request.event.payment_term_last:
|
||||
ctx['no_more_payments'] = True
|
||||
else:
|
||||
ctx['basetpl'] = 'pretixplugins/banktransfer/import_base_organizer.html'
|
||||
ctx['organizer'] = self.request.organizer
|
||||
return ctx
|
||||
|
||||
|
||||
class OrganizerBanktransferView:
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if len(request.organizer.events.order_by('currency').values_list('currency', flat=True).distinct()) > 1:
|
||||
messages.error(request, _('Please perform per-event bank imports as this organizer has events with '
|
||||
'multuple currencies.'))
|
||||
return redirect('control:organizer', organizer=request.organizer.slug)
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
class EventImportView(EventPermissionRequiredMixin, ImportView):
|
||||
permission = 'can_change_orders'
|
||||
|
||||
|
||||
class OrganizerImportView(OrganizerBanktransferView, OrganizerPermissionRequiredMixin, OrganizerDetailViewMixin,
|
||||
ImportView):
|
||||
permission = 'can_change_orders'
|
||||
|
||||
|
||||
class EventJobDetailView(EventPermissionRequiredMixin, JobDetailView):
|
||||
permission = 'can_change_orders'
|
||||
|
||||
|
||||
class OrganizerJobDetailView(OrganizerBanktransferView, OrganizerPermissionRequiredMixin, OrganizerDetailViewMixin,
|
||||
JobDetailView):
|
||||
permission = 'can_change_orders'
|
||||
|
||||
|
||||
class EventActionView(EventPermissionRequiredMixin, ActionView):
|
||||
permission = 'can_change_orders'
|
||||
|
||||
|
||||
class OrganizerActionView(OrganizerBanktransferView, OrganizerPermissionRequiredMixin, OrganizerDetailViewMixin,
|
||||
ActionView):
|
||||
permission = 'can_change_orders'
|
||||
|
||||
def order_qs(self):
|
||||
all = self.request.user.teams.filter(organizer=self.request.organizer, can_change_orders=True,
|
||||
can_view_orders=True, all_events=True).exists()
|
||||
if self.request.user.is_superuser or all:
|
||||
return Order.objects.filter(event__organizer=self.request.organizer)
|
||||
else:
|
||||
return Order.objects.filter(
|
||||
event_id__in=self.request.user.teams.filter(
|
||||
organizer=self.request.organizer, can_change_orders=True, can_view_orders=True
|
||||
).values_list('limit_events__id', flat=True)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user