combo/tests/test_lingo_manager.py

693 lines
25 KiB
Python

# -*- coding: utf-8 -*-
import datetime
from django.contrib.auth.models import User
from django.db import connection
from django.test.utils import CaptureQueriesContext
from django.utils.timezone import now
from django.utils.timezone import make_aware
import mock
import pytest
import eopayment
from combo.data.models import Page
from combo.apps.lingo.models import (Regie, BasketItem, Transaction, ActiveItems,
TipiPaymentFormCell, PaymentBackend)
from decimal import Decimal
pytestmark = pytest.mark.django_db
@pytest.fixture
def payment_backend():
return PaymentBackend.objects.create(
label='test1', slug='test1', service='dummy', service_options={'siret': '1234'})
def login(app, username='admin', password='admin'):
login_page = app.get('/login/')
login_form = login_page.forms[0]
login_form['username'] = username
login_form['password'] = password
resp = login_form.submit()
assert resp.status_int == 302
return app
def test_access(app, admin_user):
app = login(app)
resp = app.get('/manage/', status=200)
assert '/manage/lingo/' in resp.text
def test_add_regie(app, admin_user, payment_backend):
Regie.objects.all().delete()
app = login(app)
resp = app.get('/manage/lingo/regies/', status=200)
resp = resp.click('New')
assert '/manage/lingo/regies/' in resp.text
resp.forms[0]['label'] = 'Test'
resp.forms[0]['slug'] = 'test'
resp.forms[0]['description'] = 'description'
resp.forms[0]['payment_backend'] = payment_backend.pk
resp = resp.forms[0].submit()
assert resp.location.endswith('/manage/lingo/regies/')
assert Regie.objects.count() == 1
regie = Regie.objects.all()[0]
assert regie.label == 'Test'
assert regie.is_default is True
def test_edit_regie(app, admin_user, payment_backend):
test_add_regie(app, admin_user, payment_backend)
app = login(app)
resp = app.get('/manage/lingo/regies/', status=200)
resp = resp.click('Test')
assert '/manage/lingo/regies/' in resp.text
resp.forms[0]['description'] = 'other description'
resp = resp.forms[0].submit()
assert resp.location.endswith('/manage/lingo/regies/')
assert Regie.objects.count() == 1
regie = Regie.objects.all()[0]
assert regie.description == 'other description'
def test_edit_regie_dynamic_backend_fields(app, admin_user):
payment_backend = PaymentBackend.objects.create(
label='test1', slug='test1', service='systempayv2', service_options={'siret': '1234'})
regie = Regie.objects.create(
label='test-regie', slug='test-regie', payment_backend=payment_backend)
assert regie.transaction_options == {}
app = login(app)
resp = app.get('/manage/lingo/regies/%s/edit' % regie.pk, status=200)
resp.forms[0]['label'] = 'test-regie'
resp.forms[0]['slug'] = 'test-regie'
resp.forms[0]['description'] = 'description'
resp.forms[0]['payment_backend'] = payment_backend.pk
assert resp.forms[0]['manual_validation'].checked is False
resp.forms[0]['manual_validation'] = True
resp = resp.forms[0].submit()
assert Regie.objects.count() == 1
regie = Regie.objects.get(slug='test-regie')
assert regie.transaction_options['manual_validation']
# No dynamic fields if no backend capabilities
payment_backend_dummy = PaymentBackend.objects.create(
label='test1', slug='test-dummy', service='dummy', service_options={'siret': '1234'})
regie_dummy = Regie.objects.create(
label='test-regie-2', slug='test-regie-2', payment_backend=payment_backend_dummy)
resp = app.get('/manage/lingo/regies/%s/edit' % regie_dummy.pk, status=200)
assert 'manual_validation' not in resp.forms[0].fields
resp.forms[0]['label'] = 'Test'
resp.forms[0]['slug'] = 'test-regie-2'
resp.forms[0]['description'] = 'description'
resp.forms[0]['payment_backend'] = payment_backend_dummy.pk
resp = resp.forms[0].submit()
regie_dummy = Regie.objects.get(slug='test-regie-2')
assert regie_dummy.transaction_options == {}
# Change backend
resp = app.get('/manage/lingo/regies/%s/edit' % regie.pk, status=200)
resp.forms[0]['label'] = 'test-regie'
resp.forms[0]['slug'] = 'test-regie'
resp.forms[0]['description'] = 'description'
resp.forms[0]['payment_backend'] = payment_backend_dummy.pk
assert resp.forms[0]['manual_validation'].checked is True
resp.forms[0]['manual_validation'] = True
resp = resp.forms[0].submit()
regie = Regie.objects.get(slug='test-regie')
assert regie.transaction_options == {}
def test_delete_regie(app, admin_user, payment_backend):
test_add_regie(app, admin_user, payment_backend)
app = login(app)
resp = app.get('/manage/lingo/regies/', status=200)
resp = resp.click('remove')
assert 'Are you sure you want to delete this?' in resp.text
resp = resp.forms[0].submit()
assert Regie.objects.count() == 0
def test_add_second_regie(app, admin_user, payment_backend):
test_add_regie(app, admin_user, payment_backend)
regie = Regie.objects.all()[0]
app = login(app)
resp = app.get('/manage/lingo/regies/', status=200)
resp = resp.click('New')
resp.forms[0]['label'] = 'Test2'
resp.forms[0]['slug'] = 'test2'
resp.forms[0]['description'] = 'description'
resp.forms[0]['payment_backend'] = payment_backend.pk
resp = resp.forms[0].submit()
assert resp.location.endswith('/manage/lingo/regies/')
assert Regie.objects.count() == 2
assert Regie.objects.get(id=regie.id).is_default is True
assert Regie.objects.exclude(id=regie.id)[0].is_default is False
def test_download_transaction(app, admin_user, payment_backend):
test_add_regie(app, admin_user, payment_backend)
regie = Regie.objects.filter(slug='test')[0]
user = User.objects.create_user('dimebag', 'dime@bag.pan', 'pwd')
user.last_name = u'Darëll'
user.first_name = 'Dimebag'
user.save()
b_item = BasketItem.objects.create(user=user, regie=regie, subject='it\'s a subject',
source_url='http://example.net', amount=18.5)
trans1 = Transaction.objects.create(regie=regie, remote_items='remote items lol', order_id='1', user=user,
bank_transaction_id='567', status=eopayment.PAID)
trans2 = Transaction.objects.create(regie=regie, remote_items='remote items omg', order_id='2', user=user,
bank_transaction_id='136', status=eopayment.PAID)
trans1.items.set([b_item])
trans1.start_date = make_aware(datetime.datetime(2019, 7, 29))
trans1.save()
trans2.items.set([b_item])
trans2.start_date = make_aware(datetime.datetime(2019, 10, 1))
trans2.save()
app = login(app)
resp = app.get('/manage/lingo/transactions/download-csv/', status=200)
resp.forms[0]['start_date'] = datetime.date(2019, 10, 1)
resp.forms[0]['end_date'] = datetime.date(2019, 11, 1)
resp = resp.forms[0].submit()
content = [i for i in [item.split(',') for item in resp.text.split('\r\n')] if len(i) > 1]
assert len(content) == 1
row = content[0]
assert row[0] == '2'
assert row[1] == trans2.bank_transaction_id
assert row[3] == '%s %s' % (user.first_name, user.last_name)
assert Decimal(row[4]) == Decimal(trans2.amount)
assert row[5] == b_item.subject
assert Decimal(row[6]) == b_item.amount
resp = app.get('/manage/lingo/transactions/download-csv/', status=200)
resp.forms[0]['start_date'] = datetime.date(2019, 7, 1)
resp.forms[0]['end_date'] = datetime.date(2019, 11, 1)
resp = resp.forms[0].submit()
content = [i for i in [item.split(',') for item in resp.text.split('\r\n')] if len(i) > 1]
assert len(content) == 2
row = content[0]
assert row[0] == '2'
row = content[1]
assert row[0] == '1'
assert row[1] == trans1.bank_transaction_id
assert row[3] == '%s %s' % (user.first_name, user.last_name)
assert Decimal(row[4]) == Decimal(trans1.amount)
assert row[5] == b_item.subject
assert Decimal(row[6]) == b_item.amount
def test_transactions_search(app, admin_user):
for i in range(50):
Transaction(status=eopayment.PAID,
order_id='order id %s' % (i+1),
bank_transaction_id='bank id %s' % (i+1),
amount=1+i).save()
app = login(app)
with CaptureQueriesContext(connection) as ctx:
resp = app.get('/manage/lingo/', status=200)
assert resp.text.count('<tr') == 11
assert len(ctx.captured_queries) == 5
resp.form['q'] = 'order id 16'
resp = resp.form.submit()
assert resp.text.count('<tr') == 2
resp.form['q'] = now().strftime('%d/%m/%Y')
resp = resp.form.submit()
assert resp.text.count('<tr') == 11
resp.form['q'] = (now() + datetime.timedelta(days=2)).strftime('%d/%m%/Y')
resp = resp.form.submit()
assert resp.text.count('<tr') == 0
assert 'No transactions found matching' in resp.text
def test_basketitem_error_list(app, admin_user, payment_backend):
regie = Regie.objects.create(
label='test-regie',
slug='test-regie',
payment_backend=payment_backend)
user = User.objects.create_user('dimebag', 'dime@bag.pan', 'pwd')
regie_with_webservice_url = Regie.objects.create(
label='test-regie2',
slug='test-regie2',
payment_backend=payment_backend,
webservice_url='http://example.net')
date_now = now()
date_in_past = date_now - datetime.timedelta(minutes=300)
# item with payment_date and notification_date, status PAID
# => 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 but no notification_date, in the past
# => displayed
item6 = BasketItem.objects.create(
user=user,
regie=regie,
subject='item 6',
source_url='', # without source_url
amount=6,
payment_date=date_in_past,
notification_date=None)
transaction6 = Transaction.objects.create(
order_id='order id 6',
bank_transaction_id='bank_id_6',
amount=6)
transaction6.items.add(item6)
# item with payment_date, no notification_date, but a cancellation_date, in the past
# => not displayed
item7 = BasketItem.objects.create(
user=user,
regie=regie,
subject='item 7',
source_url='http://example.net/7',
amount=7,
payment_date=date_in_past,
notification_date=None,
cancellation_date=date_now)
transaction7 = Transaction.objects.create(
order_id='order id 7',
bank_transaction_id='bank_id_7',
amount=7)
transaction7.items.add(item7)
app = login(app)
resp = app.get('/manage/lingo/payments/error/', status=200)
assert list(resp.context['object_list']) == [item6, item5, item3, item2]
assert '<a href="%s">' % item6.source_url not in resp.text
assert '<a href="%s">' % item5.source_url in resp.text
assert '<a href="%s">' % item3.source_url in resp.text
assert '<a href="%s">' % item2.source_url not in resp.text
assert '/manage/lingo/?q=%s' % transaction6.bank_transaction_id 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
assert '/manage/lingo/item/%s/mark-as-notified/' % item6.pk not in resp.text
assert '/manage/lingo/item/%s/mark-as-notified/' % item5.pk in resp.text
assert '/manage/lingo/item/%s/mark-as-notified/' % item3.pk not in resp.text
assert '/manage/lingo/item/%s/mark-as-notified/' % item2.pk not in resp.text
def test_basketitem_error_list_search(app, admin_user, payment_backend):
regie = Regie.objects.create(
label='test-regie', slug='test-regie', payment_backend=payment_backend)
user = User.objects.create_user('dimebag', 'dime@bag.pan', 'pwd')
item = BasketItem.objects.create(
user=user,
regie=regie,
subject='item 1',
source_url='http://example.net/1',
amount=1)
transaction = Transaction.objects.create(
status=eopayment.ERROR,
order_id='order id 1',
bank_transaction_id='bank_id_1',
amount=1)
transaction.items.add(item)
app = login(app)
resp = app.get('/manage/lingo/payments/error/', status=200)
assert list(resp.context['object_list']) == [item]
resp.form['q'] = 'item 42'
resp = resp.form.submit()
assert list(resp.context['object_list']) == []
resp.form['q'] = 'item 1'
resp = resp.form.submit()
assert list(resp.context['object_list']) == [item]
resp.form['q'] = 'item'
resp = resp.form.submit()
assert list(resp.context['object_list']) == [item]
def test_basketitem_mark_as_notified(app, admin_user, payment_backend):
regie = Regie.objects.create(
label='test-regie', slug='test-regie', payment_backend=payment_backend)
user = User.objects.create_user('dimebag', 'dime@bag.pan', 'pwd')
date_now = now()
date_in_past = date_now - datetime.timedelta(minutes=300)
item = BasketItem.objects.create(
user=user,
regie=regie,
subject='item',
source_url='http://example.net/',
amount=42,
payment_date=date_in_past,
notification_date=date_in_past)
app = login(app)
# notification_date is not None
resp = app.get('/manage/lingo/item/%s/mark-as-notified/' % item.pk, status=404)
# payment_date is not old enough
item.notification_date = None
item.payment_date = date_now
item.save()
resp = app.get('/manage/lingo/item/%s/mark-as-notified/' % item.pk, status=404)
# cancellation_date is not None
item.payment_date = date_in_past
item.cancellation_date = date_now
item.save()
resp = app.get('/manage/lingo/item/%s/mark-as-notified/' % item.pk, status=404)
# notification_date and cancellation_date are None, payment_date is old enough
# but webservice_url is not None
item.cancellation_date = None
item.save()
regie.webservice_url = 'http://example.net'
regie.save()
resp = app.get('/manage/lingo/item/%s/mark-as-notified/' % item.pk, status=404)
# notification_date and cancellation_date are None, payment_date is old enough
# but source_url is not set
regie.webservice_url = ''
regie.save()
item.source_url = ''
item.save()
resp = app.get('/manage/lingo/item/%s/mark-as-notified/' % item.pk, status=404)
# source_url is set
item.source_url = 'http://example.com'
item.save()
with mock.patch('combo.apps.lingo.models.BasketItem.notify') as mock_notify:
resp = app.get('/manage/lingo/item/%s/mark-as-notified/' % item.pk, status=302)
assert mock_notify.call_args_list == []
assert resp.location.endswith('/manage/lingo/payments/error/')
item.refresh_from_db()
assert item.notification_date is not None
def test_configure_tipi_cell(app, admin_user):
page = Page(title='tipi', slug='tipi', template_name='standard')
page.save()
app = login(app)
resp = app.get('/manage/pages/%s/' % page.id, status=200)
cell = TipiPaymentFormCell(title='Test payment', page=page, placeholder='content', order=0)
cell.save()
resp = app.get('/manage/pages/%s/' % page.id, status=200)
assert resp.text.count('Exer:') == 1
assert resp.text.count('IDPCE:') == 1
assert resp.text.count('IDLIGNE:') == 1
assert resp.text.count('ROLREC:') == 1
assert resp.text.count('ROLDEB:') == 1
assert resp.text.count('ROLDET:') == 1
def test_configure_invoices_cell(app, admin_user, payment_backend):
page = Page(title='xxx', slug='test', template_name='standard')
page.save()
app = login(app)
resp = app.get('/manage/pages/%s/' % page.id, status=200)
# 1 occurence of tipi payment form should be present, and no other cells
assert resp.text.count('lingo') == 1
assert resp.text.count('lingo_tipipaymentformcell') == 1
regie = Regie()
regie.label = 'Test'
regie.slug = 'test'
regie.description = 'test'
regie.payment_backend = payment_backend
regie.save()
resp = app.get('/manage/pages/%s/' % page.id, status=200)
assert 'lingorecenttransactionscell' in resp.text # because there's a regie
assert not 'lingo_activeitems' in resp.text # because there's no webservice
regie.webservice_url = 'http://example.net/'
regie.save()
resp = app.get('/manage/pages/%s/' % page.id, status=200)
assert 'lingo_activeitems' in resp.text
cell = ActiveItems(regie='remote', page=page, placeholder='content', order=0)
cell.save()
resp = app.get('/manage/pages/%s/' % page.id, status=200)
assert 'clingo_activeitems-%s-regie' % cell.id not in resp.form.fields
assert 'clingo_activeitems-%s-title' % cell.id in resp.form.fields
regie2 = Regie()
regie2.label = 'Test2'
regie2.slug = 'test2'
regie2.description = 'test2'
regie2.webservice_url = 'http://example.net/'
regie2.payment_backend = payment_backend
regie2.save()
resp = app.get('/manage/pages/%s/' % page.id, status=200)
assert 'clingo_activeitems-%s-regie' % cell.id in resp.form.fields
assert 'clingo_activeitems-%s-title' % cell.id in resp.form.fields
options = [x[2] for x in resp.form['clingo_activeitems-%s-regie' % cell.id].options]
assert 'All' in options
assert regie.label in options
assert regie2.label in options
resp.form['clingo_activeitems-%s-regie' % cell.id].value = 'test2'
resp.form.submit()
assert ActiveItems.objects.get(id=cell.id).regie == regie2.slug
def test_payment_backend_list(app, admin_user):
PaymentBackend.objects.create(label='label1', slug='slug1')
PaymentBackend.objects.create(label='label2', slug='slug2')
app = login(app)
resp = app.get('/manage/lingo/paymentbackends/', status=200)
assert '/manage/lingo/paymentbackends/add' in resp.text
assert 'label1' in resp.text
assert 'label2' in resp.text
for payment_backend in PaymentBackend.objects.all():
assert '/manage/lingo/paymentbackends/%s' % payment_backend.pk in resp.text
assert '/manage/lingo/paymentbackends/%s/delete' % payment_backend.pk in resp.text
def test_add_payment_backend(app, admin_user):
assert not PaymentBackend.objects.count()
app = login(app)
resp = app.get('/manage/lingo/paymentbackends/add/', status=200)
assert '/manage/lingo/paymentbackends/' in resp.text
resp.form['label'] = 'Test'
resp.form['slug'] = 'test-add'
resp.form['service'] = 'dummy'
resp = resp.forms[0].submit()
assert PaymentBackend.objects.count() == 1
payment_backend = PaymentBackend.objects.get(slug='test-add')
assert payment_backend.label == 'Test'
assert payment_backend.service_options == {}
backend = PaymentBackend.objects.first()
assert resp.location.endswith('/manage/lingo/paymentbackends/%s/edit' % backend.pk)
resp = resp.follow()
assert 'fill additional backend parameters' in resp.text
def test_edit_payment_backend(app, admin_user):
payment_backend = PaymentBackend.objects.create(label='label1', slug='slug1', service='dummy')
app = login(app)
resp = app.get('/manage/lingo/paymentbackends/%s/edit' % payment_backend.pk, status=200)
assert '/manage/lingo/paymentbackends/' in resp.text
# service cannot be changed
assert 'disabled' in resp.form['service'].attrs
# deprecated parameters are not shown
assert not 'next_url' in resp.form.fields
assert not 'direct_notification_url' in resp.form.fields
# default values become initial values
assert resp.form['siret']._value == '1234'
resp.form['label'] = 'label1-modified'
resp.form['siret'] = '12345'
resp.form['consider_all_response_signed'] = True
resp.form['number'] = 12
resp.form['choice'] = 'b'
resp.form['choices'] = ['a', 'b']
resp = resp.form.submit()
assert resp.location.endswith('/manage/lingo/paymentbackends/')
payment_backend = PaymentBackend.objects.get(slug='slug1')
assert payment_backend.label == 'label1-modified'
assert payment_backend.service_options['siret'] == '12345'
assert payment_backend.service_options['consider_all_response_signed'] == True
assert payment_backend.service_options['number'] == 12
assert payment_backend.service_options['choice'] == 'b'
assert payment_backend.service_options['choices'] == ['a', 'b']
def test_edit_payment_backend_validation(app, admin_user):
payment_backend = PaymentBackend.objects.create(label='label1', slug='slug1', service='dummy')
app = login(app)
resp = app.get('/manage/lingo/paymentbackends/%s/edit' % payment_backend.pk)
# required field
resp.form['siret'] = ''
resp = resp.form.submit()
assert 'This field is required.' in resp.text
assert resp.form['siret']._value == None
# validation function
resp = app.get('/manage/lingo/paymentbackends/%s/edit' % payment_backend.pk)
resp.form['siret'] = 'a'
resp = resp.form.submit()
assert 'must be a number' in resp.text
assert resp.form['siret']._value == 'a'
# wrong type
resp = app.get('/manage/lingo/paymentbackends/%s/edit' % payment_backend.pk)
resp.form['number'] = 'a'
resp = resp.form.submit()
assert 'Enter a whole number.' in resp.text
assert resp.form['number']._value == 'a'
# not in choices
resp = app.get('/manage/lingo/paymentbackends/%s/edit' % payment_backend.pk)
resp.form['choice'].force_value('c')
resp = resp.form.submit()
assert 'c is not one of the available choices' in resp.text
def test_use_old_service_options_safely(app, admin_user):
PaymentBackend(service='dummy', service_options='xx').get_payment()
PaymentBackend(service='dummy', service_options='"xx"').get_payment()
PaymentBackend(service='dummy', service_options=None).get_payment()