Add new field OrderRefund.comment

This commit is contained in:
Raphael Michel
2021-01-15 11:25:09 +01:00
parent 674d7673ce
commit f1cd46f6dc
17 changed files with 103 additions and 38 deletions
+6 -1
View File
@@ -325,7 +325,8 @@ state string Payment state,
source string How this refund has been created, one of ``buyer``, ``admin``, or ``external``
amount money (string) Payment amount
created datetime Date and time of creation of this payment
payment_date datetime Date and time of completion of this payment (or ``null``)
comment string Reason for refund (shown to the customer in some cases, can be ``null``).
execution_date datetime Date and time of completion of this refund (or ``null``)
provider string Identification string of the payment provider
===================================== ========================== =======================================================
@@ -2119,6 +2120,7 @@ Order refund endpoints
"payment": 1,
"created": "2017-12-01T10:00:00Z",
"execution_date": "2017-12-04T12:13:12Z",
"comment": "Cancellation",
"provider": "banktransfer"
}
]
@@ -2161,6 +2163,7 @@ Order refund endpoints
"payment": 1,
"created": "2017-12-01T10:00:00Z",
"execution_date": "2017-12-04T12:13:12Z",
"comment": "Cancellation",
"provider": "banktransfer"
}
@@ -2195,6 +2198,7 @@ Order refund endpoints
"amount": "23.00",
"payment": 1,
"execution_date": null,
"comment": "Cancellation",
"provider": "manual",
"mark_canceled": false,
"mark_pending": true
@@ -2216,6 +2220,7 @@ Order refund endpoints
"payment": 1,
"created": "2017-12-01T10:00:00Z",
"execution_date": null,
"comment": "Cancellation",
"provider": "manual"
}
+2 -2
View File
@@ -502,7 +502,7 @@ class OrderRefundSerializer(I18nAwareModelSerializer):
class Meta:
model = OrderRefund
fields = ('local_id', 'state', 'source', 'amount', 'payment', 'created', 'execution_date', 'provider')
fields = ('local_id', 'state', 'source', 'amount', 'payment', 'created', 'execution_date', 'comment', 'provider')
class OrderURLField(serializers.URLField):
@@ -1324,7 +1324,7 @@ class OrderRefundCreateSerializer(I18nAwareModelSerializer):
class Meta:
model = OrderRefund
fields = ('state', 'source', 'amount', 'payment', 'execution_date', 'provider', 'info')
fields = ('state', 'source', 'amount', 'payment', 'execution_date', 'provider', 'info', 'comment')
def create(self, validated_data):
pid = validated_data.pop('payment', None)
+3 -2
View File
@@ -652,7 +652,7 @@ class PaymentListExporter(ListExporter):
headers = [
_('Event slug'), _('Order'), _('Payment ID'), _('Creation date'), _('Completion date'), _('Status'),
_('Status code'), _('Amount'), _('Payment method')
_('Status code'), _('Amount'), _('Payment method'), _('Comment')
]
yield headers
@@ -674,7 +674,8 @@ class PaymentListExporter(ListExporter):
obj.get_state_display(),
obj.state,
obj.amount * (-1 if isinstance(obj, OrderRefund) else 1),
provider_names.get(obj.provider, obj.provider)
provider_names.get(obj.provider, obj.provider),
obj.comment if isinstance(obj, OrderRefund) else "",
]
yield row
@@ -0,0 +1,18 @@
# Generated by Django 3.0.11 on 2021-01-15 09:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0174_merge_20201222_1031'),
]
operations = [
migrations.AddField(
model_name='orderrefund',
name='comment',
field=models.TextField(null=True),
),
]
+5
View File
@@ -1716,6 +1716,11 @@ class OrderRefund(models.Model):
max_length=255,
verbose_name=_("Payment provider")
)
comment = models.TextField(
verbose_name=_("Refund reason"),
help_text=_('May be shown to the end user or used e.g. as part of a payment reference.'),
null=True, blank=True
)
info = models.TextField(
verbose_name=_("Payment information"),
null=True, blank=True
+5 -2
View File
@@ -3,6 +3,7 @@ from decimal import Decimal
from django.db import transaction
from django.db.models import Count, Exists, IntegerField, OuterRef, Subquery
from django.utils.translation import gettext
from i18nfield.strings import LazyI18nString
from pretix.base.decimal import round_decimal
@@ -195,7 +196,8 @@ def cancel_event(self, event: Event, subevent: int, auto_refund: bool,
if auto_refund:
_try_auto_refund(o.pk, manual_refund=manual_refund, allow_partial=True,
source=OrderRefund.REFUND_SOURCE_ADMIN, refund_as_giftcard=refund_as_giftcard,
giftcard_expires=giftcard_expires, giftcard_conditions=giftcard_conditions)
giftcard_expires=giftcard_expires, giftcard_conditions=giftcard_conditions,
comment=gettext('Event canceled'))
finally:
if send:
_send_mail(o, send_subject, send_message, subevent, refund_amount, user, o.positions.all())
@@ -252,7 +254,8 @@ def cancel_event(self, event: Event, subevent: int, auto_refund: bool,
if auto_refund:
_try_auto_refund(o.pk, manual_refund=manual_refund, allow_partial=True,
source=OrderRefund.REFUND_SOURCE_ADMIN, refund_as_giftcard=refund_as_giftcard,
giftcard_expires=giftcard_expires, giftcard_conditions=giftcard_conditions)
giftcard_expires=giftcard_expires, giftcard_conditions=giftcard_conditions,
comment=gettext('Event canceled'))
if send:
_send_mail(o, send_subject, send_message, subevent, refund_amount, user, positions)
+7 -3
View File
@@ -2034,7 +2034,7 @@ _unset = object()
def _try_auto_refund(order, manual_refund=False, allow_partial=False, source=OrderRefund.REFUND_SOURCE_BUYER,
refund_as_giftcard=False, giftcard_expires=_unset, giftcard_conditions=None):
refund_as_giftcard=False, giftcard_expires=_unset, giftcard_conditions=None, comment=None):
notify_admin = False
error = False
if isinstance(order, int):
@@ -2059,6 +2059,7 @@ def _try_auto_refund(order, manual_refund=False, allow_partial=False, source=Ord
order=order,
payment=None,
source=source,
comment=comment,
state=OrderRefund.REFUND_STATE_CREATED,
execution_date=now(),
amount=can_auto_refund_sum,
@@ -2096,6 +2097,7 @@ def _try_auto_refund(order, manual_refund=False, allow_partial=False, source=Ord
source=source,
state=OrderRefund.REFUND_STATE_CREATED,
amount=value,
comment=comment,
provider=p.provider
)
order.log_action('pretix.event.order.refund.created', {
@@ -2125,6 +2127,7 @@ def _try_auto_refund(order, manual_refund=False, allow_partial=False, source=Ord
with transaction.atomic():
r = order.refunds.create(
source=source,
comment=comment,
state=OrderRefund.REFUND_STATE_CREATED,
amount=refund_amount - can_auto_refund_sum,
provider='manual'
@@ -2149,13 +2152,14 @@ def _try_auto_refund(order, manual_refund=False, allow_partial=False, source=Ord
@app.task(base=ProfiledTask, bind=True, max_retries=5, default_retry_delay=1, throws=(OrderError,))
@scopes_disabled()
def cancel_order(self, order: int, user: int=None, send_mail: bool=True, api_token=None, oauth_application=None,
device=None, cancellation_fee=None, try_auto_refund=False, refund_as_giftcard=False):
device=None, cancellation_fee=None, try_auto_refund=False, refund_as_giftcard=False, comment=None):
try:
try:
ret = _cancel_order(order, user, send_mail, api_token, device, oauth_application,
cancellation_fee)
if try_auto_refund:
_try_auto_refund(order, refund_as_giftcard=refund_as_giftcard)
_try_auto_refund(order, refund_as_giftcard=refund_as_giftcard,
comment=comment)
return ret
except LockTimeoutException:
self.retry()
@@ -6,6 +6,7 @@
{% load rich_text %}
{% load safelink %}
{% load eventsignal %}
{% load l10n %}
{% load phone_format %}
{% block title %}
{% blocktrans trimmed with code=order.code %}
@@ -97,7 +98,10 @@
{% csrf_token %}
<input type="hidden" name="start-action" value="do_nothing">
<input type="hidden" name="start-mode" value="partial">
<input type="hidden" name="start-partial_amount" value="{{ overpaid }}">
{% localize off %}
<input type="hidden" name="start-partial_amount" value="{{ overpaid|floatformat:2 }}">
{% endlocalize %}
<input type="hidden" name="comment" value="{% trans "Refund for overpayment" %}">
<div class="alert alert-warning">
{% blocktrans trimmed with amount=overpaid|money:request.event.currency %}
This order is currently overpaid by {{ amount }}.
@@ -759,11 +763,19 @@
{% endif %}
</td>
</tr>
{% if r.html_info %}
{% if r.html_info or staff_session or r.comment %}
<tr>
<td colspan="1"></td>
<td colspan="7">
{{ r.html_info|safe }}
{% if r.comment %}
<dl class="dl-horizontal">
<dt>{% trans "Comment" %}</dt>
<dd>{{ r.comment }}</dd>
</dl>
{% endif %}
{% if r.html_info %}
{{ r.html_info|safe }}
{% endif %}
{% if staff_session %}
<p>
<a href="" class="btn btn-default btn-xs" data-expandrefund
@@ -775,17 +787,6 @@
{% endif %}
</td>
</tr>
{% elif staff_session %}
<tr>
<td colspan="1"></td>
<td colspan="7">
<a href="" class="btn btn-default btn-xs" data-expandrefund
data-id="{{ r.pk }}">
<span class="fa-eye fa fa-fw"></span>
{% trans "Inspect" %}
</a>
</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
@@ -162,6 +162,13 @@
<input type="hidden" name="start-mode" value="{{ start_form.cleaned_data.mode }}">
<input type="hidden" name="start-partial_amount" value="{{ partial_amount }}">
<div class="form-group">
<label class="control-label" for="id_comment">{% trans "Refund reason" %}</label>
<input type="text" name="comment" class="form-control" title="{% trans "May be shown to the end user or used e.g. as part of a payment reference." %}" id="id_comment"
value="{{ comment|default:"" }}">
<div class="help-block">{% trans "May be shown to the end user or used e.g. as part of a payment reference." %}</div>
</div>
<div class="row checkout-button-row">
<div class="col-md-4">
<a class="btn btn-block btn-default btn-lg"
+17 -9
View File
@@ -5,7 +5,7 @@ import os
import re
from datetime import datetime, time, timedelta
from decimal import Decimal, DecimalException
from urllib.parse import urlencode
from urllib.parse import quote, urlencode
import vat_moss.id
from django.conf import settings
@@ -759,6 +759,7 @@ class OrderRefundView(OrderView):
def choose_form(self):
payments = list(self.order.payments.filter(state=OrderPayment.PAYMENT_STATE_CONFIRMED))
comment = self.request.POST.get("comment") or self.request.GET.get("comment") or None
if self.start_form.cleaned_data.get('mode') == 'full':
full_refund = self.order.payment_refund_sum
else:
@@ -800,6 +801,7 @@ class OrderRefundView(OrderView):
else OrderRefund.REFUND_STATE_CREATED
),
amount=manual_value,
comment=comment,
provider='manual'
))
@@ -827,6 +829,7 @@ class OrderRefundView(OrderView):
execution_date=now(),
amount=giftcard_value,
provider='giftcard',
comment=comment,
info=json.dumps({
'gift_card': giftcard.pk
})
@@ -857,6 +860,7 @@ class OrderRefundView(OrderView):
execution_date=now(),
amount=offsetting_value,
provider='offsetting',
comment=comment,
info=json.dumps({
'orders': [order.code]
})
@@ -891,6 +895,7 @@ class OrderRefundView(OrderView):
source=OrderRefund.REFUND_SOURCE_ADMIN,
state=OrderRefund.REFUND_STATE_CREATED,
amount=value,
comment=comment,
provider=p.provider
))
@@ -968,6 +973,7 @@ class OrderRefundView(OrderView):
'payments': payments,
'remainder': to_refund,
'order': self.order,
'comment': comment,
'giftcard_proposal': giftcard_proposal,
'partial_amount': (
self.request.POST.get('start-partial_amount') if self.request.method == 'POST'
@@ -1098,14 +1104,16 @@ class OrderTransition(OrderView):
if self.order.pending_sum < 0:
messages.success(self.request, _('The order has been canceled. You can now select how you want to '
'transfer the money back to the user.'))
return redirect(reverse('control:event.order.refunds.start', kwargs={
'event': self.request.event.slug,
'organizer': self.request.event.organizer.slug,
'code': self.order.code
}) + '?start-action=do_nothing&start-mode=partial&start-partial_amount={}&giftcard={}'.format(
round_decimal(self.order.pending_sum * -1),
'true' if self.req and self.req.refund_as_giftcard else 'false'
))
with language(self.order.locale):
return redirect(reverse('control:event.order.refunds.start', kwargs={
'event': self.request.event.slug,
'organizer': self.request.event.organizer.slug,
'code': self.order.code
}) + '?start-action=do_nothing&start-mode=partial&start-partial_amount={}&giftcard={}&comment={}'.format(
round_decimal(self.order.pending_sum * -1),
'true' if self.req and self.req.refund_as_giftcard else 'false',
quote(gettext('Order canceled'))
))
messages.success(self.request, _('The order has been canceled.'))
elif self.order.status == Order.STATUS_PENDING and to == 'e':
@@ -22,7 +22,7 @@ def get_refund_export_csv(refund_export: RefundExport):
output = StreamWriter(byte_data)
writer = csv.writer(output)
writer.writerow([_("Payer"), "IBAN", "BIC", _("Amount"), _("Currency"), _("Code")])
writer.writerow([_("Payer"), "IBAN", "BIC", _("Amount"), _("Currency"), _("Code"), _("Comment")])
for row in refund_export.rows_data:
bic = ''
if row.get('bic'):
@@ -39,6 +39,7 @@ def get_refund_export_csv(refund_export: RefundExport):
localize(Decimal(row['amount'])),
refund_export.currency,
row['id'],
row.get('comment') or '',
])
filename = _get_filename(refund_export) + ".csv"
@@ -68,7 +69,7 @@ def build_sepa_xml(refund_export: RefundExport, account_holder, iban, bic):
"IBAN": row["iban"],
"amount": int(Decimal(row['amount']) * 100), # in euro-cents
"execution_date": datetime.date.today(),
"description": f"{_('Refund')} {refund_export.entity_slug} {row['id']}",
"description": f"{_('Refund')} {refund_export.entity_slug} {row['id']} {row.get('comment') or ''}".strip()[:140],
}
if row.get('bic'):
try:
+1
View File
@@ -20,6 +20,7 @@ from pretix.base.services.mail import SendMailException
from pretix.base.services.orders import change_payment_provider
from pretix.base.services.tasks import TransactionAwareTask
from pretix.celery_app import app
from .models import BankImportJob, BankTransaction
logger = logging.getLogger(__name__)
+2
View File
@@ -602,6 +602,7 @@ def _unite_transaction_rows(transaction_rows):
"id": ", ".join(sorted(set(r['id'] for r in rows))),
"payer": ", ".join(sorted(set(r['payer'] for r in rows))),
"amount": sum(r['amount'] for r in rows),
"comment": ", ".join(r['comment'] for r in rows if r.get('comment')) or None,
})
return united_transactions_rows
@@ -649,6 +650,7 @@ class RefundExportListView(ListView):
transaction_rows.append({
"amount": refund.amount,
"id": refund.full_id,
"comment": refund.comment,
**{key: data.get(key) for key in ("payer", "iban", "bic")}
})
refund.done(user=self.request.user)
+1 -1
View File
@@ -1,7 +1,7 @@
import copy
import tempfile
from collections import OrderedDict, defaultdict
from datetime import date, datetime, timedelta, time
from datetime import date, datetime, time, timedelta
from decimal import Decimal
import pytz
+4 -2
View File
@@ -18,7 +18,7 @@ from django.shortcuts import get_object_or_404, redirect
from django.utils.decorators import method_decorator
from django.utils.functional import cached_property
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext, gettext_lazy as _
from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.generic import TemplateView, View
@@ -849,7 +849,9 @@ class OrderCancelDo(EventViewMixin, OrderDetailMixin, AsyncAction, View):
self.order.log_action('pretix.event.order.refund.requested')
return self.success(None)
else:
return self.do(self.order.pk, cancellation_fee=fee, try_auto_refund=True, refund_as_giftcard=giftcard)
comment = gettext('Canceled by customer')
return self.do(self.order.pk, cancellation_fee=fee, try_auto_refund=True, refund_as_giftcard=giftcard,
comment=comment)
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
+1
View File
@@ -201,6 +201,7 @@ TEST_REFUNDS_RES = [
"source": "admin",
"created": "2017-12-01T10:00:00Z",
"execution_date": "2017-12-01T10:00:00Z",
"comment": None,
"provider": "stripe",
"state": "done",
"amount": "23.00"
@@ -124,6 +124,7 @@ def test_unite_transaction_rows():
'iban': 'DE12345678901234567890',
'bic': 'HARKE9000',
'id': "ROLLA-R-1",
'comment': None,
'amount': Decimal("42.23"),
},
{
@@ -131,6 +132,7 @@ def test_unite_transaction_rows():
'iban': 'DE111111111111111111111',
'bic': 'ikswez2020',
'id': "PARTY-R-1",
'comment': None,
'amount': Decimal("6.50"),
}
], key=_row_key_func)
@@ -143,6 +145,7 @@ def test_unite_transaction_rows():
'iban': 'DE12345678901234567890',
'bic': 'HARKE9000',
'id': "ROLLA-R-1",
'comment': None,
'amount': Decimal("7.77"),
},
{
@@ -150,6 +153,7 @@ def test_unite_transaction_rows():
'iban': 'DE111111111111111111111',
'bic': 'ikswez2020',
'id': "PARTY-R-2",
'comment': None,
'amount': Decimal("13.50"),
}
], key=_row_key_func)
@@ -160,6 +164,7 @@ def test_unite_transaction_rows():
'iban': 'DE12345678901234567890',
'bic': 'HARKE9000',
'id': "ROLLA-R-1",
'comment': None,
'amount': Decimal("50.00"),
},
{
@@ -167,5 +172,6 @@ def test_unite_transaction_rows():
'iban': 'DE111111111111111111111',
'bic': 'ikswez2020',
'id': 'PARTY-R-1, PARTY-R-2',
'comment': None,
'amount': Decimal('20.00'),
}], key=_row_key_func)