0004-lingo-allow-requiring-individual-payment-for-regie-4.patch
combo/apps/lingo/forms.py | ||
---|---|---|
87 | 87 |
class Meta: |
88 | 88 |
model = Regie |
89 | 89 |
fields = ['label', 'slug', 'description', 'payment_backend', 'is_default', |
90 |
'webservice_url', 'extra_fees_ws_url', 'payment_min_amount', 'text_on_success'] |
|
90 |
'webservice_url', 'extra_fees_ws_url', 'payment_min_amount', 'text_on_success', |
|
91 |
'individual_payment'] |
|
91 | 92 | |
92 | 93 |
def __init__(self, *args, **kwargs): |
93 | 94 |
super(RegieForm, self).__init__(*args, **kwargs) |
combo/apps/lingo/migrations/0041_regie_individual_payment.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
# Generated by Django 1.11.18 on 2020-09-30 16:10 |
|
3 |
from __future__ import unicode_literals |
|
4 | ||
5 |
from django.db import migrations, models |
|
6 | ||
7 | ||
8 |
class Migration(migrations.Migration): |
|
9 | ||
10 |
dependencies = [ |
|
11 |
('lingo', '0040_auto_20200608_1222'), |
|
12 |
] |
|
13 | ||
14 |
operations = [ |
|
15 |
migrations.AddField( |
|
16 |
model_name='regie', |
|
17 |
name='individual_payment', |
|
18 |
field=models.BooleanField(default=False, verbose_name='Basket items must be paid individually'), |
|
19 |
), |
|
20 |
] |
combo/apps/lingo/models.py | ||
---|---|---|
145 | 145 |
payment_backend = models.ForeignKey( |
146 | 146 |
PaymentBackend, on_delete=models.CASCADE, verbose_name=_('Payment backend')) |
147 | 147 |
transaction_options = JSONField(blank=True, verbose_name=_('Transaction Options')) |
148 |
individual_payment = models.BooleanField( |
|
149 |
default=False, verbose_name=_('Basket items must be paid individually') |
|
150 |
) |
|
148 | 151 | |
149 | 152 |
def is_remote(self): |
150 | 153 |
return self.webservice_url != '' |
combo/apps/lingo/templates/lingo/combo/basket.html | ||
---|---|---|
4 | 4 |
<h2>{% trans "Basket" %}</h2> |
5 | 5 |
{% for regie_info in regies %} |
6 | 6 |
{% if regies|length != 1 %}<h3 class="regie-name">{{regie_info.regie.label}}</h3>{% endif %} |
7 |
<form action="{% url 'lingo-pay' %}" method="POST"> |
|
8 |
{% csrf_token %} |
|
9 |
<input type="hidden" name="next_url" value="{{ cell.page.get_online_url }}" /> |
|
10 |
<input type="hidden" name="regie" value="{{regie_info.regie.id}}" /> |
|
11 |
<ul> |
|
7 |
<ul class="payment-items"> |
|
12 | 8 |
{% for item in regie_info.items %} |
13 | 9 |
<li><a {% if item.source_url %}href="{{ item.source_url }}{% endif %}">{{ item.subject }}</a>: {{ item.amount }} € |
14 | 10 |
{% if item.user_cancellable %} |
15 | 11 |
<a rel="popup" href="{% url 'lingo-cancel-item' pk=item.id %}">({% trans 'remove' %})</a> |
16 | 12 |
{% endif %} |
13 |
{% if regie_info.regie.individual_payment %} |
|
14 |
<a id="{{ item.pk }}" class="button individual-item" href="{{ item.payment_url }}?next_url={{ cell.page.get_online_url }}">{% trans "Pay" %}</a> |
|
15 |
{% endif %} |
|
17 | 16 |
</li> |
18 | 17 |
{% endfor %} |
18 |
{% if not regie_info.regie.individual_payment %} |
|
19 | 19 |
<li><strong>{% trans "Total:" %}</strong> {{ regie_info.total }} €</li> |
20 |
{% endif %} |
|
20 | 21 |
</ul> |
22 | ||
23 |
{% if not regie_info.regie.individual_payment %} |
|
24 |
<form action="{% url 'lingo-pay' %}" method="POST"> |
|
25 |
{% csrf_token %} |
|
26 |
<input type="hidden" name="next_url" value="{{ cell.page.get_online_url }}" /> |
|
27 |
<input type="hidden" name="regie" value="{{regie_info.regie.id}}" /> |
|
21 | 28 |
<button>{% trans "Pay" %}</button> |
22 | 29 |
</form> |
30 |
{% endif %} |
|
31 | ||
23 | 32 |
{% endfor %} |
24 | 33 |
{% endif %} |
25 | 34 |
{% endblock %} |
combo/apps/lingo/views.py | ||
---|---|---|
457 | 457 |
regie.compute_extra_fees(user=user) |
458 | 458 |
items = BasketItem.get_items_to_be_paid(user=user).filter(regie=regie) |
459 | 459 | |
460 |
if regie.individual_payment and len(items) > 1: |
|
461 |
messages.error(request, _('Grouping basket items is not allowed.')) |
|
462 |
return HttpResponseRedirect(next_url) |
|
463 | ||
460 | 464 |
if items: |
461 | 465 |
capture_date = items[0].capture_date |
462 | 466 |
for item in items: |
tests/test_lingo_cells.py | ||
---|---|---|
95 | 95 |
item.save() |
96 | 96 |
assert cell.get_badge(context) == {'badge': u'123.45€'} |
97 | 97 | |
98 |
def test_basket_cell_individual_payment(regie, user): |
|
99 |
regie.individual_payment = True |
|
100 |
regie.save() |
|
101 |
page = Page(title='xxx', slug='test_basket_cell', template_name='standard') |
|
102 |
page.save() |
|
103 |
cell = LingoBasketCell(page=page, placeholder='content', order=0) |
|
104 | ||
105 |
item = BasketItem.objects.create(user=user, regie=regie, subject='foo', amount=123) |
|
106 |
item2 = BasketItem.objects.create(user=user, regie=regie, subject='bar', amount=123) |
|
107 | ||
108 |
context = {'request': RequestFactory(user=user).get('/')} |
|
109 |
context['request'].user = user |
|
110 |
assert cell.is_relevant(context) |
|
111 | ||
112 |
content = cell.render(context) |
|
113 |
assert content.count('Pay') == 2 |
|
114 |
assert item.payment_url in content |
|
115 |
assert not 'Total' in content |
|
116 | ||
98 | 117 |
def test_recent_transaction_cell(regie, user): |
99 | 118 |
page = Page(title='xxx', slug='test_basket_cell', template_name='standard') |
100 | 119 |
page.save() |
tests/test_lingo_payment.py | ||
---|---|---|
1531 | 1531 |
else: |
1532 | 1532 |
assert transaction.bank_transaction_date == transaction_date |
1533 | 1533 |
assert 'new transaction_date for transaction' in caplog.text |
1534 | ||
1535 | ||
1536 |
def test_successfull_items_individual_payment(app, basket_page, regie, user): |
|
1537 |
regie.individual_payment = True |
|
1538 |
regie.save() |
|
1539 |
item = BasketItem.objects.create(user=user, regie=regie, amount=42, subject='foo item') |
|
1540 |
item2 = BasketItem.objects.create(user=user, regie=regie, amount=84, subject='bar item') |
|
1541 | ||
1542 |
resp = login(app).get('/test_basket_cell/') |
|
1543 |
assert 'foo item' in resp.text |
|
1544 |
assert 'bar item' in resp.text |
|
1545 | ||
1546 |
resp = resp.click('Pay', href=item.payment_url) |
|
1547 | ||
1548 |
# successful payment |
|
1549 |
qs = urlparse.parse_qs(urlparse.urlparse(resp.location).query) |
|
1550 |
args = {'transaction_id': qs['transaction_id'][0], 'signed': True, 'ok': True, 'reason': 'Paid'} |
|
1551 |
with mock.patch('combo.utils.requests_wrapper.RequestsSession.request') as request: |
|
1552 |
resp = app.get(get_url(True, 'lingo-callback', regie), params=args) |
|
1553 | ||
1554 |
resp = app.get('/test_basket_cell/') |
|
1555 |
assert 'foo item' not in resp.text |
|
1556 |
assert 'bar item' in resp.text |
|
1534 |
- |