Skip to content

Commit

Permalink
Merge branch 'a147484942466775_change_date_signed'
Browse files Browse the repository at this point in the history
  • Loading branch information
vmaksymiv committed Oct 4, 2016
2 parents 160c3ed + 206c2db commit 7f0fb34
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 8 deletions.
7 changes: 6 additions & 1 deletion openprocurement/contracting/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class Options:
roles = {
# 'edit': blacklist('id', 'date'),
'create': whitelist('rationale', 'rationale_ru', 'rationale_en', 'rationaleTypes', 'contractNumber'),
'edit': whitelist('rationale', 'rationale_ru', 'rationale_en', 'rationaleTypes', 'contractNumber', 'status'),
'edit': whitelist('rationale', 'rationale_ru', 'rationale_en', 'rationaleTypes', 'contractNumber', 'status', 'dateSigned'),
'view': schematics_default_role,
'embedded': schematics_embedded_role,
}
Expand All @@ -135,6 +135,11 @@ class Options:
'taxRate', 'fiscalYearExtension'],
required=True), min_size=1, required=True)
contractNumber = StringType()
dateSigned = IsoDateTimeType()

def validate_dateSigned(self, data, value):
if value and value > get_now():
raise ValidationError(u"Contract signature date can't be in the future")


@implementer(IContract)
Expand Down
3 changes: 2 additions & 1 deletion openprocurement/contracting/api/tests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from datetime import datetime

from openprocurement.api.utils import VERSION
from openprocurement.api.models import get_now


now = datetime.now()
Expand Down Expand Up @@ -97,7 +98,7 @@
u"amount": 238.0,
u"valueAddedTaxIncluded": True
},
u"dateSigned": u"2016-03-18T18:48:05.762961+02:00",
u"dateSigned": get_now().isoformat(),
u"awardID": u"8481d7eb01694c25b18658036c236c5d",
u"id": uuid4().hex,
u"contractID": u"UA-2016-03-18-000001-1",
Expand Down
127 changes: 125 additions & 2 deletions openprocurement/contracting/api/tests/change.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import unittest
from copy import deepcopy
from datetime import timedelta
from openprocurement.api.models import get_now
from openprocurement.contracting.api.tests.base import (
BaseWebTest, BaseContractContentWebTest, test_contract_data)
Expand Down Expand Up @@ -184,7 +185,7 @@ def test_create_change(self):
])

response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change['id'], self.contract_token),
{'data': {'status': 'active'}})
{'data': {'status': 'active', 'dateSigned': get_now().isoformat()}})
self.assertEqual(response.status, '200 OK')
self.assertEqual(response.json['data']['status'], 'active')

Expand Down Expand Up @@ -283,7 +284,7 @@ def test_patch_change(self):
self.assertEqual(response.status, '403 Forbidden')

response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change['id'], self.contract_token),
{'data': {'status': 'active'}})
{'data': {'status': 'active', 'dateSigned': get_now().isoformat()}})
self.assertEqual(response.status, '200 OK')
self.assertNotEqual(response.json['data']['date'], creation_date)
self.assertNotEqual(response.json['data']['date'], first_patch_date)
Expand All @@ -293,6 +294,128 @@ def test_patch_change(self):
{'data': {'status': 'pending'}}, status=403)
self.assertEqual(response.status, '403 Forbidden')

def test_change_date_signed(self):
response = self.app.post_json('/contracts/{}/changes?acc_token={}'.format(self.contract['id'], self.contract_token),
{'data': {'rationale': u'причина зміни укр',
'rationale_en': u'change cause en',
'rationaleTypes': ['priceReduction'],
'contractNumber': u'№ 146'}})
self.assertEqual(response.status, '201 Created')
self.assertEqual(response.content_type, 'application/json')
change = response.json['data']
self.assertEqual(change['status'], 'pending')
self.assertEqual(change['contractNumber'], u'№ 146')

self.app.authorization = ('Basic', ('broker', ''))
response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change['id'], self.contract_token),
{'data': {'status': 'active'}}, status=403)
self.assertEqual(response.status, '403 Forbidden')
self.assertEqual(response.json['errors'], [
{"location": "body", "name": "data", "description": "Can't update contract change status. 'dateSigned' is required."}
])

response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change['id'], self.contract_token),
{'data': {'dateSigned': "12-14-11"}}, status=422)
self.assertEqual(response.json['errors'], [
{"location": "body", "name": "dateSigned", "description": ["Could not parse 12-14-11. Should be ISO8601."]}
])

valid_date1_raw = get_now()
valid_date1 = valid_date1_raw.isoformat()
response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change['id'], self.contract_token),
{'data': {'dateSigned': valid_date1}})
self.assertEqual(response.status, '200 OK')
self.assertEqual(response.json['data']['dateSigned'], valid_date1)

one_day_in_past = (get_now() - timedelta(days=1)).isoformat()
response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change['id'], self.contract_token),
{'data': {'dateSigned': one_day_in_past}}, status=403)
self.assertIn("can't be earlier than contract dateSigned", response.json['errors'][0]["description"])


response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change['id'], self.contract_token),
{'data': {'status': 'active'}})
self.assertEqual(response.status, '200 OK')

response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change['id'], self.contract_token),
{'data': {'dateSigned': get_now().isoformat()}}, status=403)
self.assertEqual(response.json['errors'], [
{"location": "body", "name": "data", "description": "Can't update contract change in current (active) status"}
])

response = self.app.get('/contracts/{}/changes/{}'.format(self.contract['id'], change['id']))
change1 = response.json['data']
self.assertEqual(change1['dateSigned'], valid_date1)

