From c491c8232ec33dd6654a77d6c17ac07bec934413 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Fri, 5 Dec 2025 10:54:03 +0100 Subject: [PATCH] Bank transfer: Allow dashes in event slug to be missing (Z#23216859) (#5682) * Bank transfer: Allow dashes in event slug to be missing (Z#23216859) * Update src/pretix/plugins/banktransfer/tasks.py Co-authored-by: Richard Schreiber * Update src/pretix/plugins/banktransfer/tasks.py Co-authored-by: Richard Schreiber * Apply suggestions from code review Co-authored-by: Richard Schreiber --------- Co-authored-by: Richard Schreiber --- src/pretix/plugins/banktransfer/tasks.py | 50 ++++++++++++++----- src/tests/plugins/banktransfer/test_import.py | 14 ++++++ 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/src/pretix/plugins/banktransfer/tasks.py b/src/pretix/plugins/banktransfer/tasks.py index f4fbc7d4e..f8999fbcd 100644 --- a/src/pretix/plugins/banktransfer/tasks.py +++ b/src/pretix/plugins/banktransfer/tasks.py @@ -34,8 +34,10 @@ import json import logging +import operator import re from decimal import Decimal +from functools import reduce import dateutil.parser from celery.exceptions import MaxRetriesExceededError @@ -117,20 +119,26 @@ def _find_order_for_code(base_qs, code): pass -def _find_order_for_invoice_id(base_qs, prefix, number): +def _find_order_for_invoice_id(base_qs, prefixes, number): try: # Working with __iregex here is an experiment, if this turns out to be too slow in production # we might need to switch to a different approach. + r = [ + Q( + prefix__istartswith=prefix, # redundant, but hopefully makes it a little faster + full_invoice_no__iregex=prefix + r'[\- ]*0*' + number + ) + for prefix in set(prefixes) + ] return base_qs.select_related('order').get( - prefix__istartswith=prefix, # redundant, but hopefully makes it a little faster - full_invoice_no__iregex=prefix + r'[\- ]*0*' + number + reduce(operator.or_, r) ).order except (Invoice.DoesNotExist, Invoice.MultipleObjectsReturned): pass @transaction.atomic -def _handle_transaction(trans: BankTransaction, matches: tuple, event: Event = None, organizer: Organizer = None): +def _handle_transaction(trans: BankTransaction, matches: tuple, regex_match_to_slug, event: Event = None, organizer: Organizer = None): orders = [] if event: for slug, code in matches: @@ -139,18 +147,19 @@ def _handle_transaction(trans: BankTransaction, matches: tuple, event: Event = N if order.code not in {o.code for o in orders}: orders.append(order) else: - order = _find_order_for_invoice_id(Invoice.objects.filter(event=event), slug, code) + order = _find_order_for_invoice_id(Invoice.objects.filter(event=event), (slug, regex_match_to_slug.get(slug, slug)), code) if order and order.code not in {o.code for o in orders}: orders.append(order) else: qs = Order.objects.filter(event__organizer=organizer) for slug, code in matches: - order = _find_order_for_code(qs.filter(event__slug__iexact=slug), code) + original_slug = regex_match_to_slug.get(slug, slug) + order = _find_order_for_code(qs.filter(Q(event__slug__iexact=slug) | Q(event__slug__iexact=original_slug)), code) if order: if order.code not in {o.code for o in orders}: orders.append(order) else: - order = _find_order_for_invoice_id(Invoice.objects.filter(event__organizer=organizer), slug, code) + order = _find_order_for_invoice_id(Invoice.objects.filter(event__organizer=organizer), (slug, original_slug), code) if order and order.code not in {o.code for o in orders}: orders.append(order) @@ -366,22 +375,37 @@ def process_banktransfers(self, job: int, data: list) -> None: transactions = _get_unknown_transactions(job, data, **job.owner_kwargs) # Match order codes + regex_match_to_slug = {} code_len_agg = Order.objects.filter(event__organizer=job.organizer).annotate( clen=Length('code') ).aggregate(min=Min('clen'), max=Max('clen')) if job.event: - prefixes = {job.event.slug.upper()} + prefixes = {job.event.slug.upper(), job.event.slug.upper().replace("-", "")} + if "-" in job.event.slug: + regex_match_to_slug[job.event.slug.upper().replace("-", "")] = job.event.slug else: - prefixes = {e.slug.upper() for e in job.organizer.events.all()} + prefixes = set() + for e in job.organizer.events.all(): + prefixes.add(e.slug.upper()) + if "-" in e.slug: + prefixes.add(e.slug.upper().replace("-", "")) + regex_match_to_slug[e.slug.upper().replace("-", "")] = e.slug # Match invoice numbers inr_len_agg = Invoice.objects.filter(event__organizer=job.organizer).annotate( clen=Length('invoice_no') ).aggregate(min=Min('clen'), max=Max('clen')) if job.event: - prefixes |= {p.rstrip(' -') for p in Invoice.objects.filter(event=job.event).distinct().values_list('prefix', flat=True)} + invoice_prefixes = Invoice.objects.filter(event=job.event) else: - prefixes |= {p.rstrip(' -') for p in Invoice.objects.filter(event__organizer=job.organizer).distinct().values_list('prefix', flat=True)} + invoice_prefixes = Invoice.objects.filter(event__organizer=job.organizer) + for p in invoice_prefixes.order_by().distinct().values_list('prefix', flat=True): + prefix = p.rstrip(" -") + prefixes.add(prefix) + if "-" in prefix: + prefix_nodash = prefix.replace("-", "") + prefixes.add(prefix_nodash) + regex_match_to_slug[prefix_nodash] = prefix pattern = re.compile( "(%s)[ \\-_]*([A-Z0-9]{%s,%s})" % ( @@ -409,9 +433,9 @@ def process_banktransfers(self, job: int, data: list) -> None: if matches: if job.event: - _handle_transaction(trans, matches, event=job.event) + _handle_transaction(trans, matches, regex_match_to_slug, event=job.event) else: - _handle_transaction(trans, matches, organizer=job.organizer) + _handle_transaction(trans, matches, regex_match_to_slug, organizer=job.organizer) else: trans.state = BankTransaction.STATE_NOMATCH trans.save() diff --git a/src/tests/plugins/banktransfer/test_import.py b/src/tests/plugins/banktransfer/test_import.py index 925289119..56646b587 100644 --- a/src/tests/plugins/banktransfer/test_import.py +++ b/src/tests/plugins/banktransfer/test_import.py @@ -385,6 +385,20 @@ def test_mark_paid_organizer_dash_in_slug(env, orga_job): assert env[2].status == Order.STATUS_PAID +@pytest.mark.django_db +def test_mark_paid_organizer_dash_in_slug_missing(env, orga_job): + env[0].slug = "foo-bar" + env[0].save() + process_banktransfers(orga_job, [{ + 'payer': 'Karla Kundin', + 'reference': 'Bestellung FOOBAR1234S', + 'date': '2016-01-26', + 'amount': '23.00' + }]) + env[2].refresh_from_db() + assert env[2].status == Order.STATUS_PAID + + @pytest.mark.django_db def test_mark_paid_organizer_varying_order_code_length(env, orga_job): env[2].code = "123412341234"