From 1cb6c0e3da6084eb407da740d5a99acc6ee2ae90 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Mon, 29 Aug 2016 23:01:50 +0200 Subject: [PATCH] Add more tolerance about handwriting in order code detection --- src/pretix/base/models/orders.py | 16 +++++++++++++++- src/pretix/control/views/orders.py | 12 ++++++++++-- src/pretix/plugins/banktransfer/views.py | 8 ++++++-- src/tests/plugins/banktransfer/test_import.py | 16 +++++++++++++++- 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/pretix/base/models/orders.py b/src/pretix/base/models/orders.py index 4baed38985..e477d91ef4 100644 --- a/src/pretix/base/models/orders.py +++ b/src/pretix/base/models/orders.py @@ -198,8 +198,22 @@ class Order(LoggedModel): else: self.payment_fee_tax_value = Decimal('0.00') + @staticmethod + def normalize_code(code): + tr = str.maketrans({ + '2': 'Z', + '4': 'A', + '5': 'S', + '6': 'G', + }) + return code.upper().translate(tr) + def assign_code(self): - charset = list('ABCDEFGHKLMNPQRSTUVWXYZ23456789') + # This omits some character pairs completely because they are hard to read even on screens (1/I and O/0) + # and includes only one of two characters for some pairs because they are sometimes hard to distinguish in + # handwriting (2/Z, 4/A, 5/S, 6/G). This allows for better detection e.g. in incoming wire transfers that + # might include OCR'd handwritten text + charset = list('ABCDEFGHJKLMNPQRSTUVWXYZ3789') while True: code = get_random_string(length=settings.ENTROPY['order_code'], allowed_chars=charset) if not Order.objects.filter(event=self.event, code=code).exists(): diff --git a/src/pretix/control/views/orders.py b/src/pretix/control/views/orders.py index 16477464ed..da7d0e86e1 100644 --- a/src/pretix/control/views/orders.py +++ b/src/pretix/control/views/orders.py @@ -468,12 +468,20 @@ class OverView(EventPermissionRequiredMixin, TemplateView): class OrderGo(EventPermissionRequiredMixin, View): permission = 'can_view_orders' + def get_order(self, code): + try: + return Order.objects.get(code=code, event=self.request.event) + except Order.DoesNotExist: + return Order.objects.get(code=Order.normalize_code(code), event=self.request.event) + def get(self, request, *args, **kwargs): code = request.GET.get("code", "").upper().strip() try: if code.startswith(request.event.slug.upper()): - code = code[len(request.event.slug.upper()):] - order = Order.objects.get(code=code, event=request.event) + code = code[len(request.event.slug):] + if code.startswith('-'): + code = code[1:] + order = self.get_order(code) return redirect('control:event.order', event=request.event.slug, organizer=request.event.organizer.slug, code=order.code) except Order.DoesNotExist: diff --git a/src/pretix/plugins/banktransfer/views.py b/src/pretix/plugins/banktransfer/views.py index e40c35d4f3..1e6b7a922b 100644 --- a/src/pretix/plugins/banktransfer/views.py +++ b/src/pretix/plugins/banktransfer/views.py @@ -214,6 +214,9 @@ class ImportView(EventPermissionRequiredMixin, TemplateView): code = match.group(1) row['code'] = code order_codes.append(code) + normalized_code = Order.normalize_code(code) + if normalized_code != code: + order_codes.append(normalized_code) orders = {} # Perform query in bulks because of SQLite's default of SQLITE_MAX_VARIABLE_NUMBER = 999 @@ -225,8 +228,9 @@ class ImportView(EventPermissionRequiredMixin, TemplateView): for row in data: if 'code' not in row: continue - if row['code'] in orders: - order = orders[row['code']] + normalized_code = Order.normalize_code(row['code']) + if row['code'] in orders or normalized_code in orders: + order = orders[row['code']] if row['code'] in orders else orders[normalized_code] row['order'] = order if order.status == Order.STATUS_PENDING: amount = Decimal(row['amount']) diff --git a/src/tests/plugins/banktransfer/test_import.py b/src/tests/plugins/banktransfer/test_import.py index 6035693613..77624d605d 100644 --- a/src/tests/plugins/banktransfer/test_import.py +++ b/src/tests/plugins/banktransfer/test_import.py @@ -31,6 +31,12 @@ def env(): datetime=now(), expires=now() + timedelta(days=10), total=23, payment_provider='banktransfer' ) + Order.objects.create( + code='GS89Z', event=event, + status=Order.STATUS_CANCELLED, + datetime=now(), expires=now() + timedelta(days=10), + total=23, payment_provider='banktransfer' + ) quota = Quota.objects.create(name="Test", size=2, event=event) item1 = Item.objects.create(event=event, name="Ticket", default_price=23) quota.items.add(item1) @@ -51,6 +57,7 @@ Buchungstag;Valuta;Buchungstext;Auftraggeber / Empfänger;Verwendungszweck;Betra 09.04.2015;09.04.2015;SEPA-Überweisung;Karla Kundin;Bestellung DUMMY1234S;42,00; 09.04.2015;09.04.2015;SEPA-Überweisung;Karla Kundin;Bestellung DUMMY1234S;23,00; 09.04.2015;09.04.2015;SEPA-Überweisung;Karla Kundin;Bestellung DUMMY6789Z;23,00; +09.04.2015;09.04.2015;SEPA-Überweisung;Karla Kundin;Bestellung DUMMY65892;23,00; """.encode("utf-8"), content_type="text/csv") @@ -72,7 +79,7 @@ Buchungstag;Valuta;Buchungstext;Auftraggeber / Empfänger;Verwendungszweck;Betra r = client.post('/control/event/dummy/dummy/banktransfer/import/', data) doc = BeautifulSoup(r.content) assert r.status_code == 200 - assert len(doc.select("form table tbody tr")) == 5 + assert len(doc.select("form table tbody tr")) == 6 trs = doc.select("form table tbody tr") assert trs[0].select("td")[0].text == "09.04.2015" assert trs[0].select("td")[1].text == "Bestellung 2015ABCDE" @@ -99,6 +106,13 @@ Buchungstag;Valuta;Buchungstext;Auftraggeber / Empfänger;Verwendungszweck;Betra assert trs[4].select("td")[2].text == "23.00" assert trs[4].select("td")[3].text == "Karla Kundin" assert trs[4].select("td")[5].text == "Order has been cancelled" + assert trs[5].select("td")[0].text == "09.04.2015" + # Test "autocorrection" + assert trs[5].select("td")[1].text == "Bestellung DUMMY65892" + assert trs[5].select("td")[2].text == "23.00" + assert trs[5].select("td")[3].text == "Karla Kundin" + assert "GS89Z" in trs[5].select("td")[4].text + assert trs[5].select("td")[5].text == "Order has been cancelled" data = {} for inp in doc.select("form input"):