mirror of
https://github.com/pretix/pretix.git
synced 2026-04-28 00:02:37 +00:00
Bank transfer: Improve refund handling (#3769)
This commit is contained in:
@@ -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
|
||||
@@ -42,13 +43,14 @@ from django.conf import settings
|
||||
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
|
||||
@@ -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,
|
||||
@@ -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:
|
||||
|
||||
@@ -43,8 +43,8 @@ from django.utils.timezone import now
|
||||
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
|
||||
@@ -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 = []
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user