0001-lingo-allow-arbitrary-date-for-deferred-payment-2704.patch
combo/apps/lingo/migrations/0032_basketitem_capture_date.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
# Generated by Django 1.11.15 on 2018-10-05 15:33 |
|
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', '0031_basketitem_waiting_date'), |
|
12 |
] |
|
13 | ||
14 |
operations = [ |
|
15 |
migrations.AddField( |
|
16 |
model_name='basketitem', |
|
17 |
name='capture_date', |
|
18 |
field=models.DateField(null=True), |
|
19 |
), |
|
20 |
] |
combo/apps/lingo/models.py | ||
---|---|---|
327 | 327 |
waiting_date = models.DateTimeField(null=True) |
328 | 328 |
payment_date = models.DateTimeField(null=True) |
329 | 329 |
notification_date = models.DateTimeField(null=True) |
330 |
capture_date = models.DateField(null=True) |
|
330 | 331 | |
331 | 332 |
class Meta: |
332 | 333 |
ordering = ['regie', 'extra_fee', 'subject'] |
combo/apps/lingo/views.py | ||
---|---|---|
26 | 26 |
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest |
27 | 27 |
from django.http import HttpResponseForbidden, Http404, JsonResponse |
28 | 28 |
from django.template.response import TemplateResponse |
29 |
from django.utils import timezone |
|
29 |
from django.utils import timezone, dateparse
|
|
30 | 30 |
from django.utils.encoding import force_text |
31 | 31 |
from django.views.decorators.csrf import csrf_exempt |
32 | 32 |
from django.views.generic import View, DetailView, ListView, TemplateView |
... | ... | |
161 | 161 |
item.subject = request_body.get('display_name') |
162 | 162 |
item.source_url = request_body.get('url') or '' |
163 | 163 | |
164 |
if 'capture_date' in request_body: |
|
165 |
try: |
|
166 |
# parse_date returns None when the string format is invalid |
|
167 |
capture_date_err = False |
|
168 |
item.capture_date = dateparse.parse_date(request_body['capture_date']) |
|
169 |
except TypeError: |
|
170 |
capture_date_err = True |
|
171 |
finally: |
|
172 |
if item.capture_date is None or capture_date_err: |
|
173 |
return HttpResponseBadRequest( |
|
174 |
_(u'Bad format for capture date, it should be yyyy-mm-dd.')) |
|
175 | ||
164 | 176 |
item.save() |
165 | 177 |
item.regie.compute_extra_fees(user=item.user) |
166 | 178 | |
... | ... | |
328 | 340 |
transaction.amount = total_amount |
329 | 341 | |
330 | 342 |
payment = get_eopayment_object(request, regie) |
331 | ||
332 |
(order_id, kind, data) = payment.request(total_amount, email=email, |
|
333 |
first_name=firstname, |
|
334 |
last_name=lastname) |
|
343 |
kwargs = { |
|
344 |
'email': email, 'first_name': firstname, 'last_name': lastname |
|
345 |
} |
|
346 |
if items: |
|
347 |
capture_date = items[0].capture_date |
|
348 |
if capture_date: |
|
349 |
kwargs['capture_date'] = capture_date.isoformat() |
|
350 |
(order_id, kind, data) = payment.request(total_amount, **kwargs) |
|
335 | 351 |
logger = logging.getLogger(__name__) |
336 | 352 |
logger.info(u'emitted payment request with id %s', smart_text(order_id), extra={ |
337 | 353 |
'eopayment_order_id': smart_text(order_id), 'eopayment_data': repr(data)}) |
... | ... | |
359 | 375 |
user = request.user if request.user.is_authenticated() else None |
360 | 376 |
remote_items = [] |
361 | 377 |
items = [] |
378 |
invalid_grouping_msg = _(u'Invalid grouping for basket items.') |
|
362 | 379 |
if regie_id and Regie.objects.get(pk=regie_id).is_remote(): |
363 | 380 |
regie = Regie.objects.get(pk=regie_id) |
364 | 381 |
# get all items data from regie webservice |
... | ... | |
375 | 392 |
regie_id = items[0].regie_id |
376 | 393 |
for item in items: |
377 | 394 |
if item.regie_id != regie_id: |
378 |
messages.error(request, _(u'Invalid grouping for basket items.'))
|
|
395 |
messages.error(request, invalid_grouping_msg)
|
|
379 | 396 |
return HttpResponseRedirect(next_url) |
380 | 397 | |
381 | 398 |
regie = Regie.objects.get(id=regie_id) |
382 | 399 |
regie.compute_extra_fees(user=user) |
383 | 400 |
items = BasketItem.get_items_to_be_paid(user=user).filter(regie=regie) |
384 | 401 | |
402 |
if items: |
|
403 |
capture_date = items[0].capture_date |
|
404 |
for item in items: |
|
405 |
if item.capture_date != capture_date: |
|
406 |
messages.error(request, invalid_grouping_msg) |
|
407 |
return HttpResponseRedirect(next_url) |
|
408 | ||
385 | 409 |
if not user and not request.POST.get('email'): |
386 | 410 |
messages.warning(request, _(u'You must give an email address.')) |
387 | 411 |
return HttpResponseRedirect(request.POST.get('item_url')) |
tests/test_lingo_payment.py | ||
---|---|---|
233 | 233 |
resp = app.post_json(url, params=data, status=400) |
234 | 234 |
assert resp.text == 'Unknown regie' |
235 | 235 | |
236 | ||
237 |
def test_basket_item_with_capture_date(app, user, regie, basket_page, monkeypatch): |
|
238 |
User.objects.get_or_create(email=user.email) |
|
239 |
url = '%s?email=%s' % (reverse('api-add-basket-item'), user.email) |
|
240 |
capture_date = timezone.now().date() |
|
241 |
data = { |
|
242 |
'amount': 10, 'capture_date': capture_date.isoformat(), |
|
243 |
'display_name': 'test item' |
|
244 |
} |
|
245 |
url = sign_url(url, settings.LINGO_API_SIGN_KEY) |
|
246 |
resp = app.post_json(url, params=data) |
|
247 |
assert resp.status_code == 200 |
|
248 |
assert BasketItem.objects.all()[0].capture_date == capture_date |
|
249 | ||
250 |
resp = login(app).get('/test_basket_cell/') |
|
251 |
import eopayment |
|
252 |
eopayment_mock = mock.Mock( |
|
253 |
return_value=('orderid', eopayment.URL, 'http://dummy-payment.demo.entrouvert.com/')) |
|
254 |
monkeypatch.setattr(eopayment.Payment, 'request', eopayment_mock) |
|
255 |
resp = resp.form.submit() |
|
256 |
assert resp.status_code == 302 |
|
257 |
location = urlparse.urlparse(resp.location) |
|
258 |
assert location.path == '/' |
|
259 |
assert location.hostname == 'dummy-payment.demo.entrouvert.com' |
|
260 |
eopayment_mock.assert_called_once_with( |
|
261 |
Decimal(10), email=user.email, first_name=user.first_name, last_name=user.last_name, |
|
262 |
capture_date=capture_date.isoformat()) |
|
263 | ||
264 | ||
265 |
@pytest.mark.parametrize("invalid_capture_date", [8, '', 'not-a-date']) |
|
266 |
def test_add_basket_capture_date_format(app, user, regie, invalid_capture_date): |
|
267 |
url = '%s?email=%s' % (reverse('api-add-basket-item'), user.email) |
|
268 |
data = {'amount': 10, 'display_name': 'test item'} |
|
269 |
data['capture_date'] = invalid_capture_date |
|
270 |
url = sign_url(url, settings.LINGO_API_SIGN_KEY) |
|
271 |
resp = app.post_json(url, params=data, status=400) |
|
272 |
assert 'Bad format for capture date, it should be yyyy-mm-dd.' in resp.content |
|
273 | ||
274 | ||
275 |
def test_cant_pay_if_different_capture_date(app, basket_page, regie, user): |
|
276 |
capture1 = (timezone.now() + timedelta(days=1)).date() |
|
277 |
capture2 = (timezone.now() + timedelta(days=2)).date() |
|
278 |
items = { |
|
279 |
'item1': { |
|
280 |
'amount': '10.5', 'capture_date': capture1.isoformat(), |
|
281 |
'source_url': 'http://example.org/item/1' |
|
282 |
}, |
|
283 |
'item2': { |
|
284 |
'amount': '42', 'capture_date': capture2.isoformat(), |
|
285 |
'source_url': 'http://example.org/item/2'}, |
|
286 |
} |
|
287 |
b_items = [] |
|
288 |
for subject, details in items.items(): |
|
289 |
b_item = BasketItem.objects.create( |
|
290 |
user=user, regie=regie, subject=subject, **details) |
|
291 |
b_items.append(b_item.pk) |
|
292 | ||
293 |
resp = login(app).get('/test_basket_cell/') |
|
294 |
resp = resp.form.submit() |
|
295 |
assert resp.status_code == 302 |
|
296 |
assert urlparse.urlparse(resp.location).path == '/test_basket_cell/' |
|
297 |
resp = resp.follow() |
|
298 |
assert "Invalid grouping for basket items." in resp.content |
|
299 | ||
300 | ||
236 | 301 |
def test_pay_single_basket_item(app, key, regie, user, john_doe): |
237 | 302 |
page = Page(title='xxx', slug='index', template_name='standard') |
238 | 303 |
page.save() |
239 |
- |