forked from CGM_Public/pretix_original
Allow import bank data from CSV files
This commit is contained in:
148
src/pretix/plugins/banktransfer/views.py
Normal file
148
src/pretix/plugins/banktransfer/views.py
Normal file
@@ -0,0 +1,148 @@
|
||||
import csv
|
||||
from decimal import Decimal
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
from django.contrib import messages
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.shortcuts import redirect, render
|
||||
from django.utils.timezone import now
|
||||
from django.views.generic import TemplateView
|
||||
from pretix.base.models import Order
|
||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||
from pretix.plugins.banktransfer import csvimport
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
logger = logging.getLogger('pretix.plugins.banktransfer')
|
||||
|
||||
|
||||
class ImportView(EventPermissionRequiredMixin, TemplateView):
|
||||
template_name = 'pretixplugins/banktransfer/import_form.html'
|
||||
permission = 'can_change_orders'
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
if 'mark_paid' in self.request.POST:
|
||||
orders = Order.objects.filter(event=self.request.event,
|
||||
code__in=self.request.POST.getlist('mark_paid'))
|
||||
for order in orders:
|
||||
order.mark_paid(provider='banktransfer', info=json.dumps({
|
||||
'reference': self.request.POST.get('reference_%s' % order.code),
|
||||
'date': self.request.POST.get('date_%s' % order.code),
|
||||
'payer': self.request.POST.get('payer_%s' % order.code),
|
||||
'import': now().isoformat(),
|
||||
}))
|
||||
|
||||
messages.success(self.request, _('The selected orders have been marked as paid.'))
|
||||
return self.redirect_back()
|
||||
return self.process_csv()
|
||||
|
||||
def process_csv(self):
|
||||
if 'file' in self.request.FILES:
|
||||
# if file is csv file
|
||||
try:
|
||||
data = csvimport.get_rows_from_file(self.request.FILES['file'])
|
||||
except csv.Error as e: # TODO: narrow down
|
||||
logger.error('Import failed: ' + str(e))
|
||||
messages.error(self.request, _('I\'m sorry, but we were unable to import this CSV file. Please '
|
||||
'contact support for help.'))
|
||||
return self.redirect_back()
|
||||
|
||||
if len(data) == 0:
|
||||
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)
|
||||
try:
|
||||
parsed = csvimport.parse(data, hint)
|
||||
except csvimport.HintMismatchError as e: # TODO: narrow down
|
||||
logger.error('Import using stored hint failed: ' + str(e))
|
||||
else:
|
||||
return self.confirm_view(parsed)
|
||||
|
||||
return self.assign_view(data)
|
||||
|
||||
elif 'amount' in self.request.POST: # CSV hint given
|
||||
data = []
|
||||
for i in range(int(self.request.POST.get('rows'))):
|
||||
data.append([
|
||||
self.request.POST.get('col[%d][%d]' % (i, j))
|
||||
for j in range(int(self.request.POST.get('cols')))
|
||||
])
|
||||
if 'reference' not in self.request.POST:
|
||||
messages.error(self.request, _('You need to select the column containing the payment reference.'))
|
||||
return self.assign_view(data)
|
||||
try:
|
||||
hint = csvimport.new_hint(self.request.POST)
|
||||
except Exception as e:
|
||||
logger.error('Parsing hint failed: ' + str(e))
|
||||
messages.error(self.request, _('We were unable to process your input.'))
|
||||
return self.assign_view(data)
|
||||
try:
|
||||
self.request.event.settings.set('banktransfer_csvhint', hint)
|
||||
except Exception as e: # TODO: narrow down
|
||||
logger.error('Import using stored hint failed: ' + str(e))
|
||||
pass
|
||||
else:
|
||||
parsed = csvimport.parse(data, hint)
|
||||
return self.confirm_view(parsed)
|
||||
return super().get(self.request)
|
||||
|
||||
def confirm_view(self, parsed):
|
||||
parsed = self.annotate_data(parsed)
|
||||
return render(self.request, 'pretixplugins/banktransfer/import_confirm.html', {
|
||||
'rows': parsed
|
||||
})
|
||||
|
||||
def assign_view(self, parsed):
|
||||
return render(self.request, 'pretixplugins/banktransfer/import_assign.html', {
|
||||
'rows': parsed
|
||||
})
|
||||
|
||||
def redirect_back(self):
|
||||
return redirect(reverse('plugins:banktransfer.import', kwargs={
|
||||
'event': self.request.event.slug,
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
}))
|
||||
|
||||
def annotate_data(self, data):
|
||||
pattern = re.compile(self.request.event.slug.upper() + "([A-Z0-9]{5})")
|
||||
amount_pattern = re.compile("[^0-9.-]")
|
||||
for row in data:
|
||||
row['ok'] = False
|
||||
match = pattern.search(row['reference'].upper())
|
||||
if not match:
|
||||
row['class'] = ''
|
||||
row['message'] = _('No order code detected')
|
||||
continue
|
||||
|
||||
code = match.group(1)
|
||||
try:
|
||||
order = Order.objects.current.get(event=self.request.event,
|
||||
code=code)
|
||||
except Order.DoesNotExist:
|
||||
row['class'] = 'error'
|
||||
row['message'] = _('Unknown order code detected')
|
||||
else:
|
||||
row['order'] = order
|
||||
if order.status == Order.STATUS_PENDING:
|
||||
amount = Decimal(amount_pattern.sub("", row['amount'].replace(",", ".")))
|
||||
if amount != order.total:
|
||||
row['class'] = 'error'
|
||||
row['message'] = _('Found wrong amount. Expected: %s' % str(order.total))
|
||||
else:
|
||||
row['class'] = 'success'
|
||||
row['message'] = _('Valid payment')
|
||||
row['ok'] = True
|
||||
elif order.status == Order.STATUS_CANCELLED:
|
||||
row['class'] = 'error'
|
||||
row['message'] = _('Order has been cancelled')
|
||||
elif order.status == Order.STATUS_PAID:
|
||||
# TODO: Do a plausibility check to tell duplicate payments from overlapping import files
|
||||
row['class'] = ''
|
||||
row['message'] = _('Order already has been paid')
|
||||
elif order.status == Order.STATUS_REFUNDED:
|
||||
row['class'] = 'warning'
|
||||
row['message'] = _('Order has been refunded')
|
||||
return data
|
||||
Reference in New Issue
Block a user