response = self.app.post_json('/contracts/{}/changes?acc_token={}'.format(self.contract['id'], self.contract_token),
{'data': {'rationale': u'iнша причина зміни укр',
'rationale_en': u'another change cause en',
'rationaleTypes': ['priceReduction'],
'contractNumber': u'№ 147'}})
self.assertEqual(response.status, '201 Created')
self.assertEqual(response.content_type, 'application/json')
change2 = response.json['data']
self.assertEqual(change['status'], 'pending')

one_day_in_future = (get_now() + timedelta(days=1)).isoformat()
response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change2['id'], self.contract_token),
{'data': {'dateSigned': one_day_in_future}}, status=422)
self.assertEqual(response.json['errors'], [
{"location": "body", "name": "dateSigned", "description": [u"Contract signature date can't be in the future"]}
])

smaller_than_last_change = (valid_date1_raw - timedelta(seconds=1)).isoformat()
response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change2['id'], self.contract_token),
{'data': {'dateSigned': smaller_than_last_change}}, status=403)
self.assertEqual("Change dateSigned ({}) can't be earlier than last active change dateSigned ({})".format(smaller_than_last_change, valid_date1), response.json['errors'][0]["description"])

date = get_now().isoformat()
response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change2['id'], self.contract_token),
{'data': {'dateSigned': date}})
self.assertEqual(response.status, '200 OK')
self.assertEqual(response.json['data']['dateSigned'], date)

# date update request
valid_date2_raw = get_now()
valid_date2 = valid_date2_raw.isoformat()
response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change2['id'], self.contract_token),
{'data': {'dateSigned': valid_date2}})
self.assertEqual(response.status, '200 OK')
self.assertEqual(response.json['data']['dateSigned'], valid_date2)

response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change2['id'], self.contract_token),
{'data': {'status': 'active'}})
self.assertEqual(response.status, '200 OK')
self.assertEqual(response.json['data']['dateSigned'], valid_date2)

response = self.app.post_json('/contracts/{}/changes?acc_token={}'.format(self.contract['id'], self.contract_token),
{'data': {'rationale': u'третя причина зміни укр',
'rationale_en': u'third change cause en',
'rationaleTypes': ['priceReduction'],
'contractNumber': u'№ 148'}})
self.assertEqual(response.status, '201 Created')
change3 = response.json['data']
self.assertEqual(change['status'], 'pending')

smaller_than_last_change = (valid_date2_raw - timedelta(seconds=1)).isoformat()
response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change3['id'], self.contract_token),
{'data': {'dateSigned': smaller_than_last_change}}, status=403)
self.assertEqual("Change dateSigned ({}) can't be earlier than last active change dateSigned ({})".format(smaller_than_last_change, valid_date2), response.json['errors'][0]["description"])

date = get_now().isoformat()
response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change3['id'], self.contract_token),
{'data': {'dateSigned': date}})
self.assertEqual(response.status, '200 OK')
self.assertEqual(response.json['data']['dateSigned'], date)

response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change3['id'], self.contract_token),
{'data': {'status': 'active'}})
self.assertEqual(response.status, '200 OK')

response = self.app.patch_json('/contracts/{}?acc_token={}'.format(self.contract['id'], self.contract_token),
{'data': {'status': 'terminated', "amountPaid": {"amount": 15}}})
self.assertEqual(response.status, '200 OK')


def suite():
suite = unittest.TestSuite()
Expand Down
3 changes: 2 additions & 1 deletion openprocurement/contracting/api/tests/document.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import unittest
from email.header import Header
from openprocurement.api.models import get_now
from openprocurement.contracting.api.tests.base import BaseContractContentWebTest

from openprocurement.api.tests.document import MockConnection
Expand Down Expand Up @@ -374,7 +375,7 @@ def test_contract_change_document(self):
self.assertEqual(doc_id, response.json["data"]["id"])

response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change['id'], self.contract_token),
{'data': {'status': 'active'}})
{'data': {'status': 'active', 'dateSigned': get_now().isoformat()}})
self.assertEqual(response.status, '200 OK')

response = self.app.post('/contracts/{}/documents?acc_token={}'.format(
Expand Down
30 changes: 27 additions & 3 deletions openprocurement/contracting/api/views/change.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,39 @@ def patch(self):
""" Contract change edit """
change = self.request.validated['change']
data = self.request.validated['data']

if change.status == 'active':
self.request.errors.add('body', 'data', 'Can\'t update contract change in current ({}) status'.format(change.status))
self.request.errors.status = 403
return

if 'status' in data and data['status'] != change.status: # status change

if data['status'] != 'active':
self.request.errors.add('body', 'data', 'Can\'t update contract change in current ({}) status'.format(change.status))
if not data.get("dateSigned", ''):
self.request.errors.add('body', 'data', 'Can\'t update contract change status. \'dateSigned\' is required.')
self.request.errors.status = 403
return

change['date'] = get_now()

if apply_patch(self.request, src=change.serialize()):
apply_patch(self.request, save=False, src=change.serialize())

if change['dateSigned']:
contract = self.request.validated['contract']
changes = contract.get("changes", [])
if len(changes) > 1: # has previous changes
last_date_signed = contract.changes[:-1][-1].dateSigned
obj_str = "last active change"
else:
last_date_signed = contract.dateSigned
obj_str = "contract"

if change['dateSigned'] < last_date_signed:
self.request.errors.add('body', 'data', 'Change dateSigned ({}) can\'t be earlier than {} dateSigned ({})'.format(change['dateSigned'].isoformat(), obj_str, last_date_signed.isoformat()))
self.request.errors.status = 403
return

if save_contract(self.request):
self.LOGGER.info('Updated contract change {}'.format(change.id),
extra=context_unpack(self.request, {'MESSAGE_ID': 'contract_change_patch'}))
return {'data': change.serialize('view')}

0 comments on commit 7f0fb34

Please sign in to comment.