diff --git a/src/pretix/control/context.py b/src/pretix/control/context.py index 5fcab98c16..c34a9d6791 100644 --- a/src/pretix/control/context.py +++ b/src/pretix/control/context.py @@ -8,7 +8,7 @@ def contextprocessor(request): Adds data to all template contexts """ url = resolve(request.path_info) - if url.namespace != 'control': + if not request.path.startswith('/control'): return {} ctx = { 'url_name': url.url_name, diff --git a/src/pretix/control/middleware.py b/src/pretix/control/middleware.py index e728a7ec4c..512ba5a6f4 100644 --- a/src/pretix/control/middleware.py +++ b/src/pretix/control/middleware.py @@ -24,9 +24,8 @@ class PermissionMiddleware: def process_request(self, request): url = resolve(request.path_info) - url_namespace = url.namespace url_name = url.url_name - if url_namespace != 'control' or url_name in self.EXCEPTIONS: + if not request.path.startswith('/control') or url_name in self.EXCEPTIONS: return if not request.user.is_authenticated(): # Taken from django/contrib/auth/decorators.py @@ -47,7 +46,7 @@ class PermissionMiddleware: request.user.events_cache = request.user.events.current.order_by( "organizer", "date_from").prefetch_related("organizer") - if 'event.' in url_name and 'event' in url.kwargs: + if 'event' in url.kwargs and 'organizer' in url.kwargs: try: request.event = Event.objects.current.filter( slug=url.kwargs['event'], diff --git a/src/pretix/control/static/pretixcontrol/less/main.less b/src/pretix/control/static/pretixcontrol/less/main.less index 4a8d618bd7..1fea2841fa 100644 --- a/src/pretix/control/static/pretixcontrol/less/main.less +++ b/src/pretix/control/static/pretixcontrol/less/main.less @@ -53,4 +53,15 @@ nav.navbar { } .container-fluid > .alert:first-child { margin-top: 20px; +} + +.flipped-scroll-wrapper { + overflow-y: auto; +} +.flipped-scroll-wrapper, .flipped-scroll-inner { + /* This nasty hack puts the scroll bar at the top, so the user really + notices that there is one */ + transform: rotateX(180deg); + -ms-transform: rotateX(180deg); /* IE 9 */ + -webkit-transform: rotateX(180deg); /* Safari and Chrome */ } \ No newline at end of file diff --git a/src/pretix/plugins/banktransfer/csvimport.py b/src/pretix/plugins/banktransfer/csvimport.py new file mode 100644 index 0000000000..50794b3cb4 --- /dev/null +++ b/src/pretix/plugins/banktransfer/csvimport.py @@ -0,0 +1,85 @@ +import csv +import io + + +class HintMismatchError(Exception): + pass + + +def parse(data, hint): + result = [] + if 'cols' not in hint: + raise HintMismatchError('Invalid hint') + if len(data[0]) != hint['cols']: + raise HintMismatchError('Wrong column count') + for row in data: + resrow = {} + if None in row or len(row) == 0: + # Wrong column count + continue + if hint.get('payer') is not None: + resrow['payer'] = "\n".join([row[int(i)].strip() for i in hint.get('payer')]) + if hint.get('reference') is not None: + resrow['reference'] = "\n".join([row[int(i)].strip() for i in hint.get('reference')]) + if hint.get('amount') is not None: + resrow['amount'] = row[int(hint.get('amount'))].strip() + if hint.get('date') is not None: + resrow['date'] = row[int(hint.get('date'))].strip() + if len(resrow['amount']) == 0 or 'amount' not in resrow \ + or resrow['amount'][0] not in list("1234567890," "+- ") \ + or len(resrow['reference']) == 0: + # This is probably a headline or something other special. + continue + result.append(resrow) + return result + + +def get_rows_from_file(file): + data = file.read() + try: + import chardet + charset = chardet.detect(data)['encoding'] + except ImportError: + charset = file.charset + data = data.decode(charset or 'utf-8') + # Sniffing line by line is necessary as some banks like to include + # one-column garbage at the beginning of the file which breaks the sniffer. + # See also: http://bugs.python.org/issue2078 + last_e = None + dialect = None + for line in data.split("\n"): + line = line.strip() + if len(line) == 0: + continue + try: + dialect = csv.Sniffer().sniff(line, delimiters=";,.#:") + except Exception as e: + last_e = e + else: + last_e = None + break + if dialect is None: + raise last_e + reader = csv.reader(io.StringIO(data), dialect) + rows = [] + for row in reader: + if rows and len(row) > len(rows[0]): + # Some banks put metadata above the real data, things like + # a headline, the bank's name, the user's name, etc. + # In many cases, we can identify this because these rows + # have less columns than the rows containing the real data. + # Therefore, if the number of columns suddenly grows, we start + # over with parsing. + rows = [] + rows.append(row) + return rows + + +def new_hint(data): + return { + 'payer': data.getlist('payer') if 'payer' in data else None, + 'reference': data.getlist('reference') if 'date' in data else None, + 'date': int(data.get('date')) if 'date' in data else None, + 'amount': int(data.get('amount')) if 'amount' in data else None, + 'cols': int(data.get('cols')) if 'cols' in data else None + } diff --git a/src/pretix/plugins/banktransfer/signals.py b/src/pretix/plugins/banktransfer/signals.py index ece6a37b21..77c790bff4 100644 --- a/src/pretix/plugins/banktransfer/signals.py +++ b/src/pretix/plugins/banktransfer/signals.py @@ -1,10 +1,29 @@ +from django.core.urlresolvers import reverse, resolve from django.dispatch import receiver +from django.utils.translation import ugettext_lazy as _ from pretix.base.signals import register_payment_providers from .payment import BankTransfer +from pretix.control.signals import nav_event @receiver(register_payment_providers) def register_payment_provider(sender, **kwargs): return BankTransfer + + +@receiver(nav_event) +def html_head_presale(sender, request=None, **kwargs): + url = resolve(request.path_info) + return [ + { + 'label': _('Import bank data'), + 'url': reverse('plugins:banktransfer.import', kwargs={ + 'event': request.event.slug, + 'organizer': request.event.organizer.slug, + }), + 'active': (url.namespace == 'plugins' and url.url_name == 'banktransfer.import'), + 'icon': 'upload', + } + ] diff --git a/src/pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_assign.html b/src/pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_assign.html new file mode 100644 index 0000000000..cab583f26b --- /dev/null +++ b/src/pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_assign.html @@ -0,0 +1,68 @@ +{% extends "pretixplugins/banktransfer/import_base.html" %} +{% load i18n %} +{% block inner %} +
{% blocktrans trimmed %} + We've been unable to automatically determine how the columns in your file are aligned. Please help us + by selecting which column contain what kind of data. + {% endblocktrans %}
+ +{% endblock %} diff --git a/src/pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_base.html b/src/pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_base.html new file mode 100644 index 0000000000..c0e6a5b42e --- /dev/null +++ b/src/pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_base.html @@ -0,0 +1,8 @@ +{% extends "pretixcontrol/event/base.html" %} +{% load i18n %} +{% block title %}{% trans "Import bank data" %}{% endblock %} +{% block content %} +{% blocktrans trimmed %} + We detected the following payments. Please review them and click the 'Confirm' button below. + {% endblocktrans %}
+ +{% endblock %} diff --git a/src/pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_form.html b/src/pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_form.html new file mode 100644 index 0000000000..0ff0297f69 --- /dev/null +++ b/src/pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_form.html @@ -0,0 +1,25 @@ +{% extends "pretixplugins/banktransfer/import_base.html" %} +{% load i18n %} +{% block inner %} +{% blocktrans trimmed %} + This page allows you to upload bank statement files to process incoming payments. + {% endblocktrans %}
+{% blocktrans trimmed %}
+ Currently, only .csv files are supported.
+ {% endblocktrans %}