Skip to content

Commit

Permalink
Bank transfer: Improve refund handling (#3769)
Browse files Browse the repository at this point in the history
  • Loading branch information
raphaelm committed Dec 20, 2023
1 parent 2e5385c commit 9e3ce4f
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 10 deletions.
59 changes: 51 additions & 8 deletions src/pretix/plugins/banktransfer/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under the License.

import json
import logging
import re
from decimal import Decimal
Expand All @@ -42,13 +43,14 @@
from django.db import transaction
from django.db.models import Max, Min, Q
from django.db.models.functions import Length
from django.utils.timezone import now
from django.utils.translation import gettext_noop
from django_scopes import scope, scopes_disabled

from pretix.base.email import get_email_context
from pretix.base.i18n import language
from pretix.base.models import (
Event, Invoice, Order, OrderPayment, Organizer, Quota,
Event, Invoice, Order, OrderPayment, OrderRefund, Organizer, Quota,
)
from pretix.base.payment import PaymentException
from pretix.base.services.locking import LockTimeoutException
Expand Down Expand Up @@ -194,6 +196,53 @@ def _handle_transaction(trans: BankTransaction, matches: tuple, event: Event = N

trans.state = BankTransaction.STATE_VALID
for order, amount in splits:
info_data = {
'reference': trans.reference,
'date': trans.date_parsed.isoformat() if trans.date_parsed else trans.date,
'payer': trans.payer,
'iban': trans.iban,
'bic': trans.bic,
'full_amount': str(trans.amount),
'trans_id': trans.pk
}
if amount < Decimal("0.00"):
pending_refund = order.refunds.filter(
amount=-amount,
provider__in=('manual', 'banktransfer'),
state__in=(OrderRefund.REFUND_STATE_CREATED, OrderRefund.REFUND_STATE_TRANSIT),
).first()
existing_payment = order.payments.filter(
provider='banktransfer',
state__in=(OrderPayment.PAYMENT_STATE_CONFIRMED,),
).first()
if pending_refund:
pending_refund.provider = "banktransfer"
pending_refund.info_data = {
**pending_refund.info_data,
**info_data,
}
pending_refund.done()
elif existing_payment:
existing_payment.create_external_refund(
amount=-amount,
info=json.dumps(info_data)
)
else:
r = order.refunds.create(
state=OrderRefund.REFUND_STATE_EXTERNAL,
source=OrderRefund.REFUND_SOURCE_EXTERNAL,
amount=-amount,
order=order,
execution_date=now(),
provider='banktransfer',
info=json.dumps(info_data)
)
order.log_action('pretix.event.order.refund.created.externally', {
'local_id': r.local_id,
'provider': r.provider,
})
continue

try:
p, created = order.payments.get_or_create(
amount=amount,
Expand All @@ -213,13 +262,7 @@ def _handle_transaction(trans: BankTransaction, matches: tuple, event: Event = N

p.info_data = {
**p.info_data,
'reference': trans.reference,
'date': trans.date_parsed.isoformat() if trans.date_parsed else trans.date,
'payer': trans.payer,
'iban': trans.iban,
'bic': trans.bic,
'full_amount': str(trans.amount),
'trans_id': trans.pk
**info_data,
}

if created:
Expand Down
87 changes: 85 additions & 2 deletions src/tests/plugins/banktransfer/test_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@
from django_scopes import scopes_disabled

from pretix.base.models import (
Event, Item, Order, OrderFee, OrderPayment, OrderPosition, Organizer,
Quota, Team, User,
Event, Item, Order, OrderFee, OrderPayment, OrderPosition, OrderRefund,
Organizer, Quota, Team, User,
)
from pretix.base.services.invoices import generate_invoice
from pretix.plugins.banktransfer.models import BankImportJob, BankTransaction
Expand Down Expand Up @@ -141,6 +141,11 @@ def orga_job(env):
return BankImportJob.objects.create(organizer=env[0].organizer).pk


@pytest.fixture
def orga_job2(env):
return BankImportJob.objects.create(organizer=env[0].organizer).pk


@pytest.mark.django_db
def test_mark_paid(env, job):
djmail.outbox = []
Expand Down Expand Up @@ -610,3 +615,81 @@ def test_pending_paypal_replace_fee_missing(env, job):
assert env[2].fees.count() == 1
assert env[2].fees.last().value == Decimal('1.00')
assert env[2].total == Decimal('24.00')


@pytest.mark.django_db
def test_refund_handling_no_payments(env, orga_job):
process_banktransfers(orga_job, [{
'payer': 'Karla Kundin',
'reference': 'Bestellung DUMMY-1234S',
'date': '2016-01-26',
'amount': '-23.00'
}])
with scopes_disabled():
env[2].refresh_from_db()
assert env[2].status == Order.STATUS_PENDING
assert env[2].payments.count() == 0
r = env[2].refunds.get()
assert r.state == OrderRefund.REFUND_STATE_EXTERNAL
assert r.amount == Decimal("23.00")


@pytest.mark.django_db
def test_refund_handling_refund_for_payment(env, orga_job, orga_job2):
process_banktransfers(orga_job, [{
'payer': 'Karla Kundin',
'reference': 'Bestellung DUMMY-1234S',
'date': '2016-01-26',
'amount': '13.00'
}])
with scopes_disabled():
env[2].refresh_from_db()
assert env[2].status == Order.STATUS_PENDING
p = env[2].payments.get()

process_banktransfers(orga_job2, [{
'payer': 'Karla Kundin',
'reference': 'Erstattung DUMMY-1234S',
'date': '2016-01-27',
'amount': '-13.00'
}])
with scopes_disabled():
env[2].refresh_from_db()
assert env[2].status == Order.STATUS_PENDING
assert env[2].payments.count() == 1
r = env[2].refunds.get()
assert r.state == OrderRefund.REFUND_STATE_EXTERNAL
assert r.payment == p
assert r.amount == Decimal("13.00")


@pytest.mark.django_db
def test_refund_handling_pending_refund(env, orga_job, orga_job2):
process_banktransfers(orga_job, [{
'payer': 'Karla Kundin',
'reference': 'Bestellung DUMMY-1234S',
'date': '2016-01-26',
'amount': '23.00'
}])
with scopes_disabled():
env[2].refresh_from_db()
assert env[2].status == Order.STATUS_PAID
env[2].status = Order.STATUS_PENDING
env[2].save()
r = env[2].refunds.create(
state=OrderRefund.REFUND_STATE_CREATED,
provider="manual",
amount="23.00",
)

process_banktransfers(orga_job2, [{
'payer': 'Karla Kundin',
'reference': 'Erstattung DUMMY-1234S',
'date': '2016-01-27',
'amount': '-23.00'
}])
with scopes_disabled():
env[2].refresh_from_db()
r.refresh_from_db()
assert env[2].payments.count() == 1
assert r.state == OrderRefund.REFUND_STATE_DONE

0 comments on commit 9e3ce4f

Please sign in to comment.