diff --git a/docs/source/complaints.rst b/docs/source/complaints.rst index f9577ee7..71b9df4d 100644 --- a/docs/source/complaints.rst +++ b/docs/source/complaints.rst @@ -39,8 +39,8 @@ Tender Conditions Claims/Complaints accepted -> stopping; edge[style=bold]; accepted -> {declined,satisfied,stopped}; - pending -> {accepted,invalid}; - stopping -> {stopped,invalid,declined}; + pending -> {accepted,invalid,stopped}; + stopping -> {stopped,invalid,declined,satisfied}; {pending;stopping} -> mistaken; edge[label="auction" style=dotted]; answered -> {invalid,declined,resolved}; @@ -75,8 +75,8 @@ Tender Award Claims/Complaints pending -> stopping; accepted -> stopping; edge[style=bold]; - pending -> {accepted,invalid}; - stopping -> {stopped,invalid,declined}; + pending -> {accepted,invalid,stopped}; + stopping -> {stopped,invalid,declined,satisfied}; accepted -> {declined,satisfied,stopped}; {pending;stopping} -> mistaken; } diff --git a/openprocurement/tender/openua/tests/award.py b/openprocurement/tender/openua/tests/award.py index 76f2fec4..88032f74 100644 --- a/openprocurement/tender/openua/tests/award.py +++ b/openprocurement/tender/openua/tests/award.py @@ -49,6 +49,7 @@ patch_tender_award_complaint, review_tender_award_complaint, review_tender_award_claim, + review_tender_award_stopping_complaint, # TenderLotAwardComplaintResourceTest create_tender_lot_award_complaint, patch_tender_lot_award_complaint, @@ -70,6 +71,7 @@ class TenderUaAwardComplaintResourceTestMixin(object): test_patch_tender_award_complaint = snitch(patch_tender_award_complaint) test_review_tender_award_complaint = snitch(review_tender_award_complaint) test_review_tender_award_claim = snitch(review_tender_award_claim) + test_review_tender_award_stopping_complaint = snitch(review_tender_award_stopping_complaint) class TenderAwardResourceTest(BaseTenderUAContentWebTest, TenderAwardResourceTestMixin): diff --git a/openprocurement/tender/openua/tests/award_blanks.py b/openprocurement/tender/openua/tests/award_blanks.py index 96cbae02..8844f000 100644 --- a/openprocurement/tender/openua/tests/award_blanks.py +++ b/openprocurement/tender/openua/tests/award_blanks.py @@ -850,8 +850,8 @@ def patch_tender_award_complaint(self): def review_tender_award_complaint(self): - for status in ['invalid', 'declined', 'satisfied']: - self.app.authorization = ('Basic', ('token', '')) + for status in ['invalid', 'stopped', 'declined', 'satisfied']: + self.app.authorization = ('Basic', ('broker', '')) response = self.app.post_json('/tenders/{}/awards/{}/complaints?acc_token={}'.format(self.tender_id, self.award_id, self.bid_token), {'data': { 'title': 'complaint title', 'description': 'complaint description', @@ -864,13 +864,15 @@ def review_tender_award_complaint(self): self.app.authorization = ('Basic', ('reviewer', '')) response = self.app.patch_json('/tenders/{}/awards/{}/complaints/{}'.format(self.tender_id, self.award_id, complaint['id']), {"data": { - "decision": '{} complaint'.format(status) + "decision": '{} complaint'.format(status), + 'rejectReasonDescription': 'reject reason' }}) self.assertEqual(response.status, '200 OK') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['data']["decision"], '{} complaint'.format(status)) + self.assertEqual(response.json['data']['rejectReasonDescription'], 'reject reason') - if status != "invalid": + if status in ['declined', 'satisfied']: response = self.app.patch_json('/tenders/{}/awards/{}/complaints/{}'.format(self.tender_id, self.award_id, complaint['id']), {"data": { "status": "accepted" }}) @@ -900,6 +902,47 @@ def review_tender_award_complaint(self): self.assertEqual(response.json['data']["status"], status) +def review_tender_award_stopping_complaint(self): + for status in ['satisfied', 'stopped', 'declined', 'mistaken', 'invalid']: + self.app.authorization = ('Basic', ('broker', '')) + response = self.app.post_json('/tenders/{}/awards/{}/complaints?acc_token={}'.format(self.tender_id, self.award_id, self.bid_token), { + 'data': { + 'title': 'complaint title', + 'description': 'complaint description', + 'author': test_organization, + 'status': 'pending', + } + }) + self.assertEqual(response.status, '201 Created') + self.assertEqual(response.content_type, 'application/json') + complaint = response.json['data'] + owner_token = response.json['access']['token'] + + url_patch_complaint = '/tenders/{}/awards/{}/complaints/{}'.format(self.tender_id, self.award_id, complaint['id']) + response = self.app.patch_json('{}?acc_token={}'.format(url_patch_complaint, owner_token), { + 'data': { + 'status': 'stopping', + 'cancellationReason': 'reason', + } + }) + self.assertEqual(response.status, '200 OK') + self.assertEqual(response.content_type, 'application/json') + self.assertEqual(response.json['data']['status'], 'stopping') + self.assertEqual(response.json['data']['cancellationReason'], 'reason') + + self.app.authorization = ('Basic', ('reviewer', '')) + response = self.app.patch_json(url_patch_complaint, { + 'data': { + 'decision': 'decision', + 'status': status, + } + }) + self.assertEqual(response.status, '200 OK') + self.assertEqual(response.content_type, 'application/json') + self.assertEqual(response.json['data']['status'], status) + self.assertEqual(response.json['data']['decision'], 'decision') + + def review_tender_award_claim(self): for status in ['invalid', 'resolved', 'declined']: self.app.authorization = ('Basic', ('token', '')) diff --git a/openprocurement/tender/openua/tests/complaint.py b/openprocurement/tender/openua/tests/complaint.py index 0b0a9435..c6242ff8 100644 --- a/openprocurement/tender/openua/tests/complaint.py +++ b/openprocurement/tender/openua/tests/complaint.py @@ -21,6 +21,7 @@ create_tender_complaint, patch_tender_complaint, review_tender_complaint, + review_tender_stopping_complaint, # TenderComplaintDocumentResourceTest patch_tender_complaint_document, put_tender_complaint_document, @@ -33,6 +34,7 @@ class TenderUAComplaintResourceTestMixin(object): test_create_tender_complaint = snitch(create_tender_complaint) test_patch_tender_complaint = snitch(patch_tender_complaint) test_review_tender_complaint = snitch(review_tender_complaint) + test_review_tender_stopping_complaint = snitch(review_tender_stopping_complaint) class TenderComplaintResourceTest(BaseTenderUAContentWebTest, diff --git a/openprocurement/tender/openua/tests/complaint_blanks.py b/openprocurement/tender/openua/tests/complaint_blanks.py index 159a4c93..c95379ac 100644 --- a/openprocurement/tender/openua/tests/complaint_blanks.py +++ b/openprocurement/tender/openua/tests/complaint_blanks.py @@ -212,8 +212,8 @@ def patch_tender_complaint(self): def review_tender_complaint(self): - for status in ['invalid', 'satisfied', 'declined']: - self.app.authorization = ('Basic', ('token', '')) + for status in ['invalid', 'stopped', 'satisfied', 'declined']: + self.app.authorization = ('Basic', ('broker', '')) response = self.app.post_json('/tenders/{}/complaints'.format(self.tender_id), {'data': { 'title': 'complaint title', 'description': 'complaint description', @@ -226,13 +226,15 @@ def review_tender_complaint(self): self.app.authorization = ('Basic', ('reviewer', '')) response = self.app.patch_json('/tenders/{}/complaints/{}'.format(self.tender_id, complaint['id']), {"data": { - "decision": '{} complaint'.format(status) + "decision": '{} complaint'.format(status), + 'rejectReasonDescription': 'reject reason' }}) self.assertEqual(response.status, '200 OK') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['data']["decision"], '{} complaint'.format(status)) + self.assertEqual(response.json['data']["rejectReasonDescription"], 'reject reason') - if status != "invalid": + if status in ['satisfied', 'declined']: response = self.app.patch_json('/tenders/{}/complaints/{}'.format(self.tender_id, complaint['id']), {"data": { "status": "accepted" @@ -257,6 +259,47 @@ def review_tender_complaint(self): self.assertEqual(response.json['data']["status"], status) +def review_tender_stopping_complaint(self): + for status in ['satisfied', 'stopped', 'declined', 'mistaken', 'invalid']: + self.app.authorization = ('Basic', ('broker', '')) + response = self.app.post_json('/tenders/{}/complaints'.format(self.tender_id), { + 'data': { + 'title': 'complaint title', + 'description': 'complaint description', + 'author': self.test_author, + 'status': 'pending', + } + }) + self.assertEqual(response.status, '201 Created') + self.assertEqual(response.content_type, 'application/json') + complaint = response.json['data'] + owner_token = response.json['access']['token'] + + url_patch_complaint = '/tenders/{}/complaints/{}'.format(self.tender_id, complaint['id']) + response = self.app.patch_json('{}?acc_token={}'.format(url_patch_complaint, owner_token), { + 'data': { + 'status': 'stopping', + 'cancellationReason': 'reason', + } + }) + self.assertEqual(response.status, '200 OK') + self.assertEqual(response.content_type, 'application/json') + self.assertEqual(response.json['data']['status'], 'stopping') + self.assertEqual(response.json['data']['cancellationReason'], 'reason') + + self.app.authorization = ('Basic', ('reviewer', '')) + response = self.app.patch_json(url_patch_complaint, { + 'data': { + 'decision': 'decision', + 'status': status, + } + }) + self.assertEqual(response.status, '200 OK') + self.assertEqual(response.content_type, 'application/json') + self.assertEqual(response.json['data']['status'], status) + self.assertEqual(response.json['data']['decision'], 'decision') + + # TenderLotAwardComplaintResourceTest diff --git a/openprocurement/tender/openua/views/award_complaint.py b/openprocurement/tender/openua/views/award_complaint.py index ad40f593..9d95bba5 100644 --- a/openprocurement/tender/openua/views/award_complaint.py +++ b/openprocurement/tender/openua/views/award_complaint.py @@ -137,13 +137,10 @@ def patch(self): apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateAccepted = get_now() self.context.acceptance = True - elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status == 'accepted' and data.get('status', self.context.status) in ['declined', 'satisfied']: + elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status in ['accepted', 'stopping'] and data.get('status', self.context.status) in ['declined', 'satisfied']: apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateDecision = get_now() - elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status == 'stopping' and data.get('status', self.context.status) == 'declined': - apply_patch(self.request, save=False, src=self.context.serialize()) - self.context.dateDecision = get_now() - elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status in ['accepted', 'stopping'] and data.get('status', self.context.status) == 'stopped': + elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status in ['pending', 'accepted', 'stopping'] and data.get('status', self.context.status) == 'stopped': apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateDecision = get_now() self.context.dateCanceled = self.context.dateCanceled or get_now() diff --git a/openprocurement/tender/openua/views/complaint.py b/openprocurement/tender/openua/views/complaint.py index e0e94499..db5961c2 100644 --- a/openprocurement/tender/openua/views/complaint.py +++ b/openprocurement/tender/openua/views/complaint.py @@ -137,13 +137,10 @@ def patch(self): apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateAccepted = get_now() self.context.acceptance = True - elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status == 'accepted' and data.get('status', self.context.status) in ['declined', 'satisfied']: + elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status in ['accepted', 'stopping'] and data.get('status', self.context.status) in ['declined', 'satisfied']: apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateDecision = get_now() - elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status == 'stopping' and data.get('status', self.context.status) == 'declined': - apply_patch(self.request, save=False, src=self.context.serialize()) - self.context.dateDecision = get_now() - elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status in ['accepted', 'stopping'] and data.get('status', self.context.status) == 'stopped': + elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status in ['pending', 'accepted', 'stopping'] and data.get('status', self.context.status) == 'stopped': apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateDecision = get_now() self.context.dateCanceled = self.context.dateCanceled or get_now()