0001-lingo-new-page-to-display-payments-in-error-21626.patch
combo/apps/lingo/manager_views.py | ||
---|---|---|
20 | 20 | |
21 | 21 |
from django.core.urlresolvers import reverse_lazy |
22 | 22 |
from django.db.models import Q |
23 |
from django.db.models.expressions import RawSQL |
|
23 | 24 |
from django.utils import six |
24 |
from django.utils.timezone import make_aware |
|
25 |
from django.utils.timezone import make_aware, now
|
|
25 | 26 |
from django.views.generic import CreateView, UpdateView, ListView, DeleteView |
26 | 27 |
from django.http import HttpResponse |
27 | 28 |
from django.template.response import TemplateResponse |
... | ... | |
30 | 31 | |
31 | 32 |
from .forms import RegieForm |
32 | 33 |
from .forms import TransactionExportForm |
33 |
from .models import PaymentBackend, Regie, Transaction |
|
34 |
from .models import BasketItem, PaymentBackend, Regie, Transaction
|
|
34 | 35 | |
35 | 36 | |
36 | 37 |
class RegieListView(ListView): |
... | ... | |
103 | 104 |
return qs |
104 | 105 | |
105 | 106 | |
107 |
class BasketItemErrorListView(ListView): |
|
108 |
model = BasketItem |
|
109 |
template_name = 'lingo/basketitem_error_list.html' |
|
110 |
paginate_by = 10 |
|
111 | ||
112 |
def get_queryset(self): |
|
113 |
raw = ( |
|
114 |
'SELECT %s FROM lingo_transaction ' |
|
115 |
'INNER JOIN lingo_transaction_items ' |
|
116 |
'ON "lingo_transaction"."id" = "lingo_transaction_items"."transaction_id" ' |
|
117 |
'AND "lingo_transaction_items"."basketitem_id"="lingo_basketitem"."id" ' |
|
118 |
'ORDER BY start_date DESC LIMIT 1') |
|
119 |
queryset = ( |
|
120 |
BasketItem.objects |
|
121 |
.annotate(bank_transaction_id=RawSQL(raw % 'bank_transaction_id', [])) |
|
122 |
.annotate(transaction_status=RawSQL(raw % 'status', [])) |
|
123 |
.order_by()) |
|
124 |
queryset1 = ( |
|
125 |
queryset |
|
126 |
.filter( |
|
127 |
notification_date__isnull=True, |
|
128 |
cancellation_date__isnull=True, |
|
129 |
payment_date__lt=now() - datetime.timedelta(minutes=300))) |
|
130 |
queryset2 = queryset.filter(transaction_status=eopayment.ERROR) |
|
131 |
return queryset1.union(queryset2).order_by('-creation_date') |
|
132 | ||
133 | ||
106 | 134 |
def download_transactions_csv(request): |
107 | 135 |
if request.method == 'POST': |
108 | 136 |
form = TransactionExportForm(data=request.POST) |
combo/apps/lingo/templates/lingo/basketitem_error_list.html | ||
---|---|---|
1 |
{% extends "lingo/manager_base.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block appbar %} |
|
5 |
<h2>{% trans 'Payments in error' %}</h2> |
|
6 |
{% endblock %} |
|
7 | ||
8 |
{% block breadcrumb %} |
|
9 |
{{ block.super }} |
|
10 |
<a href="{% url 'lingo-manager-payment-error-list' %}">{% trans 'Payments in error' %}</a> |
|
11 |
{% endblock %} |
|
12 | ||
13 |
{% block content %} |
|
14 | ||
15 |
{% if object_list %} |
|
16 |
<table class="main"> |
|
17 |
<thead> |
|
18 |
<tr> |
|
19 |
<th>{% trans 'Item' %}</th> |
|
20 |
<th>{% trans 'Amount' %}</th> |
|
21 |
<th colspan="2">{% trans 'Date' %}</th> |
|
22 |
</tr> |
|
23 |
</thead> |
|
24 |
<tbody> |
|
25 |
{% for object in object_list %} |
|
26 |
<tr> |
|
27 |
<td><a href="{{ object.source_url }}">{{ object.subject }}</a></td> |
|
28 |
<td class="price">{{ object.amount }} €</td> |
|
29 |
<td>{{ object.payment_date }}</td> |
|
30 |
<td> |
|
31 |
{% if object.transaction_status != 99 %} |
|
32 |
<a href="{% url 'lingo-manager-homepage' %}?q={{ object.bank_transaction_id }}">{% trans "See transaction" %}</a> |
|
33 |
{% endif %} |
|
34 |
</td> |
|
35 |
</tr> |
|
36 |
{% endfor %} |
|
37 |
</tbody> |
|
38 |
</table> |
|
39 | ||
40 |
{% include "gadjo/pagination.html" %} |
|
41 | ||
42 |
{% else %} |
|
43 |
<div class="big-msg-info"> |
|
44 |
{% blocktrans %} |
|
45 |
This site doesn't have any payment in error yet. |
|
46 |
{% endblocktrans %} |
|
47 |
</div> |
|
48 |
{% endif %} |
|
49 | ||
50 |
{% endblock %} |
combo/apps/lingo/templates/lingo/transaction_list.html | ||
---|---|---|
4 | 4 |
{% block appbar %} |
5 | 5 |
<h2>{% trans 'Transactions' %}</h2> |
6 | 6 |
<span class="actions"> |
7 |
<a href="{% url 'lingo-manager-payment-error-list' %}">{% trans 'Payments in error' %}</a> |
|
7 | 8 |
<a href="{% url 'lingo-manager-paymentbackend-list' %}">{% trans 'Payment backends' %}</a> |
8 | 9 |
<a href="{% url 'lingo-manager-regie-list' %}">{% trans 'Regies' %}</a> |
9 | 10 |
<a rel="popup" href="{% url 'lingo-manager-transactions-download' %}" data-autoclose-dialog="true">{% trans 'download CSV' %}</a> |
combo/apps/lingo/urls.py | ||
---|---|---|
23 | 23 |
RemoveBasketItemApiView, ValidateTransactionApiView, |
24 | 24 |
CancelTransactionApiView, SelfInvoiceView, BasketItemPayView) |
25 | 25 |
from .manager_views import (RegieListView, RegieCreateView, RegieUpdateView, |
26 |
RegieDeleteView, TransactionListView, download_transactions_csv, |
|
27 |
PaymentBackendListView, PaymentBackendCreateView, |
|
28 |
PaymentBackendUpdateView, PaymentBackendDeleteView) |
|
26 |
RegieDeleteView, TransactionListView, BasketItemErrorListView, |
|
27 |
download_transactions_csv, PaymentBackendListView, |
|
28 |
PaymentBackendCreateView, PaymentBackendUpdateView, |
|
29 |
PaymentBackendDeleteView) |
|
29 | 30 | |
30 | 31 |
lingo_manager_urls = [ |
31 | 32 |
url('^$', TransactionListView.as_view(), name='lingo-manager-homepage'), |
33 |
url('^payments/error/$', BasketItemErrorListView.as_view(), name='lingo-manager-payment-error-list'), |
|
32 | 34 |
url('^transactions/download-csv/$', download_transactions_csv, name='lingo-manager-transactions-download'), |
33 | 35 |
url('^regies/$', RegieListView.as_view(), name='lingo-manager-regie-list'), |
34 | 36 |
url('^regies/add/$', RegieCreateView.as_view(), name='lingo-manager-regie-add'), |
tests/test_lingo_manager.py | ||
---|---|---|
227 | 227 |
assert resp.text.count('<tr') == 0 |
228 | 228 |
assert 'No transactions found matching' in resp.text |
229 | 229 | |
230 | ||
231 |
def test_basketitem_error_list(app, admin_user, payment_backend): |
|
232 |
regie = Regie.objects.create( |
|
233 |
label='test-regie', slug='test-regie', payment_backend=payment_backend) |
|
234 |
user = User.objects.create_user('dimebag', 'dime@bag.pan', 'pwd') |
|
235 | ||
236 |
date_now = now() |
|
237 |
date_in_past = date_now - datetime.timedelta(minutes=300) |
|
238 | ||
239 |
# item with payment_date and notification_date, status PAID |
|
240 |
# => not displayed |
|
241 |
item1 = BasketItem.objects.create( |
|
242 |
user=user, |
|
243 |
regie=regie, |
|
244 |
subject='item 1', |
|
245 |
source_url='http://example.net/1', |
|
246 |
amount=1, |
|
247 |
payment_date=date_in_past, |
|
248 |
notification_date=date_in_past) |
|
249 |
transaction11 = Transaction.objects.create( |
|
250 |
status=eopayment.ERROR, |
|
251 |
order_id='order id 1.1', |
|
252 |
bank_transaction_id='bank_id_11', |
|
253 |
amount=1) |
|
254 |
transaction11.items.add(item1) |
|
255 |
transaction12 = Transaction.objects.create( |
|
256 |
status=eopayment.PAID, |
|
257 |
order_id='order id 1.2', |
|
258 |
bank_transaction_id='bank_id_12', |
|
259 |
amount=1) |
|
260 |
transaction12.items.add(item1) |
|
261 | ||
262 |
# item with payment_date and notification_date, status ERROR |
|
263 |
# => displayed |
|
264 |
item2 = BasketItem.objects.create( |
|
265 |
user=user, |
|
266 |
regie=regie, |
|
267 |
subject='item 2', |
|
268 |
source_url='http://example.net/2', |
|
269 |
amount=2, |
|
270 |
payment_date=date_in_past, |
|
271 |
notification_date=date_in_past) |
|
272 |
transaction21 = Transaction.objects.create( |
|
273 |
status=eopayment.ERROR, |
|
274 |
order_id='order id 2.1', |
|
275 |
bank_transaction_id='bank_id_21', |
|
276 |
amount=2) |
|
277 |
transaction21.items.add(item2) |
|
278 |
transaction22 = Transaction.objects.create( |
|
279 |
status=eopayment.ERROR, |
|
280 |
order_id='order id 2.2', |
|
281 |
bank_transaction_id='bank_id_22', |
|
282 |
amount=2) |
|
283 |
transaction22.items.add(item2) |
|
284 | ||
285 |
# item without dates, status ERROR |
|
286 |
# => displayed |
|
287 |
item3 = BasketItem.objects.create( |
|
288 |
user=user, |
|
289 |
regie=regie, |
|
290 |
subject='item 3', |
|
291 |
source_url='http://example.net/3', |
|
292 |
amount=3) |
|
293 |
transaction3 = Transaction.objects.create( |
|
294 |
status=eopayment.ERROR, |
|
295 |
order_id='order id 3', |
|
296 |
bank_transaction_id='bank_id_3', |
|
297 |
amount=3) |
|
298 |
transaction3.items.add(item3) |
|
299 | ||
300 |
# item with payment_date but no notification_date, too young |
|
301 |
# => not displayed |
|
302 |
item4 = BasketItem.objects.create( |
|
303 |
user=user, |
|
304 |
regie=regie, |
|
305 |
subject='item 4', |
|
306 |
source_url='http://example.net/4', |
|
307 |
amount=4, |
|
308 |
payment_date=date_now, |
|
309 |
notification_date=None) |
|
310 |
transaction4 = Transaction.objects.create( |
|
311 |
order_id='order id 4', |
|
312 |
bank_transaction_id='bank_id_4', |
|
313 |
amount=4) |
|
314 |
transaction4.items.add(item4) |
|
315 | ||
316 |
# item with payment_date but no notification_date, in the past |
|
317 |
# => displayed |
|
318 |
item5 = BasketItem.objects.create( |
|
319 |
user=user, |
|
320 |
regie=regie, |
|
321 |
subject='item 5', |
|
322 |
source_url='http://example.net/5', |
|
323 |
amount=5, |
|
324 |
payment_date=date_in_past, |
|
325 |
notification_date=None) |
|
326 |
transaction5 = Transaction.objects.create( |
|
327 |
order_id='order id 5', |
|
328 |
bank_transaction_id='bank_id_5', |
|
329 |
amount=5) |
|
330 |
transaction5.items.add(item5) |
|
331 | ||
332 |
# item with payment_date, no notification_date, but a cancellation_date, in the past |
|
333 |
# => not displayed |
|
334 |
item6 = BasketItem.objects.create( |
|
335 |
user=user, |
|
336 |
regie=regie, |
|
337 |
subject='item 6', |
|
338 |
source_url='http://example.net/6', |
|
339 |
amount=6, |
|
340 |
payment_date=date_in_past, |
|
341 |
notification_date=None, |
|
342 |
cancellation_date=date_now) |
|
343 |
transaction6 = Transaction.objects.create( |
|
344 |
order_id='order id 6', |
|
345 |
bank_transaction_id='bank_id_6', |
|
346 |
amount=6) |
|
347 |
transaction6.items.add(item6) |
|
348 | ||
349 |
app = login(app) |
|
350 |
resp = app.get('/manage/lingo/payments/error/', status=200) |
|
351 |
assert list(resp.context['object_list']) == [item5, item3, item2] |
|
352 |
assert '<a href="%s">' % item5.source_url in resp.text |
|
353 |
assert '<a href="%s">' % item3.source_url in resp.text |
|
354 |
assert '<a href="%s">' % item2.source_url in resp.text |
|
355 |
assert '/manage/lingo/?q=%s' % transaction5.bank_transaction_id in resp.text |
|
356 |
assert '/manage/lingo/?q=%s' % transaction3.bank_transaction_id not in resp.text |
|
357 |
assert '/manage/lingo/?q=%s' % transaction22.bank_transaction_id not in resp.text |
|
358 | ||
359 | ||
230 | 360 |
def test_configure_tipi_cell(app, admin_user): |
231 | 361 |
page = Page(title='tipi', slug='tipi', template_name='standard') |
232 | 362 |
page.save() |
233 |
- |