diff --git a/combo/apps/lingo/manager_views.py b/combo/apps/lingo/manager_views.py
index 0dc5fd36..1c264701 100644
--- a/combo/apps/lingo/manager_views.py
+++ b/combo/apps/lingo/manager_views.py
@@ -20,8 +20,9 @@ from dateutil import parser as date_parser
from django.core.urlresolvers import reverse_lazy
from django.db.models import Q
+from django.db.models.expressions import RawSQL
from django.utils import six
-from django.utils.timezone import make_aware
+from django.utils.timezone import make_aware, now
from django.views.generic import CreateView, UpdateView, ListView, DeleteView
from django.http import HttpResponse
from django.template.response import TemplateResponse
@@ -30,7 +31,7 @@ import eopayment
from .forms import RegieForm
from .forms import TransactionExportForm
-from .models import PaymentBackend, Regie, Transaction
+from .models import BasketItem, PaymentBackend, Regie, Transaction
class RegieListView(ListView):
@@ -103,6 +104,34 @@ class TransactionListView(ListView):
return qs
+class BasketItemErrorListView(ListView):
+ model = BasketItem
+ template_name = 'lingo/basketitem_error_list.html'
+ paginate_by = 10
+
+ def get_queryset(self):
+ raw = (
+ 'SELECT %s FROM lingo_transaction '
+ 'INNER JOIN lingo_transaction_items '
+ 'ON "lingo_transaction"."id" = "lingo_transaction_items"."transaction_id" '
+ 'AND "lingo_transaction_items"."basketitem_id"="lingo_basketitem"."id" '
+ 'ORDER BY start_date DESC LIMIT 1')
+ queryset = (
+ BasketItem.objects
+ .annotate(bank_transaction_id=RawSQL(raw % 'bank_transaction_id', []))
+ .annotate(transaction_status=RawSQL(raw % 'status', []))
+ .filter(regie__webservice_url='')
+ .order_by())
+ queryset1 = (
+ queryset
+ .filter(
+ notification_date__isnull=True,
+ cancellation_date__isnull=True,
+ payment_date__lt=now() - datetime.timedelta(minutes=300)))
+ queryset2 = queryset.filter(transaction_status=eopayment.ERROR)
+ return queryset1.union(queryset2).order_by('-creation_date')
+
+
def download_transactions_csv(request):
if request.method == 'POST':
form = TransactionExportForm(data=request.POST)
diff --git a/combo/apps/lingo/templates/lingo/basketitem_error_list.html b/combo/apps/lingo/templates/lingo/basketitem_error_list.html
new file mode 100644
index 00000000..a689e379
--- /dev/null
+++ b/combo/apps/lingo/templates/lingo/basketitem_error_list.html
@@ -0,0 +1,50 @@
+{% extends "lingo/manager_base.html" %}
+{% load i18n %}
+
+{% block appbar %}
+
{% trans 'Payments in error' %}
+{% endblock %}
+
+{% block breadcrumb %}
+{{ block.super }}
+{% trans 'Payments in error' %}
+{% endblock %}
+
+{% block content %}
+
+{% if object_list %}
+
+
+
+ {% trans 'Item' %} |
+ {% trans 'Amount' %} |
+ {% trans 'Date' %} |
+
+
+
+{% for object in object_list %}
+
+ {% if object.source_url %}{{ object.subject }}{% else %}{{ object.subject }}{% endif %} |
+ {{ object.amount }} € |
+ {{ object.payment_date }} |
+
+ {% if object.transaction_status != 99 %}
+ {% trans "See transaction" %}
+ {% endif %}
+ |
+
+{% endfor %}
+
+
+
+{% include "gadjo/pagination.html" %}
+
+{% else %}
+
+ {% blocktrans %}
+ This site doesn't have any payment in error.
+ {% endblocktrans %}
+
+{% endif %}
+
+{% endblock %}
diff --git a/combo/apps/lingo/templates/lingo/transaction_list.html b/combo/apps/lingo/templates/lingo/transaction_list.html
index 25300308..6dd650f2 100644
--- a/combo/apps/lingo/templates/lingo/transaction_list.html
+++ b/combo/apps/lingo/templates/lingo/transaction_list.html
@@ -4,6 +4,7 @@
{% block appbar %}
{% trans 'Transactions' %}
+{% trans 'Payments in error' %}
{% trans 'Payment backends' %}
{% trans 'Regies' %}
{% trans 'download CSV' %}
diff --git a/combo/apps/lingo/urls.py b/combo/apps/lingo/urls.py
index 5498d9f1..a0d0a61e 100644
--- a/combo/apps/lingo/urls.py
+++ b/combo/apps/lingo/urls.py
@@ -23,12 +23,14 @@ from .views import (RegiesApiView, AddBasketItemApiView, PayView, CallbackView,
RemoveBasketItemApiView, ValidateTransactionApiView,
CancelTransactionApiView, SelfInvoiceView, BasketItemPayView)
from .manager_views import (RegieListView, RegieCreateView, RegieUpdateView,
- RegieDeleteView, TransactionListView, download_transactions_csv,
- PaymentBackendListView, PaymentBackendCreateView,
- PaymentBackendUpdateView, PaymentBackendDeleteView)
+ RegieDeleteView, TransactionListView, BasketItemErrorListView,
+ download_transactions_csv, PaymentBackendListView,
+ PaymentBackendCreateView, PaymentBackendUpdateView,
+ PaymentBackendDeleteView)
lingo_manager_urls = [
url('^$', TransactionListView.as_view(), name='lingo-manager-homepage'),
+ url('^payments/error/$', BasketItemErrorListView.as_view(), name='lingo-manager-payment-error-list'),
url('^transactions/download-csv/$', download_transactions_csv, name='lingo-manager-transactions-download'),
url('^regies/$', RegieListView.as_view(), name='lingo-manager-regie-list'),
url('^regies/add/$', RegieCreateView.as_view(), name='lingo-manager-regie-add'),
diff --git a/tests/test_lingo_manager.py b/tests/test_lingo_manager.py
index 34212420..f1f4c42d 100644
--- a/tests/test_lingo_manager.py
+++ b/tests/test_lingo_manager.py
@@ -227,6 +227,172 @@ def test_transactions_search(app, admin_user):
assert resp.text.count(' not displayed
+ item1 = BasketItem.objects.create(
+ user=user,
+ regie=regie,
+ subject='item 1',
+ source_url='http://example.net/1',
+ amount=1,
+ payment_date=date_in_past,
+ notification_date=date_in_past)
+ transaction11 = Transaction.objects.create(
+ status=eopayment.ERROR,
+ order_id='order id 1.1',
+ bank_transaction_id='bank_id_11',
+ amount=1)
+ transaction11.items.add(item1)
+ transaction12 = Transaction.objects.create(
+ status=eopayment.PAID,
+ order_id='order id 1.2',
+ bank_transaction_id='bank_id_12',
+ amount=1)
+ transaction12.items.add(item1)
+
+ # item with payment_date and notification_date, status ERROR
+ # => displayed
+ item2 = BasketItem.objects.create(
+ user=user,
+ regie=regie,
+ subject='item 2',
+ source_url='',
+ amount=2,
+ payment_date=date_in_past,
+ notification_date=date_in_past)
+ transaction21 = Transaction.objects.create(
+ status=eopayment.ERROR,
+ order_id='order id 2.1',
+ bank_transaction_id='bank_id_21',
+ amount=2)
+ transaction21.items.add(item2)
+ transaction22 = Transaction.objects.create(
+ status=eopayment.ERROR,
+ order_id='order id 2.2',
+ bank_transaction_id='bank_id_22',
+ amount=2)
+ transaction22.items.add(item2)
+ # the same but for the other regie
+ item2_bis = BasketItem.objects.create(
+ user=user,
+ regie=regie_with_webservice_url,
+ subject='item 2 bis',
+ source_url='',
+ amount=22222,
+ payment_date=date_in_past,
+ notification_date=date_in_past)
+ transaction2_bis = Transaction.objects.create(
+ status=eopayment.ERROR,
+ order_id='order id 2 bis',
+ bank_transaction_id='bank_id_2_bis',
+ amount=22222)
+ transaction2_bis.items.add(item2_bis)
+
+ # item without dates, status ERROR
+ # => displayed
+ item3 = BasketItem.objects.create(
+ user=user,
+ regie=regie,
+ subject='item 3',
+ source_url='http://example.net/3',
+ amount=3)
+ transaction3 = Transaction.objects.create(
+ status=eopayment.ERROR,
+ order_id='order id 3',
+ bank_transaction_id='bank_id_3',
+ amount=3)
+ transaction3.items.add(item3)
+
+ # item with payment_date but no notification_date, too young
+ # => not displayed
+ item4 = BasketItem.objects.create(
+ user=user,
+ regie=regie,
+ subject='item 4',
+ source_url='http://example.net/4',
+ amount=4,
+ payment_date=date_now,
+ notification_date=None)
+ transaction4 = Transaction.objects.create(
+ order_id='order id 4',
+ bank_transaction_id='bank_id_4',
+ amount=4)
+ transaction4.items.add(item4)
+
+ # item with payment_date but no notification_date, in the past
+ # => displayed
+ item5 = BasketItem.objects.create(
+ user=user,
+ regie=regie,
+ subject='item 5',
+ source_url='http://example.net/5',
+ amount=5,
+ payment_date=date_in_past,
+ notification_date=None)
+ transaction5 = Transaction.objects.create(
+ order_id='order id 5',
+ bank_transaction_id='bank_id_5',
+ amount=5)
+ transaction5.items.add(item5)
+ # the same but for the other regie
+ item5_bis = BasketItem.objects.create(
+ user=user,
+ regie=regie_with_webservice_url,
+ subject='item 5 bis',
+ source_url='http://example.net/5_bis',
+ amount=55555,
+ payment_date=date_in_past,
+ notification_date=None)
+ transaction5_bis = Transaction.objects.create(
+ order_id='order id 5 bis',
+ bank_transaction_id='bank_id_5_bis',
+ amount=55555)
+ transaction5_bis.items.add(item5_bis)
+
+ # item with payment_date, no notification_date, but a cancellation_date, in the past
+ # => not displayed
+ item6 = BasketItem.objects.create(
+ user=user,
+ regie=regie,
+ subject='item 6',
+ source_url='http://example.net/6',
+ amount=6,
+ payment_date=date_in_past,
+ notification_date=None,
+ cancellation_date=date_now)
+ transaction6 = Transaction.objects.create(
+ order_id='order id 6',
+ bank_transaction_id='bank_id_6',
+ amount=6)
+ transaction6.items.add(item6)
+
+ app = login(app)
+ resp = app.get('/manage/lingo/payments/error/', status=200)
+ assert list(resp.context['object_list']) == [item5, item3, item2]
+ assert '' % item5.source_url in resp.text
+ assert '' % item3.source_url in resp.text
+ assert '' % item2.source_url not in resp.text
+ assert '/manage/lingo/?q=%s' % transaction5.bank_transaction_id in resp.text
+ assert '/manage/lingo/?q=%s' % transaction3.bank_transaction_id not in resp.text
+ assert '/manage/lingo/?q=%s' % transaction22.bank_transaction_id not in resp.text
+
+
def test_configure_tipi_cell(app, admin_user):
page = Page(title='tipi', slug='tipi', template_name='standard')
page.save()