From 4037e15afc2fdcebaa933c456d4b937ea306f848 Mon Sep 17 00:00:00 2001 From: Vitalii Martyniak Date: Mon, 16 Mar 2020 10:21:29 +0200 Subject: [PATCH 01/13] Add role `contract_supplier` to local roles of Contract --- .../cfaselectionua/models/submodels/contract.py | 6 ++++++ src/openprocurement/tender/core/models.py | 14 +++++++++++--- src/openprocurement/tender/core/utils.py | 15 ++++++++++++++- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/openprocurement/tender/cfaselectionua/models/submodels/contract.py b/src/openprocurement/tender/cfaselectionua/models/submodels/contract.py index 5517ef9c7b..63d8722e1b 100644 --- a/src/openprocurement/tender/cfaselectionua/models/submodels/contract.py +++ b/src/openprocurement/tender/cfaselectionua/models/submodels/contract.py @@ -4,6 +4,7 @@ from schematics.types.compound import ModelType from schematics.types import StringType from openprocurement.tender.core.models import ContractValue +from openprocurement.tender.core.utils import get_contract_supplier_roles from openprocurement.api.utils import get_now from openprocurement.api.models import Model, ListType, Contract as BaseContract, Document @@ -16,6 +17,11 @@ class Options: awardID = StringType(required=True) documents = ListType(ModelType(Document, required=True), default=list()) + def __local_roles__(self): + roles = {} + roles.update(get_contract_supplier_roles(self)) + return roles + def validate_awardID(self, data, awardID): parent = data["__parent__"] if awardID and isinstance(parent, Model) and awardID not in [i.id for i in parent.awards]: diff --git a/src/openprocurement/tender/core/models.py b/src/openprocurement/tender/core/models.py index f4ff649235..26d1964848 100644 --- a/src/openprocurement/tender/core/models.py +++ b/src/openprocurement/tender/core/models.py @@ -53,8 +53,11 @@ COMPLAINT_ENHANCED_AMOUNT_RATE, COMPLAINT_ENHANCED_MIN_AMOUNT, COMPLAINT_ENHANCED_MAX_AMOUNT, ) from openprocurement.tender.core.utils import ( - calc_auction_end_time, rounding_shouldStartAfter, - restrict_value_to_bounds, round_up_to_ten + calc_auction_end_time, + rounding_shouldStartAfter, + restrict_value_to_bounds, + round_up_to_ten, + get_contract_supplier_roles, ) from openprocurement.tender.core.validation import ( validate_lotvalue_value, @@ -378,6 +381,11 @@ class Options: awardID = StringType(required=True) documents = ListType(ModelType(Document, required=True), default=list()) + def __local_roles__(self): + roles = {} + roles.update(get_contract_supplier_roles(self)) + return roles + def validate_awardID(self, data, awardID): parent = data["__parent__"] if awardID and isinstance(parent, Model) and awardID not in [i.id for i in parent.awards]: @@ -776,7 +784,7 @@ def validate_relatedLot(self, data, relatedLot): parent = data["__parent__"] if relatedLot and isinstance(parent, Model): validate_relatedlot(get_tender(parent), relatedLot) - + def get_related_lot_obj(self, tender): lot_id = self.get("relatedLot") or self.get("__parent__").get("lotID") if lot_id: diff --git a/src/openprocurement/tender/core/utils.py b/src/openprocurement/tender/core/utils.py index cbab627afa..f8314b1dc6 100644 --- a/src/openprocurement/tender/core/utils.py +++ b/src/openprocurement/tender/core/utils.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import jmespath from decimal import Decimal from re import compile @@ -425,4 +426,16 @@ def block_tender(request): new_rules and (any([i.status not in ["active", "unsuccessful"] for i in tender.cancellations if not i.relatedLot]) or not accept_tender) - ) \ No newline at end of file + ) + + +def get_contract_supplier_roles(contract): + roles = {} + if 'bids' not in contract.__parent__: + return roles + bid_id = jmespath.search("awards[?id=='{}'].bid_id".format(contract.awardID), contract.__parent__)[0] + tokens = jmespath.search("bids[?id=='{}'].[owner,owner_token]".format(bid_id), contract.__parent__) + if tokens: + for item in tokens: + roles['_'.join(item)] = 'contract_supplier' + return roles From 4684dfd4f20d9a0d4c3041a9960f563265616b50 Mon Sep 17 00:00:00 2001 From: Vitalii Martyniak Date: Tue, 17 Mar 2020 08:19:16 +0200 Subject: [PATCH 02/13] Add contract permissions for supplier and buyer --- .../tender/cfaselectionua/models/tender.py | 6 ++++++ .../tender/competitivedialogue/models.py | 8 +++++++- src/openprocurement/tender/core/models.py | 6 ++++++ src/openprocurement/tender/core/utils.py | 17 +++++++++++++++++ src/openprocurement/tender/esco/models.py | 7 +++++++ src/openprocurement/tender/openeu/models.py | 7 +++++++ src/openprocurement/tender/openua/models.py | 7 ++++++- 7 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/openprocurement/tender/cfaselectionua/models/tender.py b/src/openprocurement/tender/cfaselectionua/models/tender.py index 8bcf085c0c..0f44c1ef04 100644 --- a/src/openprocurement/tender/cfaselectionua/models/tender.py +++ b/src/openprocurement/tender/cfaselectionua/models/tender.py @@ -23,6 +23,7 @@ BaseCancellation, validate_features_uniq, ) +from openprocurement.tender.core.utils import get_contract_supplier_permissions class Cancellation(BaseCancellation): @@ -225,11 +226,16 @@ def __acl__(self): acl.extend( [ (Allow, "{}_{}".format(self.owner, self.owner_token), "edit_complaint"), + (Allow, "{}_{}".format(self.owner, self.owner_token), "edit_contract"), + (Allow, "{}_{}".format(self.owner, self.owner_token), "upload_contract_documents"), (Allow, "g:agreement_selection", "edit_agreement_selection"), (Allow, "g:agreement_selection", "edit_tender"), (Allow, "g:brokers", "create_cancellation_complaint") ] ) + suppliers_permissions = get_contract_supplier_permissions(self) + if suppliers_permissions: + acl.extend(suppliers_permissions) self._acl_cancellation(acl) return acl diff --git a/src/openprocurement/tender/competitivedialogue/models.py b/src/openprocurement/tender/competitivedialogue/models.py index 0fae268357..a6249daad7 100644 --- a/src/openprocurement/tender/competitivedialogue/models.py +++ b/src/openprocurement/tender/competitivedialogue/models.py @@ -33,7 +33,7 @@ Lot as BaseLotUA, EUConfidentialDocument, ) -from openprocurement.tender.core.utils import calculate_tender_business_date +from openprocurement.tender.core.utils import calculate_tender_business_date, get_contract_supplier_permissions from openprocurement.tender.openua.models import Item as BaseUAItem, Tender as BaseTenderUA from openprocurement.tender.openua.constants import TENDER_PERIOD as TENDERING_DURATION_UA from openprocurement.tender.openeu.models import ( @@ -377,6 +377,8 @@ def stage2__acl__(obj): acl = [ (Allow, "{}_{}".format(obj.owner, obj.dialogue_token), "generate_credentials"), (Allow, "{}_{}".format(obj.owner, obj.owner_token), "edit_complaint"), + (Allow, "{}_{}".format(obj.owner, obj.owner_token), "edit_contract"), + (Allow, "{}_{}".format(obj.owner, obj.owner_token), "upload_contract_documents"), (Allow, "g:competitive_dialogue", "edit_tender"), (Allow, "g:competitive_dialogue", "edit_cancellation") ] @@ -394,6 +396,10 @@ def stage2__acl__(obj): if i.status == "active" ] ) + + suppliers_permissions = get_contract_supplier_permissions(obj) + if suppliers_permissions: + acl.extend(suppliers_permissions) return acl diff --git a/src/openprocurement/tender/core/models.py b/src/openprocurement/tender/core/models.py index 26d1964848..2dd3917d7a 100644 --- a/src/openprocurement/tender/core/models.py +++ b/src/openprocurement/tender/core/models.py @@ -58,6 +58,7 @@ restrict_value_to_bounds, round_up_to_ten, get_contract_supplier_roles, + get_contract_supplier_permissions, ) from openprocurement.tender.core.validation import ( validate_lotvalue_value, @@ -1464,9 +1465,14 @@ def get_role(self): def __acl__(self): acl = [(Allow, "{}_{}".format(i.owner, i.owner_token), "create_award_complaint") for i in self.bids] + suppliers_permissions = get_contract_supplier_permissions(self) + if suppliers_permissions: + acl.extend(suppliers_permissions) acl.extend( [ (Allow, "{}_{}".format(self.owner, self.owner_token), "edit_complaint"), + (Allow, "{}_{}".format(self.owner, self.owner_token), "edit_contract"), + (Allow, "{}_{}".format(self.owner, self.owner_token), "upload_contract_documents"), ] ) self._acl_cancellation_complaint(acl) diff --git a/src/openprocurement/tender/core/utils.py b/src/openprocurement/tender/core/utils.py index f8314b1dc6..94b9eb3c1c 100644 --- a/src/openprocurement/tender/core/utils.py +++ b/src/openprocurement/tender/core/utils.py @@ -11,6 +11,7 @@ from time import sleep from pyramid.exceptions import URLDecodeError from pyramid.compat import decode_path_info +from pyramid.security import Allow from cornice.resource import resource from couchdb.http import ResourceConflict from openprocurement.api.constants import ( @@ -429,6 +430,22 @@ def block_tender(request): ) +def get_contract_supplier_permissions(tender): + """ + Set `upload_contract_document` permissions for award in `active` status owners + """ + suppliers_permissions = [] + if tender.get('bids', []) and tender.get('awards', []): + win_bids = jmespath.search("awards[?status=='active'].bid_id", tender._data) or [] + for bid in tender.bids: + if bid.status == "active" and bid.id in win_bids: + suppliers_permissions.append( + (Allow, "{}_{}".format(bid.owner, bid.owner_token), "upload_contract_documents") + ) + suppliers_permissions.append((Allow, "{}_{}".format(bid.owner, bid.owner_token), "edit_contract")) + return suppliers_permissions + + def get_contract_supplier_roles(contract): roles = {} if 'bids' not in contract.__parent__: diff --git a/src/openprocurement/tender/esco/models.py b/src/openprocurement/tender/esco/models.py index da44f1d7d1..91a3f6b898 100644 --- a/src/openprocurement/tender/esco/models.py +++ b/src/openprocurement/tender/esco/models.py @@ -55,6 +55,7 @@ calculate_clarifications_business_date, ) from openprocurement.tender.core.constants import CPV_ITEMS_CLASS_FROM +from openprocurement.tender.core.utils import get_contract_supplier_permissions from openprocurement.tender.openua.models import Tender as OpenUATender from openprocurement.tender.openua.constants import COMPLAINT_SUBMIT_TIME, ENQUIRY_STAND_STILL_TIME, AUCTION_PERIOD_TIME from openprocurement.tender.openeu.models import ( @@ -654,10 +655,16 @@ def __acl__(self): acl.extend( [ (Allow, "{}_{}".format(self.owner, self.owner_token), "edit_complaint"), + (Allow, "{}_{}".format(self.owner, self.owner_token), "edit_contract"), + (Allow, "{}_{}".format(self.owner, self.owner_token), "upload_contract_documents"), ] ) self._acl_cancellation_complaint(acl) + + suppliers_permissions = get_contract_supplier_permissions(self) + if suppliers_permissions: + acl.extend(suppliers_permissions) return acl @serializable(serialized_name="enquiryPeriod", type=ModelType(EnquiryPeriod)) diff --git a/src/openprocurement/tender/openeu/models.py b/src/openprocurement/tender/openeu/models.py index 6e6ca5b896..c7029427db 100644 --- a/src/openprocurement/tender/openeu/models.py +++ b/src/openprocurement/tender/openeu/models.py @@ -54,6 +54,7 @@ has_unanswered_complaints, calculate_complaint_business_date, calculate_clarifications_business_date, + get_contract_supplier_permissions, ) from openprocurement.tender.belowthreshold.models import Tender as BaseTender from openprocurement.tender.core.validation import validate_lotvalue_value, validate_relatedlot @@ -648,8 +649,14 @@ def __acl__(self): acl.extend( [ (Allow, "{}_{}".format(self.owner, self.owner_token), "edit_complaint"), + (Allow, "{}_{}".format(self.owner, self.owner_token), "edit_contract"), + (Allow, "{}_{}".format(self.owner, self.owner_token), "upload_contract_documents"), ] ) + + suppliers_permissions = get_contract_supplier_permissions(self) + if suppliers_permissions: + acl.extend(suppliers_permissions) self._acl_cancellation_complaint(acl) return acl diff --git a/src/openprocurement/tender/openua/models.py b/src/openprocurement/tender/openua/models.py index b45f4002ad..0f85b00039 100644 --- a/src/openprocurement/tender/openua/models.py +++ b/src/openprocurement/tender/openua/models.py @@ -56,6 +56,7 @@ calculate_tender_business_date, calculate_complaint_business_date, calculate_clarifications_business_date, + get_contract_supplier_permissions, ) from openprocurement.tender.core.validation import validate_lotvalue_value, validate_relatedlot from openprocurement.tender.belowthreshold.models import Tender as BaseTender @@ -530,9 +531,13 @@ def __acl__(self): acl.extend( [ (Allow, "{}_{}".format(self.owner, self.owner_token), "edit_complaint"), + (Allow, "{}_{}".format(self.owner, self.owner_token), "edit_contract"), + (Allow, "{}_{}".format(self.owner, self.owner_token), "upload_contract_documents"), ] ) - + suppliers_permissions = get_contract_supplier_permissions(self) + if suppliers_permissions: + acl.extend(suppliers_permissions) self._acl_cancellation_complaint(acl) return acl From 2f98c3a4c809b35a77db720f38a3e602649e0ac2 Mon Sep 17 00:00:00 2001 From: Vitalii Martyniak Date: Tue, 17 Mar 2020 08:45:14 +0200 Subject: [PATCH 03/13] Set `edit_contract` permission for contract views --- src/openprocurement/tender/belowthreshold/views/contract.py | 2 +- src/openprocurement/tender/cfaselectionua/views/contract.py | 2 +- src/openprocurement/tender/esco/views/contract.py | 2 +- src/openprocurement/tender/openua/views/contract.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/openprocurement/tender/belowthreshold/views/contract.py b/src/openprocurement/tender/belowthreshold/views/contract.py index 0d0b7c90d2..9b278b84b9 100644 --- a/src/openprocurement/tender/belowthreshold/views/contract.py +++ b/src/openprocurement/tender/belowthreshold/views/contract.py @@ -63,7 +63,7 @@ def get(self): @json_view( content_type="application/json", - permission="edit_tender", + permission="edit_contract", validators=( validate_patch_contract_data, validate_contract_operation_not_in_allowed_status, diff --git a/src/openprocurement/tender/cfaselectionua/views/contract.py b/src/openprocurement/tender/cfaselectionua/views/contract.py index 9fbd7586d4..0241b1e748 100644 --- a/src/openprocurement/tender/cfaselectionua/views/contract.py +++ b/src/openprocurement/tender/cfaselectionua/views/contract.py @@ -62,7 +62,7 @@ def get(self): @json_view( content_type="application/json", - permission="edit_tender", + permission="edit_contract", validators=( validate_patch_contract_data, validate_contract_operation_not_in_allowed_status, diff --git a/src/openprocurement/tender/esco/views/contract.py b/src/openprocurement/tender/esco/views/contract.py index 0500e19719..56f4616f4f 100644 --- a/src/openprocurement/tender/esco/views/contract.py +++ b/src/openprocurement/tender/esco/views/contract.py @@ -29,7 +29,7 @@ class TenderESCOContractResource(TenderEUContractResource): @json_view( content_type="application/json", - permission="edit_tender", + permission="edit_contract", validators=( validate_patch_contract_data, validate_contract_operation_not_in_allowed_status, diff --git a/src/openprocurement/tender/openua/views/contract.py b/src/openprocurement/tender/openua/views/contract.py index 279e32e770..b03453e0d6 100644 --- a/src/openprocurement/tender/openua/views/contract.py +++ b/src/openprocurement/tender/openua/views/contract.py @@ -26,7 +26,7 @@ class TenderUaAwardContractResource(TenderAwardContractResource): @json_view( content_type="application/json", - permission="edit_tender", + permission="edit_contract", validators=( validate_patch_contract_data, validate_contract_operation_not_in_allowed_status, From a222866c60412ee119a92c4b902557eeb67bce2d Mon Sep 17 00:00:00 2001 From: Vitalii Martyniak Date: Tue, 17 Mar 2020 08:52:37 +0200 Subject: [PATCH 04/13] Add `edit` roles for supplier, tender owner and admins on Contract --- .../cfaselectionua/models/submodels/Contract.csv | 4 +++- .../cfaselectionua/models/submodels/contract.py | 9 +++++++++ src/openprocurement/tender/core/models.py | 13 ++++++++++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/openprocurement/tender/cfaselectionua/models/submodels/Contract.csv b/src/openprocurement/tender/cfaselectionua/models/submodels/Contract.csv index 0ae8e46410..3e46f14cb5 100644 --- a/src/openprocurement/tender/cfaselectionua/models/submodels/Contract.csv +++ b/src/openprocurement/tender/cfaselectionua/models/submodels/Contract.csv @@ -1,5 +1,7 @@ rolename,status,value,documents,description,title,items,suppliers,contractNumber,title_en,period,description_en,dateSigned,title_ru,date,awardID,description_ru,id,__parent__,contractID -edit,1,1,,1,1,,,1,1,1,1,1,1,,,1,,1, +admins,1,1,,1,1,,,1,1,1,1,1,1,,,1,,1, +edit_tender_owner,1,1,,1,1,,,1,1,1,1,1,1,,,1,,1, +edit_contract_supplier,1,,,,,,,,,,,,,,,,,, default,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1 create,,1,,1,1,1,1,1,1,1,1,,1,,1,1,,1,1 embedded,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1 diff --git a/src/openprocurement/tender/cfaselectionua/models/submodels/contract.py b/src/openprocurement/tender/cfaselectionua/models/submodels/contract.py index 63d8722e1b..3b66967bac 100644 --- a/src/openprocurement/tender/cfaselectionua/models/submodels/contract.py +++ b/src/openprocurement/tender/cfaselectionua/models/submodels/contract.py @@ -17,6 +17,15 @@ class Options: awardID = StringType(required=True) documents = ListType(ModelType(Document, required=True), default=list()) + def get_role(self): + root = self.get_root() + request = root.request + if request.authenticated_role in ("tender_owner", "contract_supplier"): + role = "edit_{}".format(request.authenticated_role) + else: + role = request.authenticated_role + return role + def __local_roles__(self): roles = {} roles.update(get_contract_supplier_roles(self)) diff --git a/src/openprocurement/tender/core/models.py b/src/openprocurement/tender/core/models.py index 2dd3917d7a..021fde49cf 100644 --- a/src/openprocurement/tender/core/models.py +++ b/src/openprocurement/tender/core/models.py @@ -373,7 +373,9 @@ class Contract(BaseContract): class Options: roles = { "create": blacklist("id", "status", "date", "documents", "dateSigned"), - "edit": blacklist("id", "documents", "date", "awardID", "suppliers", "items", "contractID"), + "admins": blacklist("id", "documents", "date", "awardID", "suppliers", "items", "contractID"), + "edit_tender_owner": blacklist("id", "documents", "date", "awardID", "suppliers", "items", "contractID"), + "edit_contract_supplier": whitelist("status"), "embedded": schematics_embedded_role, "view": schematics_default_role, } @@ -382,6 +384,15 @@ class Options: awardID = StringType(required=True) documents = ListType(ModelType(Document, required=True), default=list()) + def get_role(self): + root = self.get_root() + request = root.request + if request.authenticated_role in ("tender_owner", "contract_supplier"): + role = "edit_{}".format(request.authenticated_role) + else: + role = request.authenticated_role + return role + def __local_roles__(self): roles = {} roles.update(get_contract_supplier_roles(self)) From e4fee1f06a84318223ae2b15b8005c0e2fea1ce3 Mon Sep 17 00:00:00 2001 From: Vitalii Martyniak Date: Tue, 17 Mar 2020 09:00:37 +0200 Subject: [PATCH 05/13] Add validation for contract update by supplier --- src/openprocurement/tender/core/validation.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/openprocurement/tender/core/validation.py b/src/openprocurement/tender/core/validation.py index 3e6a8839b1..f82997ef44 100644 --- a/src/openprocurement/tender/core/validation.py +++ b/src/openprocurement/tender/core/validation.py @@ -1203,3 +1203,10 @@ def validate_complaint_type_change(request): complaint = request.validated["complaint"] if complaint.type == "claim": raise_operation_error(request, "Can't update claim to complaint") + + +def validate_update_contract_status_by_supplier(request): + if request.authenticated_role == "contract_supplier": + data = request.validated["data"] + if "status" in data and data["status"] != "pending" or request.context.status != "pending.winnerSigning": + raise_operation_error(request, "Supplier can change status to `pending`") From 70e305cafe8bc89851f714b4d1da50059425b692 Mon Sep 17 00:00:00 2001 From: Vitalii Martyniak Date: Tue, 17 Mar 2020 09:35:26 +0200 Subject: [PATCH 06/13] Add `pending.winnerSigning` status for Contract --- src/openprocurement/api/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openprocurement/api/models.py b/src/openprocurement/api/models.py index 568b8e4a9a..5ba3176854 100644 --- a/src/openprocurement/api/models.py +++ b/src/openprocurement/api/models.py @@ -673,7 +673,7 @@ class Contract(Model): description = StringType() # Contract description description_en = StringType() description_ru = StringType() - status = StringType(choices=["pending", "terminated", "active", "cancelled"], default="pending") + status = StringType(choices=["pending", "pending.winnerSigning", "terminated", "active", "cancelled"], default="pending") period = ModelType(Period) value = ModelType(Value) dateSigned = IsoDateTimeType() From 0d7fd5c9a5d7d563b80195b0faeac386045b22ef Mon Sep 17 00:00:00 2001 From: Vitalii Martyniak Date: Mon, 2 Mar 2020 11:55:07 +0200 Subject: [PATCH 07/13] Allow switch contract to `pending.winnerSigning` --- .../tender/belowthreshold/views/contract.py | 8 +++++--- .../tender/cfaselectionua/views/contract.py | 8 +++++--- src/openprocurement/tender/esco/views/contract.py | 2 ++ src/openprocurement/tender/openua/views/contract.py | 8 +++++--- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/openprocurement/tender/belowthreshold/views/contract.py b/src/openprocurement/tender/belowthreshold/views/contract.py index 9b278b84b9..44909c5a83 100644 --- a/src/openprocurement/tender/belowthreshold/views/contract.py +++ b/src/openprocurement/tender/belowthreshold/views/contract.py @@ -11,6 +11,7 @@ validate_update_contract_value_with_award, validate_update_contract_value_amount, validate_update_contract_value_net_required, + validate_update_contract_status_by_supplier, ) from openprocurement.tender.belowthreshold.utils import check_tender_status @@ -68,6 +69,7 @@ def get(self): validate_patch_contract_data, validate_contract_operation_not_in_allowed_status, validate_update_contract_only_for_active_lots, + validate_update_contract_status_by_supplier, validate_update_contract_value, validate_contract_signing, validate_update_contract_value_net_required, @@ -80,9 +82,9 @@ def patch(self): """ contract_status = self.request.context.status apply_patch(self.request, save=False, src=self.request.context.serialize()) - if contract_status != self.request.context.status and ( - contract_status != "pending" or self.request.context.status != "active" - ): + if contract_status != self.request.context.status and \ + (contract_status not in ("pending", "pending.winnerSigning",) or \ + self.request.context.status not in ("active", "pending", "pending.winnerSigning",)): raise_operation_error(self.request, "Can't update contract status") if self.request.context.status == "active" and not self.request.context.dateSigned: self.request.context.dateSigned = get_now() diff --git a/src/openprocurement/tender/cfaselectionua/views/contract.py b/src/openprocurement/tender/cfaselectionua/views/contract.py index 0241b1e748..58841b4f07 100644 --- a/src/openprocurement/tender/cfaselectionua/views/contract.py +++ b/src/openprocurement/tender/cfaselectionua/views/contract.py @@ -10,6 +10,7 @@ validate_update_contract_value_with_award, validate_update_contract_value_amount, validate_update_contract_value_net_required, + validate_update_contract_status_by_supplier, ) from openprocurement.tender.cfaselectionua.utils import check_tender_status @@ -66,6 +67,7 @@ def get(self): validators=( validate_patch_contract_data, validate_contract_operation_not_in_allowed_status, + validate_update_contract_status_by_supplier, validate_update_contract_only_for_active_lots, validate_update_contract_value, validate_update_contract_value_net_required, @@ -78,9 +80,9 @@ def patch(self): """ contract_status = self.request.context.status apply_patch(self.request, save=False, src=self.request.context.serialize()) - if contract_status != self.request.context.status and ( - contract_status != "pending" or self.request.context.status != "active" - ): + if contract_status != self.request.context.status and \ + (contract_status not in ("pending", "pending.winnerSigning",) or \ + self.request.context.status not in ("active", "pending", "pending.winnerSigning",)): raise_operation_error(self.request, "Can't update contract status") if self.request.context.status == "active" and not self.request.context.dateSigned: self.request.context.dateSigned = get_now() diff --git a/src/openprocurement/tender/esco/views/contract.py b/src/openprocurement/tender/esco/views/contract.py index 56f4616f4f..f5268d48d6 100644 --- a/src/openprocurement/tender/esco/views/contract.py +++ b/src/openprocurement/tender/esco/views/contract.py @@ -9,6 +9,7 @@ validate_update_contract_value_with_award, validate_update_contract_value_amount, validate_update_contract_value_net_required, + validate_update_contract_status_by_supplier, ) from openprocurement.tender.esco.validation import validate_update_contract_value_esco from openprocurement.tender.openeu.views.contract import TenderAwardContractResource as TenderEUContractResource @@ -33,6 +34,7 @@ class TenderESCOContractResource(TenderEUContractResource): validators=( validate_patch_contract_data, validate_contract_operation_not_in_allowed_status, + validate_update_contract_status_by_supplier, validate_update_contract_only_for_active_lots, validate_contract_update_with_accepted_complaint, validate_update_contract_value_esco, diff --git a/src/openprocurement/tender/openua/views/contract.py b/src/openprocurement/tender/openua/views/contract.py index b03453e0d6..bf595c7cb8 100644 --- a/src/openprocurement/tender/openua/views/contract.py +++ b/src/openprocurement/tender/openua/views/contract.py @@ -11,6 +11,7 @@ validate_update_contract_value_with_award, validate_update_contract_value_amount, validate_update_contract_value_net_required, + validate_update_contract_status_by_supplier, ) from openprocurement.tender.core.utils import save_tender, apply_patch, optendersresource from openprocurement.tender.openua.validation import validate_contract_update_with_accepted_complaint @@ -30,6 +31,7 @@ class TenderUaAwardContractResource(TenderAwardContractResource): validators=( validate_patch_contract_data, validate_contract_operation_not_in_allowed_status, + validate_update_contract_status_by_supplier, validate_update_contract_only_for_active_lots, validate_contract_update_with_accepted_complaint, validate_update_contract_value, @@ -44,9 +46,9 @@ def patch(self): """ contract_status = self.request.context.status apply_patch(self.request, save=False, src=self.request.context.serialize()) - if contract_status != self.request.context.status and ( - contract_status != "pending" or self.request.context.status != "active" - ): + if contract_status != self.request.context.status and \ + (contract_status not in ("pending", "pending.winnerSigning",) or \ + self.request.context.status not in ("active", "pending", "pending.winnerSigning",)): raise_operation_error(self.request, "Can't update contract status") if self.request.context.status == "active" and not self.request.context.dateSigned: self.request.context.dateSigned = get_now() From 6fa802ef552e1a5395c75b27719b70e3d91804f9 Mon Sep 17 00:00:00 2001 From: Vitalii Martyniak Date: Tue, 17 Mar 2020 13:04:30 +0200 Subject: [PATCH 08/13] Set permission `upload_contract_documents` for contract documents views --- .../tender/belowthreshold/views/contract_document.py | 8 +++++--- .../tender/cfaselectionua/views/contract_document.py | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/openprocurement/tender/belowthreshold/views/contract_document.py b/src/openprocurement/tender/belowthreshold/views/contract_document.py index 8837c7803f..d7645ddb3b 100644 --- a/src/openprocurement/tender/belowthreshold/views/contract_document.py +++ b/src/openprocurement/tender/belowthreshold/views/contract_document.py @@ -62,7 +62,7 @@ def collection_get(self): ) return {"data": collection_data} - @json_view(permission="edit_tender", validators=(validate_file_upload,)) + @json_view(permission="upload_contract_documents", validators=(validate_file_upload,)) def collection_post(self): """Tender Contract Document Upload """ @@ -96,7 +96,7 @@ def get(self): ] return {"data": document_data} - @json_view(validators=(validate_file_update,), permission="edit_tender") + @json_view(validators=(validate_file_update,), permission="upload_contract_documents") def put(self): """Tender Contract Document Update""" if not self.validate_contract_document("update"): @@ -110,7 +110,9 @@ def put(self): ) return {"data": document.serialize("view")} - @json_view(content_type="application/json", validators=(validate_patch_document_data,), permission="edit_tender") + @json_view(content_type="application/json", + validators=(validate_patch_document_data,), + permission="upload_contract_documents") def patch(self): """Tender Contract Document Update""" if not self.validate_contract_document("update"): diff --git a/src/openprocurement/tender/cfaselectionua/views/contract_document.py b/src/openprocurement/tender/cfaselectionua/views/contract_document.py index bf5a53d403..d996fe901f 100644 --- a/src/openprocurement/tender/cfaselectionua/views/contract_document.py +++ b/src/openprocurement/tender/cfaselectionua/views/contract_document.py @@ -62,7 +62,7 @@ def collection_get(self): ) return {"data": collection_data} - @json_view(permission="edit_tender", validators=(validate_file_upload,)) + @json_view(permission="upload_contract_documents", validators=(validate_file_upload,)) def collection_post(self): """Tender Contract Document Upload """ @@ -96,7 +96,7 @@ def get(self): ] return {"data": document_data} - @json_view(validators=(validate_file_update,), permission="edit_tender") + @json_view(validators=(validate_file_update,), permission="upload_contract_documents") def put(self): """Tender Contract Document Update""" if not self.validate_contract_document("update"): @@ -110,7 +110,9 @@ def put(self): ) return {"data": document.serialize("view")} - @json_view(content_type="application/json", validators=(validate_patch_document_data,), permission="edit_tender") + @json_view(content_type="application/json", + validators=(validate_patch_document_data,), + permission="upload_contract_documents") def patch(self): """Tender Contract Document Update""" if not self.validate_contract_document("update"): From c2e0474144a102090e45846a38840654378e9221 Mon Sep 17 00:00:00 2001 From: Vitalii Martyniak Date: Tue, 17 Mar 2020 13:07:30 +0200 Subject: [PATCH 09/13] Add validation for contract documents operations in `pending.winnerSigning` --- src/openprocurement/tender/core/validation.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/openprocurement/tender/core/validation.py b/src/openprocurement/tender/core/validation.py index f82997ef44..6ae1b41962 100644 --- a/src/openprocurement/tender/core/validation.py +++ b/src/openprocurement/tender/core/validation.py @@ -1210,3 +1210,18 @@ def validate_update_contract_status_by_supplier(request): data = request.validated["data"] if "status" in data and data["status"] != "pending" or request.context.status != "pending.winnerSigning": raise_operation_error(request, "Supplier can change status to `pending`") + + +def validate_role_for_contract_document_operation(request): + if request.authenticated_role not in ("tender_owner", "contract_supplier",): + raise_operation_error(request, "Can {} document only buyer or supplier".format(OPERATIONS.get(request.method))) + if request.authenticated_role == "contract_supplier" and \ + request.validated["contract"].status != "pending.winnerSigning": + raise_operation_error( + request, "Supplier can't {} document in current contract status".format(OPERATIONS.get(request.method)) + ) + if request.authenticated_role == "tender_owner" and \ + request.validated["contract"].status == "pending.winnerSigning": + raise_operation_error( + request, "Tender onwer can't {} document in current contract status".format(OPERATIONS.get(request.method)) + ) From 1bfaf9f9970989a85dca0776e35dacec8c25918a Mon Sep 17 00:00:00 2001 From: Vitalii Martyniak Date: Tue, 17 Mar 2020 13:09:40 +0200 Subject: [PATCH 10/13] Allow suppliers upload contract documents in `pending.winnerSigning` status --- .../tender/belowthreshold/views/contract_document.py | 11 +++++++---- .../tender/cfaselectionua/views/contract_document.py | 11 +++++++---- .../tender/openua/views/contract_document.py | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/openprocurement/tender/belowthreshold/views/contract_document.py b/src/openprocurement/tender/belowthreshold/views/contract_document.py index d7645ddb3b..d3019f9713 100644 --- a/src/openprocurement/tender/belowthreshold/views/contract_document.py +++ b/src/openprocurement/tender/belowthreshold/views/contract_document.py @@ -11,6 +11,7 @@ from openprocurement.api.validation import validate_file_update, validate_file_upload, validate_patch_document_data from openprocurement.tender.core.utils import save_tender, optendersresource, apply_patch +from openprocurement.tender.core.validation import validate_role_for_contract_document_operation @optendersresource( @@ -46,7 +47,7 @@ def validate_contract_document(self, operation): ] ): raise_operation_error(self.request, "Can {} document only in active lot status".format(operation)) - if self.request.validated["contract"].status not in ["pending", "active"]: + if self.request.validated["contract"].status not in ["pending", "pending.winnerSigning", "active"]: raise_operation_error(self.request, "Can't {} document in current contract status".format(operation)) return True @@ -62,7 +63,8 @@ def collection_get(self): ) return {"data": collection_data} - @json_view(permission="upload_contract_documents", validators=(validate_file_upload,)) + @json_view(permission="upload_contract_documents", validators=(validate_file_upload, + validate_role_for_contract_document_operation,)) def collection_post(self): """Tender Contract Document Upload """ @@ -96,7 +98,8 @@ def get(self): ] return {"data": document_data} - @json_view(validators=(validate_file_update,), permission="upload_contract_documents") + @json_view(validators=(validate_file_update, validate_role_for_contract_document_operation,), + permission="upload_contract_documents") def put(self): """Tender Contract Document Update""" if not self.validate_contract_document("update"): @@ -111,7 +114,7 @@ def put(self): return {"data": document.serialize("view")} @json_view(content_type="application/json", - validators=(validate_patch_document_data,), + validators=(validate_patch_document_data, validate_role_for_contract_document_operation,), permission="upload_contract_documents") def patch(self): """Tender Contract Document Update""" diff --git a/src/openprocurement/tender/cfaselectionua/views/contract_document.py b/src/openprocurement/tender/cfaselectionua/views/contract_document.py index d996fe901f..8e818f8759 100644 --- a/src/openprocurement/tender/cfaselectionua/views/contract_document.py +++ b/src/openprocurement/tender/cfaselectionua/views/contract_document.py @@ -11,6 +11,7 @@ from openprocurement.api.validation import validate_file_update, validate_file_upload, validate_patch_document_data from openprocurement.tender.core.utils import save_tender, optendersresource, apply_patch +from openprocurement.tender.core.validation import validate_role_for_contract_document_operation @optendersresource( @@ -46,7 +47,7 @@ def validate_contract_document(self, operation): ] ): raise_operation_error(self.request, "Can {} document only in active lot status".format(operation)) - if self.request.validated["contract"].status not in ["pending", "active"]: + if self.request.validated["contract"].status not in ["pending", "pending.winnerSigning", "active"]: raise_operation_error(self.request, "Can't {} document in current contract status".format(operation)) return True @@ -62,7 +63,8 @@ def collection_get(self): ) return {"data": collection_data} - @json_view(permission="upload_contract_documents", validators=(validate_file_upload,)) + @json_view(permission="upload_contract_documents", validators=(validate_file_upload, + validate_role_for_contract_document_operation,)) def collection_post(self): """Tender Contract Document Upload """ @@ -96,7 +98,8 @@ def get(self): ] return {"data": document_data} - @json_view(validators=(validate_file_update,), permission="upload_contract_documents") + @json_view(validators=(validate_file_update, validate_role_for_contract_document_operation,), + permission="upload_contract_documents") def put(self): """Tender Contract Document Update""" if not self.validate_contract_document("update"): @@ -111,7 +114,7 @@ def put(self): return {"data": document.serialize("view")} @json_view(content_type="application/json", - validators=(validate_patch_document_data,), + validators=(validate_patch_document_data, validate_role_for_contract_document_operation,), permission="upload_contract_documents") def patch(self): """Tender Contract Document Update""" diff --git a/src/openprocurement/tender/openua/views/contract_document.py b/src/openprocurement/tender/openua/views/contract_document.py index c6b11029a7..4d77bcafda 100644 --- a/src/openprocurement/tender/openua/views/contract_document.py +++ b/src/openprocurement/tender/openua/views/contract_document.py @@ -37,7 +37,7 @@ def validate_contract_document(self, operation): ] ): raise_operation_error(self.request, "Can {} document only in active lot status".format(operation)) - if self.request.validated["contract"].status not in ["pending", "active"]: + if self.request.validated["contract"].status not in ["pending", "pending.winnerSigning", "active"]: raise_operation_error(self.request, "Can't {} document in current contract status".format(operation)) if any( [ From a574902c1991d0c9311db9928e9767d76904ea66 Mon Sep 17 00:00:00 2001 From: Vitalii Martyniak Date: Tue, 17 Mar 2020 13:32:07 +0200 Subject: [PATCH 11/13] Add tests for contract status `pending.winnerSingning` --- .../tender/belowthreshold/tests/contract.py | 9 + .../belowthreshold/tests/contract_blanks.py | 168 ++++++++++++++++++ .../tender/cfaselectionua/tests/contract.py | 15 +- .../tests/stage2/contract.py | 12 ++ .../tender/esco/tests/contract.py | 8 + .../tender/openeu/tests/contract.py | 10 +- .../tender/openua/tests/contract.py | 9 + .../tender/openuadefense/tests/contract.py | 9 + 8 files changed, 238 insertions(+), 2 deletions(-) diff --git a/src/openprocurement/tender/belowthreshold/tests/contract.py b/src/openprocurement/tender/belowthreshold/tests/contract.py index c7c495a874..d9c220aeaa 100644 --- a/src/openprocurement/tender/belowthreshold/tests/contract.py +++ b/src/openprocurement/tender/belowthreshold/tests/contract.py @@ -30,6 +30,9 @@ lot2_patch_tender_contract_document, patch_tender_contract_value_vat_not_included, patch_tender_contract_value, + patch_tender_contract_status_by_owner, + patch_tender_contract_status_by_supplier, + patch_tender_contract_status_by_others, ) @@ -82,6 +85,9 @@ def setUp(self): test_create_tender_contract_in_complete_status = snitch(create_tender_contract_in_complete_status) test_patch_tender_contract = snitch(patch_tender_contract) test_patch_tender_contract_value = snitch(patch_tender_contract_value) + test_patch_tender_contract_status_by_owner = snitch(patch_tender_contract_status_by_owner) + test_patch_tender_contract_status_by_supplier = snitch(patch_tender_contract_status_by_supplier) + test_patch_tender_contract_status_by_others = snitch(patch_tender_contract_status_by_others) class TenderContractVATNotIncludedResourceTest(TenderContentWebTest, TenderContractResourceTestMixin): @@ -119,6 +125,9 @@ def setUp(self): self.create_award() test_patch_tender_contract_value_vat_not_included = snitch(patch_tender_contract_value_vat_not_included) + test_patch_tender_contract_status_by_owner = snitch(patch_tender_contract_status_by_owner) + test_patch_tender_contract_status_by_supplier = snitch(patch_tender_contract_status_by_supplier) + test_patch_tender_contract_status_by_others = snitch(patch_tender_contract_status_by_others) class Tender2LotContractResourceTest(TenderContentWebTest): diff --git a/src/openprocurement/tender/belowthreshold/tests/contract_blanks.py b/src/openprocurement/tender/belowthreshold/tests/contract_blanks.py index a57a328fca..d26e6fdac1 100644 --- a/src/openprocurement/tender/belowthreshold/tests/contract_blanks.py +++ b/src/openprocurement/tender/belowthreshold/tests/contract_blanks.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import jmespath from datetime import timedelta from copy import deepcopy from openprocurement.api.utils import get_now @@ -540,6 +541,173 @@ def patch_tender_contract_value_vat_not_included(self): ) +def patch_tender_contract_status_by_owner(self): + response = self.app.get("/tenders/{}/contracts".format(self.tender_id)) + contract = response.json["data"][0] + contract_id = contract["id"] + self.set_status("complete", {"status": "active.awarded"}) + + # prepare contract + doc = self.db.get(self.tender_id) + for i in doc.get("awards", []): + if 'complaintPeriod' in i: + i["complaintPeriod"]["endDate"] = i["complaintPeriod"]["startDate"] + if doc['contracts'][0]['value']['valueAddedTaxIncluded']: + doc['contracts'][0]['value']['amountNet'] = str(float(doc['contracts'][0]['value']['amount']) - 1) + self.db.save(doc) + + # Tender onwer + self.app.authorization = ("Basic", ("broker", "")) + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}}, + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract_id, self.tender_token), + {"data": {"status": "pending"}}, + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending") + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract_id, self.tender_token), + {"data": {"status": "active"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], u"active") + + +def patch_tender_contract_status_by_supplier(self): + response = self.app.get("/tenders/{}/contracts".format(self.tender_id)) + contract = response.json["data"][0] + contract_id = contract["id"] + self.set_status("complete", {"status": "active.awarded"}) + + doc = self.db.get(self.tender_id) + bid_id = jmespath.search("awards[?id=='{}'].bid_id".format(contract["awardID"]), doc)[0] + bid_token = jmespath.search("bids[?id=='{}'].owner_token".format(bid_id), doc)[0] + + for bid in doc.get("bids", []): + if bid["id"] == bid_id and bid["status"] == "pending": + bid["status"] = "active" + for i in doc.get("awards", []): + if "complaintPeriod" in i: + i["complaintPeriod"]["endDate"] = i["complaintPeriod"]["startDate"] + if doc["contracts"][0]["value"]["valueAddedTaxIncluded"]: + doc["contracts"][0]["value"]["amountNet"] = str(float(doc["contracts"][0]["value"]["amount"]) - 1) + self.db.save(doc) + + # Supplier + self.app.authorization = ("Basic", ("broker", "")) + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract_id, bid_token), + {"data": {"status": "pending.winnerSigning"}}, + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual( + response.json["errors"], + [{u'description': u'Supplier can change status to `pending`', u'location': u'body', u'name': u'data'}] + ) + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract_id, bid_token), + {"data": {"value": {"amount": 10000}}}, + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual( + response.json["errors"], + [{u'description': u'Supplier can change status to `pending`', u'location': u'body', u'name': u'data'}] + ) + + # Tender onwer + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}}, + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + + # Supplier + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract_id, bid_token), + {"data": {"status": "active"}}, + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual( + response.json["errors"], + [{u'description': u'Supplier can change status to `pending`', u'location': u'body', u'name': u'data'}] + ) + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract_id, bid_token), + {"data": {"value": {"amount": 10000}, "status": "pending"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertNotEqual(response.json["data"]["value"]["amount"], u"10000") + self.assertEqual(response.json["data"]["status"], u"pending") + + response = self.app.get("/tenders/{}/contracts/{}".format(self.tender_id, contract_id)) + self.assertNotEqual(response.json["data"]["value"]["amount"], u"10000") + + # Tender owner + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract_id, self.tender_token), + {"data": {"status": "active"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], u"active") + + +def patch_tender_contract_status_by_others(self): + response = self.app.get("/tenders/{}/contracts".format(self.tender_id)) + contract = response.json["data"][0] + contract_id = contract["id"] + self.set_status("complete", {"status": "active.awarded"}) + + doc = self.db.get(self.tender_id) + for i in doc.get("awards", []): + if 'complaintPeriod' in i: + i["complaintPeriod"]["endDate"] = i["complaintPeriod"]["startDate"] + if 'value' in doc['contracts'][0] and doc['contracts'][0]['value']['valueAddedTaxIncluded']: + doc['contracts'][0]['value']['amountNet'] = str(float(doc['contracts'][0]['value']['amount']) - 1) + self.db.save(doc) + bid_id = jmespath.search("awards[?id=='{}'].bid_id".format(contract["awardID"]), doc)[0] + bid_token = jmespath.search("bids[?id!='{}'].owner_token".format(bid_id), doc)[0] + + self.app.authorization = ("Basic", ("broker", "")) + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract_id, bid_token), + {"data": {"status": "pending.winnerSigning"}}, + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"], + [{u'description': u'Forbidden', u'location': u'url', u'name': u'permission'}]) + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract_id, bid_token), + {"data": {"status": "pending"}}, + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"], + [{u'description': u'Forbidden', u'location': u'url', u'name': u'permission'}]) + + def get_tender_contract(self): self.app.authorization = ("Basic", ("token", "")) response = self.app.post_json( diff --git a/src/openprocurement/tender/cfaselectionua/tests/contract.py b/src/openprocurement/tender/cfaselectionua/tests/contract.py index 4da17128dd..8de1b1f743 100644 --- a/src/openprocurement/tender/cfaselectionua/tests/contract.py +++ b/src/openprocurement/tender/cfaselectionua/tests/contract.py @@ -33,6 +33,9 @@ from openprocurement.tender.belowthreshold.tests.contract_blanks import ( patch_tender_contract_value_vat_not_included, patch_tender_contract_value, + patch_tender_contract_status_by_owner, + patch_tender_contract_status_by_others, + patch_tender_contract_status_by_supplier, ) @@ -58,6 +61,9 @@ class TenderContractResourceTest(TenderContentWebTest, TenderContractResourceTes test_create_tender_contract_in_complete_status = snitch(create_tender_contract_in_complete_status) test_patch_tender_contract = snitch(patch_tender_contract) test_patch_tender_contract_value = snitch(patch_tender_contract_value) + test_patch_tender_contract_status_by_owner = snitch(patch_tender_contract_status_by_owner) + test_patch_tender_contract_status_by_others = snitch(patch_tender_contract_status_by_others) + test_patch_tender_contract_status_by_supplier = snitch(patch_tender_contract_status_by_supplier) class TenderContractVATNotIncludedResourceTest(TenderContentWebTest, TenderContractResourceTestMixin): @@ -67,7 +73,11 @@ class TenderContractVATNotIncludedResourceTest(TenderContentWebTest, TenderContr def update_vat_fields(self, items): for item in items: - item["value"]["valueAddedTaxIncluded"] = False + if "lotValues" in item: + for lot_value in item["lotValues"]: + lot_value["value"]["valueAddedTaxIncluded"] = False + else: + item["value"]["valueAddedTaxIncluded"] = False def generate_bids(self, status, start_end="start"): self.initial_bids = deepcopy(self.initial_bids) @@ -81,6 +91,9 @@ def calculate_agreement_contracts_value_amount(self, agreement, items): self.update_vat_fields(agreement["contracts"]) test_patch_tender_contract_value_vat_not_included = snitch(patch_tender_contract_value_vat_not_included) + test_patch_tender_contract_status_by_owner = snitch(patch_tender_contract_status_by_owner) + test_patch_tender_contract_status_by_others = snitch(patch_tender_contract_status_by_others) + test_patch_tender_contract_status_by_supplier = snitch(patch_tender_contract_status_by_supplier) @unittest.skip("Skip multi-lots tests") diff --git a/src/openprocurement/tender/competitivedialogue/tests/stage2/contract.py b/src/openprocurement/tender/competitivedialogue/tests/stage2/contract.py index 0e954ac182..2ce8f79973 100644 --- a/src/openprocurement/tender/competitivedialogue/tests/stage2/contract.py +++ b/src/openprocurement/tender/competitivedialogue/tests/stage2/contract.py @@ -21,6 +21,9 @@ create_tender_contract, patch_tender_contract_value_vat_not_included, patch_tender_contract_value, + patch_tender_contract_status_by_owner, + patch_tender_contract_status_by_others, + patch_tender_contract_status_by_supplier, ) from openprocurement.tender.openua.tests.contract_blanks import ( # TenderStage2EU(UA)ContractResourceTest @@ -78,6 +81,9 @@ def setUp(self): test_create_tender_contract = snitch(create_tender_contract) test_patch_tender_contract_datesigned = snitch(patch_tender_contract_datesigned) test_patch_tender_contract = snitch(patch_tender_contract_eu) + test_patch_tender_contract_status_by_owner = snitch(patch_tender_contract_status_by_owner) + test_patch_tender_contract_status_by_others = snitch(patch_tender_contract_status_by_others) + test_patch_tender_contract_status_by_supplier = snitch(patch_tender_contract_status_by_supplier) class TenderStage2EUContractDocumentResourceTest( @@ -147,6 +153,9 @@ def setUp(self): test_patch_tender_contract_datesigned = snitch(patch_tender_contract_datesigned) test_patch_tender_contract = snitch(patch_tender_contract) test_patch_tender_contract_value = snitch(patch_tender_contract_value) + test_patch_tender_contract_status_by_owner = snitch(patch_tender_contract_status_by_owner) + test_patch_tender_contract_status_by_others = snitch(patch_tender_contract_status_by_others) + test_patch_tender_contract_status_by_supplier = snitch(patch_tender_contract_status_by_supplier) class TenderContractVATNotIncludedResourceTest(BaseCompetitiveDialogUAStage2ContentWebTest): @@ -183,6 +192,9 @@ def setUp(self): self.create_award() test_patch_tender_contract_value_vat_not_included = snitch(patch_tender_contract_value_vat_not_included) + test_patch_tender_contract_status_by_owner = snitch(patch_tender_contract_status_by_owner) + test_patch_tender_contract_status_by_others = snitch(patch_tender_contract_status_by_others) + test_patch_tender_contract_status_by_supplier = snitch(patch_tender_contract_status_by_supplier) class TenderStage2UAContractDocumentResourceTest( diff --git a/src/openprocurement/tender/esco/tests/contract.py b/src/openprocurement/tender/esco/tests/contract.py index 71b5078b68..eff7253898 100644 --- a/src/openprocurement/tender/esco/tests/contract.py +++ b/src/openprocurement/tender/esco/tests/contract.py @@ -11,6 +11,11 @@ TenderContractResourceTestMixin, TenderContractDocumentResourceTestMixin, ) +from openprocurement.tender.belowthreshold.tests.contract_blanks import ( + patch_tender_contract_status_by_owner, + patch_tender_contract_status_by_others, + patch_tender_contract_status_by_supplier, +) from openprocurement.tender.openua.tests.contract_blanks import ( # TenderContractResourceTest @@ -102,6 +107,9 @@ def setUp(self): test_create_tender_contract = snitch(create_tender_contract) test_patch_tender_contract_datesigned = snitch(patch_tender_contract_datesigned) test_patch_tender_contract = snitch(patch_tender_contract) + test_patch_tender_contract_status_by_owner = snitch(patch_tender_contract_status_by_owner) + test_patch_tender_contract_status_by_others = snitch(patch_tender_contract_status_by_others) + test_patch_tender_contract_status_by_supplier = snitch(patch_tender_contract_status_by_supplier) class TenderContractDocumentResourceTest(BaseESCOContentWebTest, TenderContractDocumentResourceTestMixin): diff --git a/src/openprocurement/tender/openeu/tests/contract.py b/src/openprocurement/tender/openeu/tests/contract.py index 9685522bc0..ac894fc68f 100644 --- a/src/openprocurement/tender/openeu/tests/contract.py +++ b/src/openprocurement/tender/openeu/tests/contract.py @@ -9,7 +9,12 @@ TenderContractResourceTestMixin, TenderContractDocumentResourceTestMixin, ) -from openprocurement.tender.belowthreshold.tests.contract_blanks import patch_tender_contract_value +from openprocurement.tender.belowthreshold.tests.contract_blanks import ( + patch_tender_contract_value, + patch_tender_contract_status_by_owner, + patch_tender_contract_status_by_others, + patch_tender_contract_status_by_supplier, +) from openprocurement.tender.openua.tests.contract_blanks import ( # TenderContractResourceTest @@ -62,6 +67,9 @@ def setUp(self): test_patch_tender_contract_datesigned = snitch(patch_tender_contract_datesigned) test_patch_tender_contract = snitch(patch_tender_contract) test_patch_tender_contract_value = snitch(patch_tender_contract_value) + test_patch_tender_contract_status_by_owner = snitch(patch_tender_contract_status_by_owner) + test_patch_tender_contract_status_by_others = snitch(patch_tender_contract_status_by_others) + test_patch_tender_contract_status_by_supplier = snitch(patch_tender_contract_status_by_supplier) class TenderContractDocumentResourceTest(BaseTenderContentWebTest, TenderContractDocumentResourceTestMixin): diff --git a/src/openprocurement/tender/openua/tests/contract.py b/src/openprocurement/tender/openua/tests/contract.py index 7162cc6e40..9a3519676d 100644 --- a/src/openprocurement/tender/openua/tests/contract.py +++ b/src/openprocurement/tender/openua/tests/contract.py @@ -19,6 +19,9 @@ from openprocurement.tender.belowthreshold.tests.contract_blanks import ( patch_tender_contract_value_vat_not_included, patch_tender_contract_value, + patch_tender_contract_status_by_owner, + patch_tender_contract_status_by_others, + patch_tender_contract_status_by_supplier, ) @@ -55,6 +58,9 @@ def setUp(self): test_patch_tender_contract_datesigned = snitch(patch_tender_contract_datesigned) test_patch_tender_contract = snitch(patch_tender_contract) test_patch_tender_contract_value = snitch(patch_tender_contract_value) + test_patch_tender_contract_status_by_owner = snitch(patch_tender_contract_status_by_owner) + test_patch_tender_contract_status_by_others = snitch(patch_tender_contract_status_by_others) + test_patch_tender_contract_status_by_supplier = snitch(patch_tender_contract_status_by_supplier) class TenderContractVATNotIncludedResourceTest(BaseTenderUAContentWebTest, TenderContractResourceTestMixin): @@ -92,6 +98,9 @@ def setUp(self): self.create_award() test_patch_tender_contract_value_vat_not_included = snitch(patch_tender_contract_value_vat_not_included) + test_patch_tender_contract_status_by_owner = snitch(patch_tender_contract_status_by_owner) + test_patch_tender_contract_status_by_others = snitch(patch_tender_contract_status_by_others) + test_patch_tender_contract_status_by_supplier = snitch(patch_tender_contract_status_by_supplier) class TenderContractDocumentResourceTest(BaseTenderUAContentWebTest, TenderContractDocumentResourceTestMixin): diff --git a/src/openprocurement/tender/openuadefense/tests/contract.py b/src/openprocurement/tender/openuadefense/tests/contract.py index 31e13ee3c4..6efebb1825 100644 --- a/src/openprocurement/tender/openuadefense/tests/contract.py +++ b/src/openprocurement/tender/openuadefense/tests/contract.py @@ -21,6 +21,9 @@ from openprocurement.tender.belowthreshold.tests.contract_blanks import ( patch_tender_contract_value_vat_not_included, patch_tender_contract_value, + patch_tender_contract_status_by_owner, + patch_tender_contract_status_by_others, + patch_tender_contract_status_by_supplier, ) @@ -56,6 +59,9 @@ def setUp(self): test_create_tender_contract = snitch(create_tender_contract) test_patch_tender_contract = snitch(patch_tender_contract) test_patch_tender_contract_value = snitch(patch_tender_contract_value) + test_patch_tender_contract_status_by_owner = snitch(patch_tender_contract_status_by_owner) + test_patch_tender_contract_status_by_others = snitch(patch_tender_contract_status_by_others) + test_patch_tender_contract_status_by_supplier = snitch(patch_tender_contract_status_by_supplier) class TenderContractVATNotIncludedResourceTest(BaseTenderUAContentWebTest, TenderContractResourceTestMixin): @@ -94,6 +100,9 @@ def setUp(self): self.create_award() test_patch_tender_contract_value_vat_not_included = snitch(patch_tender_contract_value_vat_not_included) + test_patch_tender_contract_status_by_owner = snitch(patch_tender_contract_status_by_owner) + test_patch_tender_contract_status_by_others = snitch(patch_tender_contract_status_by_others) + test_patch_tender_contract_status_by_supplier = snitch(patch_tender_contract_status_by_supplier) class TenderContractDocumentResourceTest(BaseTenderUAContentWebTest, TenderContractDocumentResourceTestMixin): From 4fe6ee9b398a9044b173715bc493be44ad30a1b9 Mon Sep 17 00:00:00 2001 From: Vitalii Martyniak Date: Tue, 17 Mar 2020 13:39:06 +0200 Subject: [PATCH 12/13] Add tests for upload contract docs in `pending.winnerSigning` status --- .../tender/belowthreshold/tests/contract.py | 27 +- .../belowthreshold/tests/contract_blanks.py | 897 +++++++++++++++++- .../tender/cfaselectionua/tests/contract.py | 18 + .../cfaselectionua/tests/contract_blanks.py | 170 ++++ .../tests/stage2/contract.py | 17 + .../tender/esco/tests/contract.py | 11 + .../tender/limited/tests/contract.py | 7 + .../tender/limited/tests/contract_blanks.py | 251 +++++ .../tender/openeu/tests/contract.py | 11 + .../tender/openua/tests/contract.py | 11 + .../tender/openuadefense/tests/contract.py | 11 + 11 files changed, 1417 insertions(+), 14 deletions(-) diff --git a/src/openprocurement/tender/belowthreshold/tests/contract.py b/src/openprocurement/tender/belowthreshold/tests/contract.py index d9c220aeaa..f88314ff51 100644 --- a/src/openprocurement/tender/belowthreshold/tests/contract.py +++ b/src/openprocurement/tender/belowthreshold/tests/contract.py @@ -33,6 +33,15 @@ patch_tender_contract_status_by_owner, patch_tender_contract_status_by_supplier, patch_tender_contract_status_by_others, + create_tender_contract_document_by_supplier, + create_tender_contract_document_by_others, + put_tender_contract_document_by_supplier, + put_tender_contract_document_by_others, + patch_tender_contract_document_by_supplier, + lot2_create_tender_contract_document_by_supplier, + lot2_create_tender_contract_document_by_others, + lot2_put_tender_contract_document_by_supplier, + lot2_patch_tender_contract_document_by_supplier, ) @@ -198,6 +207,12 @@ def setUp(self): self.contract_id = contract["id"] self.app.authorization = auth + test_create_tender_contract_document_by_supplier = snitch(create_tender_contract_document_by_supplier) + test_create_tender_contract_document_by_others = snitch(create_tender_contract_document_by_others) + test_put_tender_contract_document_by_supplier = snitch(put_tender_contract_document_by_supplier) + test_put_tender_contract_document_by_others = snitch(put_tender_contract_document_by_others) + test_patch_tender_contract_document_by_supplier = snitch(patch_tender_contract_document_by_supplier) + class Tender2LotContractDocumentResourceTest(TenderContentWebTest): initial_status = "active.qualification" @@ -240,15 +255,21 @@ def setUp(self): self.contract_id = contract["id"] self.app.authorization = auth - lot2_create_tender_contract_document = snitch(lot2_create_tender_contract_document) - lot2_put_tender_contract_document = snitch(lot2_put_tender_contract_document) - lot2_patch_tender_contract_document = snitch(lot2_patch_tender_contract_document) + test_lot2_create_tender_contract_document = snitch(lot2_create_tender_contract_document) + test_lot2_put_tender_contract_document = snitch(lot2_put_tender_contract_document) + test_lot2_patch_tender_contract_document = snitch(lot2_patch_tender_contract_document) + test_lot2_create_tender_contract_document_by_supplier = snitch(lot2_create_tender_contract_document_by_supplier) + test_lot2_create_tender_contract_document_by_others = snitch(lot2_create_tender_contract_document_by_others) + test_lot2_put_tender_contract_document_by_supplier = snitch(lot2_put_tender_contract_document_by_supplier) + test_lot2_patch_tender_contract_document_by_supplier = snitch(lot2_patch_tender_contract_document_by_supplier) def suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(TenderContractResourceTest)) suite.addTest(unittest.makeSuite(TenderContractDocumentResourceTest)) + suite.addTest(unittest.makeSuite(TenderContractVATNotIncludedResourceTest)) + suite.addTest(unittest.makeSuite(Tender2LotContractDocumentResourceTest)) return suite diff --git a/src/openprocurement/tender/belowthreshold/tests/contract_blanks.py b/src/openprocurement/tender/belowthreshold/tests/contract_blanks.py index d26e6fdac1..02cb7676f7 100644 --- a/src/openprocurement/tender/belowthreshold/tests/contract_blanks.py +++ b/src/openprocurement/tender/belowthreshold/tests/contract_blanks.py @@ -934,6 +934,17 @@ def not_found(self): def create_tender_contract_document(self): + response = self.app.get("/tenders/{}/contracts/{}".format(self.tender_id, self.contract_id)) + self.assertEqual(response.json["data"]["status"], "pending") + + doc = self.db.get(self.tender_id) + for i in doc.get("awards", []): + if "complaintPeriod" in i: + i["complaintPeriod"]["endDate"] = i["complaintPeriod"]["startDate"] + if 'value' in doc["contracts"][0] and doc["contracts"][0]["value"]["valueAddedTaxIncluded"]: + doc["contracts"][0]["value"]["amountNet"] = str(float(doc["contracts"][0]["value"]["amount"]) - 1) + self.db.save(doc) + response = self.app.post( "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), upload_files=[("file", "name.doc", "content")], @@ -957,6 +968,32 @@ def create_tender_contract_document(self): self.assertEqual(doc_id, response.json["data"][0]["id"]) self.assertEqual("name.doc", response.json["data"][0]["title"]) + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + upload_files=[("file", "contract.doc", "content")], + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"], + [{u'description': u"Tender onwer can't add document in current contract status", + u'location': u'body', + u'name': u'data'}]) + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending") + + response = self.app.get( "/tenders/{}/contracts/{}/documents/{}?download=some_id".format(self.tender_id, self.contract_id, doc_id), status=404, @@ -1012,6 +1049,187 @@ def create_tender_contract_document(self): ) +def create_tender_contract_document_by_supplier(self): + response = self.app.get("/tenders/{}/contracts/{}".format(self.tender_id, self.contract_id)) + contract = response.json["data"] + self.assertEqual(response.json["data"]["status"], "pending") + doc = self.db.get(self.tender_id) + bid_id = jmespath.search("awards[?id=='{}'].bid_id".format(contract["awardID"]), doc)[0] + bid_token = jmespath.search("bids[?id=='{}'].owner_token".format(bid_id), doc)[0] + + for bid in doc.get("bids", []): + if bid["id"] == bid_id and bid["status"] == "pending": + bid["status"] = "active" + for i in doc.get("awards", []): + if 'complaintPeriod' in i: + i["complaintPeriod"]["endDate"] = i["complaintPeriod"]["startDate"] + if 'value' in doc['contracts'][0] and doc['contracts'][0]['value']['valueAddedTaxIncluded']: + doc['contracts'][0]['value']['amountNet'] = str(float(doc['contracts'][0]['value']['amount']) - 1) + self.db.save(doc) + + # Supplier + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), + upload_files=[("file", "name.doc", "content")], + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"], + [{u'description': u"Supplier can't add document in current contract status", + u'location': u'body', + u'name': u'data'}]) + + # Tender owner + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + # Supplier + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), + upload_files=[("file", "name.doc", "content")], + ) + self.assertEqual(response.status, "201 Created") + self.assertEqual(response.content_type, "application/json") + doc_id = response.json["data"]["id"] + self.assertIn(doc_id, response.headers["Location"]) + self.assertEqual("name.doc", response.json["data"]["title"]) + key = response.json["data"]["url"].split("?")[-1] + + response = self.app.get("/tenders/{}/contracts/{}/documents".format(self.tender_id, self.contract_id)) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(doc_id, response.json["data"][0]["id"]) + self.assertEqual("name.doc", response.json["data"][0]["title"]) + + response = self.app.get("/tenders/{}/contracts/{}/documents?all=true".format(self.tender_id, self.contract_id)) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(doc_id, response.json["data"][0]["id"]) + self.assertEqual("name.doc", response.json["data"][0]["title"]) + + response = self.app.get( + "/tenders/{}/contracts/{}/documents/{}?download=some_id".format(self.tender_id, self.contract_id, doc_id), + status=404, + ) + self.assertEqual(response.status, "404 Not Found") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(response.json["status"], "error") + self.assertEqual( + response.json["errors"], [{u"description": u"Not Found", u"location": u"url", u"name": u"download"}] + ) + + response = self.app.get( + "/tenders/{}/contracts/{}/documents/{}?{}".format(self.tender_id, self.contract_id, doc_id, key) + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/msword") + self.assertEqual(response.content_length, 7) + self.assertEqual(response.body, "content") + + response = self.app.get("/tenders/{}/contracts/{}/documents/{}".format(self.tender_id, self.contract_id, doc_id)) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(doc_id, response.json["data"]["id"]) + self.assertEqual("name.doc", response.json["data"]["title"]) + + tender = self.db.get(self.tender_id) + tender["contracts"][-1]["status"] = "cancelled" + self.db.save(tender) + + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), + upload_files=[("file", "name.doc", "content")], + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(response.json["errors"][0]["description"], + "Supplier can't add document in current contract status") + + self.set_status("{}".format(self.forbidden_contract_document_modification_actions_status)) + + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), + upload_files=[("file", "name.doc", "content")], + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(response.json["errors"][0]["description"], + "Supplier can't add document in current contract status") + + +def create_tender_contract_document_by_others(self): + response = self.app.get("/tenders/{}/contracts/{}".format(self.tender_id, self.contract_id)) + contract = response.json["data"] + self.assertEqual(response.json["data"]["status"], "pending") + + doc = self.db.get(self.tender_id) + for i in doc.get("awards", []): + if 'complaintPeriod' in i: + i["complaintPeriod"]["endDate"] = i["complaintPeriod"]["startDate"] + if 'value' in doc['contracts'][0] and doc['contracts'][0]['value']['valueAddedTaxIncluded']: + doc['contracts'][0]['value']['amountNet'] = str(float(doc['contracts'][0]['value']['amount']) - 1) + self.db.save(doc) + bid_id = jmespath.search("awards[?id=='{}'].bid_id".format(contract["awardID"]), doc)[0] + bid_token = jmespath.search("bids[?id!='{}'].owner_token".format(bid_id), doc)[0] + + # Bid owner + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), + upload_files=[("file", "name.doc", "content")], + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"], + [{u'description': u'Forbidden', u'location': u'url', u'name': u'permission'}]) + + # Tender owner + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + # Bid onwer + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), + upload_files=[("file", "name.doc", "content")], + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"], [{"location": "url", "name": "permission", "description": "Forbidden"}]) + + tender = self.db.get(self.tender_id) + tender["contracts"][-1]["status"] = "cancelled" + self.db.save(tender) + + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), + upload_files=[("file", "name.doc", "content")], + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(response.json["errors"][0]["description"], "Forbidden") + + self.set_status("{}".format(self.forbidden_contract_document_modification_actions_status)) + + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), + upload_files=[("file", "name.doc", "content")], + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(response.json["errors"][0]["description"], "Forbidden") + + def put_tender_contract_document(self): response = self.app.post( "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), @@ -1034,6 +1252,31 @@ def put_tender_contract_document(self): self.assertEqual(response.json["status"], "error") self.assertEqual(response.json["errors"], [{u"description": u"Not Found", u"location": u"body", u"name": u"file"}]) + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + response = self.app.put( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, self.tender_token + ), + upload_files=[("file", "name.doc", "content2")], + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"][0]["description"], + "Tender onwer can't update document in current contract status") + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending") + response = self.app.put( "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( self.tender_id, self.contract_id, doc_id, self.tender_token @@ -1113,9 +1356,45 @@ def put_tender_contract_document(self): ) -def patch_tender_contract_document(self): +def put_tender_contract_document_by_supplier(self): + response = self.app.get("/tenders/{}/contracts/{}".format(self.tender_id, self.contract_id)) + contract = response.json["data"] + self.assertEqual(response.json["data"]["status"], "pending") + doc = self.db.get(self.tender_id) + bid_id = jmespath.search("awards[?id=='{}'].bid_id".format(contract["awardID"]), doc)[0] + bid_token = jmespath.search("bids[?id=='{}'].owner_token".format(bid_id), doc)[0] + + for bid in doc.get("bids", []): + if bid["id"] == bid_id and bid["status"] == "pending": + bid["status"] = "active" + for i in doc.get("awards", []): + if 'complaintPeriod' in i: + i["complaintPeriod"]["endDate"] = i["complaintPeriod"]["startDate"] + if 'value' in doc['contracts'][0] and doc['contracts'][0]['value']['valueAddedTaxIncluded']: + doc['contracts'][0]['value']['amountNet'] = str(float(doc['contracts'][0]['value']['amount']) - 1) + self.db.save(doc) + + # Supplier response = self.app.post( - "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), + upload_files=[("file", "name.doc", "content")], + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"][0]["description"], + "Supplier can't add document in current contract status") + + # Tender owner + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + # Supplier + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), upload_files=[("file", "name.doc", "content")], ) self.assertEqual(response.status, "201 Created") @@ -1123,23 +1402,282 @@ def patch_tender_contract_document(self): doc_id = response.json["data"]["id"] self.assertIn(doc_id, response.headers["Location"]) - response = self.app.patch_json( + response = self.app.put( "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( - self.tender_id, self.contract_id, doc_id, self.tender_token + self.tender_id, self.contract_id, doc_id, bid_token ), - {"data": {"description": "document description"}}, + status=404, + upload_files=[("invalid_name", "name.doc", "content")], ) - self.assertEqual(response.status, "200 OK") + self.assertEqual(response.status, "404 Not Found") self.assertEqual(response.content_type, "application/json") - self.assertEqual(doc_id, response.json["data"]["id"]) + self.assertEqual(response.json["status"], "error") + self.assertEqual(response.json["errors"], [{u"description": u"Not Found", u"location": u"body", u"name": u"file"}]) - response = self.app.get("/tenders/{}/contracts/{}/documents/{}".format(self.tender_id, self.contract_id, doc_id)) + response = self.app.put( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, bid_token + ), + upload_files=[("file", "name.doc", "content2")], + ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.content_type, "application/json") self.assertEqual(doc_id, response.json["data"]["id"]) - self.assertEqual("document description", response.json["data"]["description"]) + key = response.json["data"]["url"].split("?")[-1] - tender = self.db.get(self.tender_id) + response = self.app.get( + "/tenders/{}/contracts/{}/documents/{}?{}".format(self.tender_id, self.contract_id, doc_id, key) + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/msword") + self.assertEqual(response.content_length, 8) + self.assertEqual(response.body, "content2") + + response = self.app.get("/tenders/{}/contracts/{}/documents/{}".format(self.tender_id, self.contract_id, doc_id)) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(doc_id, response.json["data"]["id"]) + self.assertEqual("name.doc", response.json["data"]["title"]) + + response = self.app.put( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, bid_token + ), + "content3", + content_type="application/msword", + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(doc_id, response.json["data"]["id"]) + key = response.json["data"]["url"].split("?")[-1] + + response = self.app.get( + "/tenders/{}/contracts/{}/documents/{}?{}".format(self.tender_id, self.contract_id, doc_id, key) + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/msword") + self.assertEqual(response.content_length, 8) + self.assertEqual(response.body, "content3") + + tender = self.db.get(self.tender_id) + tender["contracts"][-1]["status"] = "cancelled" + self.db.save(tender) + + response = self.app.put( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, bid_token + ), + upload_files=[("file", "name.doc", "content3")], + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(response.json["errors"][0]["description"], + "Supplier can't update document in current contract status") + + self.set_status("{}".format(self.forbidden_contract_document_modification_actions_status)) + + response = self.app.put( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, bid_token + ), + upload_files=[("file", "name.doc", "content3")], + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(response.json["errors"][0]["description"], + "Supplier can't update document in current contract status") + + +def put_tender_contract_document_by_others(self): + response = self.app.get("/tenders/{}/contracts/{}".format(self.tender_id, self.contract_id)) + contract = response.json["data"] + self.assertEqual(response.json["data"]["status"], "pending") + doc = self.db.get(self.tender_id) + for i in doc.get("awards", []): + if 'complaintPeriod' in i: + i["complaintPeriod"]["endDate"] = i["complaintPeriod"]["startDate"] + if 'value' in doc['contracts'][0] and doc['contracts'][0]['value']['valueAddedTaxIncluded']: + doc['contracts'][0]['value']['amountNet'] = str(float(doc['contracts'][0]['value']['amount']) - 1) + self.db.save(doc) + bid_id = jmespath.search("awards[?id=='{}'].bid_id".format(contract["awardID"]), doc)[0] + bid_token = jmespath.search("bids[?id!='{}'].owner_token".format(bid_id), doc)[0] + + # Bid onwer + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), + upload_files=[("file", "name.doc", "content")], + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"], + [{u'description': u'Forbidden', u'location': u'url', u'name': u'permission'}]) + + # Tender owner + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + # Bid owner + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), + upload_files=[("file", "name.doc", "content")], + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"], + [{u'description': u'Forbidden', u'location': u'url', u'name': u'permission'}]) + + +def patch_tender_contract_document(self): + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + upload_files=[("file", "name.doc", "content")], + ) + self.assertEqual(response.status, "201 Created") + self.assertEqual(response.content_type, "application/json") + doc_id = response.json["data"]["id"] + self.assertIn(doc_id, response.headers["Location"]) + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + response = self.app.patch_json( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, self.tender_token + ), + {"data": {"description": "document description"}}, + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"][0]["description"], + "Tender onwer can't update document in current contract status") + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending") + + response = self.app.patch_json( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, self.tender_token + ), + {"data": {"description": "document description"}}, + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(doc_id, response.json["data"]["id"]) + + response = self.app.get("/tenders/{}/contracts/{}/documents/{}".format(self.tender_id, self.contract_id, doc_id)) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(doc_id, response.json["data"]["id"]) + self.assertEqual("document description", response.json["data"]["description"]) + + tender = self.db.get(self.tender_id) + tender["contracts"][-1]["status"] = "cancelled" + self.db.save(tender) + + response = self.app.patch_json( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, self.tender_token + ), + {"data": {"description": "document description"}}, + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(response.json["errors"][0]["description"], "Can't update document in current contract status") + + self.set_status("{}".format(self.forbidden_contract_document_modification_actions_status)) + + response = self.app.patch_json( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, self.tender_token + ), + {"data": {"description": "document description"}}, + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.content_type, "application/json") + self.assertEqual( + response.json["errors"][0]["description"], + "Can't update document in current ({}) tender status".format( + self.forbidden_contract_document_modification_actions_status + ), + ) + + +def patch_tender_contract_document_by_supplier(self): + response = self.app.get("/tenders/{}/contracts/{}".format(self.tender_id, self.contract_id)) + contract = response.json["data"] + self.assertEqual(response.json["data"]["status"], "pending") + doc = self.db.get(self.tender_id) + bid_id = jmespath.search("awards[?id=='{}'].bid_id".format(contract["awardID"]), doc)[0] + bid_token = jmespath.search("bids[?id=='{}'].owner_token".format(bid_id), doc)[0] + + for bid in doc.get("bids", []): + if bid["id"] == bid_id and bid["status"] == "pending": + bid["status"] = "active" + for i in doc.get("awards", []): + if 'complaintPeriod' in i: + i["complaintPeriod"]["endDate"] = i["complaintPeriod"]["startDate"] + if 'value' in doc['contracts'][0] and doc['contracts'][0]['value']['valueAddedTaxIncluded']: + doc['contracts'][0]['value']['amountNet'] = str(float(doc['contracts'][0]['value']['amount']) - 1) + self.db.save(doc) + + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), + upload_files=[("file", "name.doc", "content")], + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"][0]["description"], + "Supplier can't add document in current contract status") + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), + upload_files=[("file", "name.doc", "content")], + ) + self.assertEqual(response.status, "201 Created") + self.assertEqual(response.content_type, "application/json") + doc_id = response.json["data"]["id"] + self.assertIn(doc_id, response.headers["Location"]) + + response = self.app.patch_json( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, bid_token + ), + {"data": {"description": "document description"}}, + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(doc_id, response.json["data"]["id"]) + + response = self.app.get("/tenders/{}/contracts/{}/documents/{}".format(self.tender_id, self.contract_id, doc_id)) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(doc_id, response.json["data"]["id"]) + self.assertEqual("document description", response.json["data"]["description"]) + + tender = self.db.get(self.tender_id) tender["contracts"][-1]["status"] = "cancelled" self.db.save(tender) @@ -1177,9 +1715,91 @@ def patch_tender_contract_document(self): def lot2_create_tender_contract_document(self): + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + upload_files=[("file", "name.doc", "content")], + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"][0]["description"], + "Tender onwer can't add document in current contract status") + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending") + + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + upload_files=[("file", "name.doc", "content")], + ) + self.assertEqual(response.status, "201 Created") + self.assertEqual(response.content_type, "application/json") + doc_id = response.json["data"]["id"] + self.assertIn(doc_id, response.headers["Location"]) + self.assertEqual("name.doc", response.json["data"]["title"]) + key = response.json["data"]["url"].split("?")[-1] + + cancellation = dict(**test_cancellation) + cancellation.update({ + "status": "active", + "cancellationOf": "lot", + "relatedLot": self.initial_lots[0]["id"], + }) + + response = self.app.post_json( + "/tenders/{}/cancellations?acc_token={}".format(self.tender_id, self.tender_token), {"data": cancellation}, + ) + response = self.app.post( "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), upload_files=[("file", "name.doc", "content")], + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(response.json["errors"][0]["description"], "Can add document only in active lot status") + + +def lot2_create_tender_contract_document_by_supplier(self): + response = self.app.get("/tenders/{}/contracts/{}".format(self.tender_id, self.contract_id)) + contract = response.json["data"] + self.assertEqual(response.json["data"]["status"], "pending") + doc = self.db.get(self.tender_id) + bid_id = jmespath.search("awards[?id=='{}'].bid_id".format(contract["awardID"]), doc)[0] + bid_token = jmespath.search("bids[?id=='{}'].owner_token".format(bid_id), doc)[0] + + # Supplier + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), + upload_files=[("file", "name.doc", "content")], + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"][0]["description"], + "Supplier can't add document in current contract status") + + # Tender owner + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + # Supplier + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), + upload_files=[("file", "name.doc", "content")], ) self.assertEqual(response.status, "201 Created") self.assertEqual(response.content_type, "application/json") @@ -1199,8 +1819,9 @@ def lot2_create_tender_contract_document(self): {"data": cancellation}, ) + # Supplier response = self.app.post( - "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), upload_files=[("file", "name.doc", "content")], status=403, ) @@ -1209,6 +1830,43 @@ def lot2_create_tender_contract_document(self): self.assertEqual(response.json["errors"][0]["description"], "Can add document only in active lot status") +def lot2_create_tender_contract_document_by_others(self): + response = self.app.get("/tenders/{}/contracts/{}".format(self.tender_id, self.contract_id)) + contract = response.json["data"] + self.assertEqual(response.json["data"]["status"], "pending") + doc = self.db.get(self.tender_id) + bid_id = jmespath.search("awards[?id=='{}'].bid_id".format(contract["awardID"]), doc)[0] + bid_token = jmespath.search("bids[?id!='{}'].owner_token".format(bid_id), doc)[0] + + # Bid owner + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), + upload_files=[("file", "name.doc", "content")], + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"], + [{u'description': u'Forbidden', u'location': u'url', u'name': u'permission'}]) + + # Tender owner + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + # Bid owner + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), + upload_files=[("file", "name.doc", "content")], + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"], + [{u'description': u'Forbidden', u'location': u'url', u'name': u'permission'}]) + + def lot2_put_tender_contract_document(self): response = self.app.post( "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), @@ -1231,6 +1889,31 @@ def lot2_put_tender_contract_document(self): self.assertEqual(response.json["status"], "error") self.assertEqual(response.json["errors"], [{u"description": u"Not Found", u"location": u"body", u"name": u"file"}]) + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + response = self.app.put( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, self.tender_token + ), + upload_files=[("file", "name.doc", "content2")], + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"][0]["description"], + "Tender onwer can't update document in current contract status") + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending") + response = self.app.put( "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( self.tender_id, self.contract_id, doc_id, self.tender_token @@ -1265,6 +1948,90 @@ def lot2_put_tender_contract_document(self): self.assertEqual(response.json["errors"][0]["description"], "Can update document only in active lot status") +def lot2_put_tender_contract_document_by_supplier(self): + response = self.app.get("/tenders/{}/contracts/{}".format(self.tender_id, self.contract_id)) + contract = response.json["data"] + self.assertEqual(response.json["data"]["status"], "pending") + doc = self.db.get(self.tender_id) + bid_id = jmespath.search("awards[?id=='{}'].bid_id".format(contract["awardID"]), doc)[0] + bid_token = jmespath.search("bids[?id=='{}'].owner_token".format(bid_id), doc)[0] + + # Supplier + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), + upload_files=[("file", "name.doc", "content")], + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"][0]["description"], + "Supplier can't add document in current contract status") + + # Tender owner + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + # Supplier + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), + upload_files=[("file", "name.doc", "content")], + ) + self.assertEqual(response.status, "201 Created") + self.assertEqual(response.content_type, "application/json") + doc_id = response.json["data"]["id"] + self.assertIn(doc_id, response.headers["Location"]) + + response = self.app.put( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, bid_token + ), + status=404, + upload_files=[("invalid_name", "name.doc", "content")], + ) + self.assertEqual(response.status, "404 Not Found") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(response.json["status"], "error") + self.assertEqual(response.json["errors"], [{u"description": u"Not Found", u"location": u"body", u"name": u"file"}]) + + response = self.app.put( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, bid_token + ), + upload_files=[("file", "name.doc", "content2")], + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(doc_id, response.json["data"]["id"]) + key = response.json["data"]["url"].split("?")[-1] + + # Tender owner + cancellation = dict(**test_cancellation) + cancellation.update({ + "status": "active", + "cancellationOf": "lot", + "relatedLot": self.initial_lots[0]["id"], + }) + response = self.app.post_json( + "/tenders/{}/cancellations?acc_token={}".format(self.tender_id, self.tender_token), + {"data": cancellation}, + ) + + # Supplier + response = self.app.put( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, bid_token + ), + upload_files=[("file", "name.doc", "content3")], + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(response.json["errors"][0]["description"], "Can update document only in active lot status") + + def lot2_patch_tender_contract_document(self): response = self.app.post( "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), @@ -1275,12 +2042,38 @@ def lot2_patch_tender_contract_document(self): doc_id = response.json["data"]["id"] self.assertIn(doc_id, response.headers["Location"]) + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + response = self.app.patch_json( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, self.tender_token + ), + {"data": {"description": "document description"}}, + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"][0]["description"], + "Tender onwer can't update document in current contract status") + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending") + response = self.app.patch_json( "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( self.tender_id, self.contract_id, doc_id, self.tender_token ), {"data": {"description": "document description"}}, ) + self.assertEqual(response.status, "200 OK") self.assertEqual(response.content_type, "application/json") self.assertEqual(doc_id, response.json["data"]["id"]) @@ -1306,3 +2099,85 @@ def lot2_patch_tender_contract_document(self): self.assertEqual(response.status, "403 Forbidden") self.assertEqual(response.content_type, "application/json") self.assertEqual(response.json["errors"][0]["description"], "Can update document only in active lot status") + + +def lot2_patch_tender_contract_document_by_supplier(self): + response = self.app.get("/tenders/{}/contracts/{}".format(self.tender_id, self.contract_id)) + contract = response.json["data"] + self.assertEqual(response.json["data"]["status"], "pending") + doc = self.db.get(self.tender_id) + bid_id = jmespath.search("awards[?id=='{}'].bid_id".format(contract["awardID"]), doc)[0] + bid_token = jmespath.search("bids[?id=='{}'].owner_token".format(bid_id), doc)[0] + + # Tender owner + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + # Supplier + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), + upload_files=[("file", "name.doc", "content")], + ) + self.assertEqual(response.status, "201 Created") + self.assertEqual(response.content_type, "application/json") + doc_id = response.json["data"]["id"] + self.assertIn(doc_id, response.headers["Location"]) + + response = self.app.patch_json( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, bid_token + ), + {"data": {"description": "document description"}}, + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(doc_id, response.json["data"]["id"]) + + # Tender owner + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending") + + # Supplier + response = self.app.patch_json( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, bid_token + ), + {"data": {"description": "document description"}}, + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"][0]["description"], + "Supplier can't update document in current contract status") + + # Tender owner + cancellation = dict(**test_cancellation) + cancellation.update({ + "status": "active", + "cancellationOf": "lot", + "relatedLot": self.initial_lots[0]["id"], + }) + + response = self.app.post_json( + "/tenders/{}/cancellations?acc_token={}".format(self.tender_id, self.tender_token), {"data": cancellation}, + ) + + # Supplier + response = self.app.patch_json( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, bid_token + ), + {"data": {"description": "new document description"}}, + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(response.json["errors"][0]["description"], + "Supplier can't update document in current contract status") diff --git a/src/openprocurement/tender/cfaselectionua/tests/contract.py b/src/openprocurement/tender/cfaselectionua/tests/contract.py index 8de1b1f743..80e5f23d75 100644 --- a/src/openprocurement/tender/cfaselectionua/tests/contract.py +++ b/src/openprocurement/tender/cfaselectionua/tests/contract.py @@ -36,6 +36,15 @@ patch_tender_contract_status_by_owner, patch_tender_contract_status_by_others, patch_tender_contract_status_by_supplier, + create_tender_contract_document_by_supplier, + create_tender_contract_document_by_others, + put_tender_contract_document_by_supplier, + put_tender_contract_document_by_others, + patch_tender_contract_document_by_supplier, + lot2_create_tender_contract_document_by_supplier, + lot2_create_tender_contract_document_by_others, + lot2_put_tender_contract_document_by_supplier, + lot2_patch_tender_contract_document_by_supplier, ) @@ -135,6 +144,11 @@ class TenderContractDocumentResourceTest(TenderContentWebTest, TenderContractDoc initial_bids = test_bids initial_lots = test_lots + test_create_tender_contract_document_by_supplier = snitch(create_tender_contract_document_by_supplier) + test_create_tender_contract_document_by_others = snitch(create_tender_contract_document_by_others) + test_put_tender_contract_document_by_supplier = snitch(put_tender_contract_document_by_supplier) + test_put_tender_contract_document_by_others = snitch(put_tender_contract_document_by_others) + test_patch_tender_contract_document_by_supplier = snitch(patch_tender_contract_document_by_supplier) @unittest.skip("Skip multi-lots tests") class Tender2LotContractDocumentResourceTest(TenderContentWebTest): @@ -181,6 +195,10 @@ def setUp(self): lot2_create_tender_contract_document = snitch(lot2_create_tender_contract_document) lot2_put_tender_contract_document = snitch(lot2_put_tender_contract_document) lot2_patch_tender_contract_document = snitch(lot2_patch_tender_contract_document) + test_lot2_create_tender_contract_document_by_supplier = snitch(lot2_create_tender_contract_document_by_supplier) + test_lot2_create_tender_contract_document_by_others = snitch(lot2_create_tender_contract_document_by_others) + test_lot2_put_tender_contract_document_by_supplier = snitch(lot2_put_tender_contract_document_by_supplier) + test_lot2_patch_tender_contract_document_by_supplier = snitch(lot2_patch_tender_contract_document_by_supplier) def suite(): diff --git a/src/openprocurement/tender/cfaselectionua/tests/contract_blanks.py b/src/openprocurement/tender/cfaselectionua/tests/contract_blanks.py index 8c4226a085..9386d4e32a 100644 --- a/src/openprocurement/tender/cfaselectionua/tests/contract_blanks.py +++ b/src/openprocurement/tender/cfaselectionua/tests/contract_blanks.py @@ -577,6 +577,37 @@ def not_found(self): def create_tender_contract_document(self): + doc = self.db.get(self.tender_id) + for i in doc.get("awards", []): + if 'complaintPeriod' in i: + i["complaintPeriod"]["endDate"] = i["complaintPeriod"]["startDate"] + if doc['contracts'][0]['value']['valueAddedTaxIncluded']: + doc['contracts'][0]['value']['amountNet'] = str(float(doc['contracts'][0]['value']['amount']) - 1) + self.db.save(doc) + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + upload_files=[("file", "name.doc", "content")], + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"][0]["description"], + "Tender onwer can't add document in current contract status") + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending") + response = self.app.post( "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), upload_files=[("file", "name.doc", "content")], @@ -656,6 +687,14 @@ def create_tender_contract_document(self): def put_tender_contract_document(self): + doc = self.db.get(self.tender_id) + for i in doc.get("awards", []): + if 'complaintPeriod' in i: + i["complaintPeriod"]["endDate"] = i["complaintPeriod"]["startDate"] + if doc['contracts'][0]['value']['valueAddedTaxIncluded']: + doc['contracts'][0]['value']['amountNet'] = str(float(doc['contracts'][0]['value']['amount']) - 1) + self.db.save(doc) + response = self.app.post( "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), upload_files=[("file", "name.doc", "content")], @@ -677,6 +716,31 @@ def put_tender_contract_document(self): self.assertEqual(response.json["status"], "error") self.assertEqual(response.json["errors"], [{u"description": u"Not Found", u"location": u"body", u"name": u"file"}]) + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + response = self.app.put( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, self.tender_token + ), + upload_files=[("file", "name.doc", "content2")], + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"][0]["description"], + "Tender onwer can't update document in current contract status") + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending") + response = self.app.put( "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( self.tender_id, self.contract_id, doc_id, self.tender_token @@ -757,6 +821,14 @@ def put_tender_contract_document(self): def patch_tender_contract_document(self): + doc = self.db.get(self.tender_id) + for i in doc.get("awards", []): + if 'complaintPeriod' in i: + i["complaintPeriod"]["endDate"] = i["complaintPeriod"]["startDate"] + if doc['contracts'][0]['value']['valueAddedTaxIncluded']: + doc['contracts'][0]['value']['amountNet'] = str(float(doc['contracts'][0]['value']['amount']) - 1) + self.db.save(doc) + response = self.app.post( "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), upload_files=[("file", "name.doc", "content")], @@ -766,6 +838,31 @@ def patch_tender_contract_document(self): doc_id = response.json["data"]["id"] self.assertIn(doc_id, response.headers["Location"]) + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + response = self.app.patch_json( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, self.tender_token + ), + {"data": {"description": "document description"}}, + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"][0]["description"], + "Tender onwer can't update document in current contract status") + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending") + response = self.app.patch_json( "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( self.tender_id, self.contract_id, doc_id, self.tender_token @@ -820,6 +917,29 @@ def patch_tender_contract_document(self): def lot2_create_tender_contract_document(self): + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + upload_files=[("file", "name.doc", "content")], + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"][0]["description"], + "Tender onwer can't add document in current contract status") + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending") + response = self.app.post( "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), upload_files=[("file", "name.doc", "content")], @@ -873,6 +993,31 @@ def lot2_put_tender_contract_document(self): self.assertEqual(response.json["status"], "error") self.assertEqual(response.json["errors"], [{u"description": u"Not Found", u"location": u"body", u"name": u"file"}]) + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + response = self.app.put( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, self.tender_token + ), + upload_files=[("file", "name.doc", "content2")], + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"][0]["description"], + "Tender onwer can't update document in current contract status") + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending") + response = self.app.put( "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( self.tender_id, self.contract_id, doc_id, self.tender_token @@ -916,6 +1061,31 @@ def lot2_patch_tender_contract_document(self): doc_id = response.json["data"]["id"] self.assertIn(doc_id, response.headers["Location"]) + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending.winnerSigning"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + + response = self.app.patch_json( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, self.tender_token + ), + {"data": {"description": "document description"}}, + status=403 + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.json["errors"][0]["description"], + "Tender onwer can't update document in current contract status") + + response = self.app.patch_json( + "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + {"data": {"status": "pending"}} + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.json["data"]["status"], "pending") + response = self.app.patch_json( "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( self.tender_id, self.contract_id, doc_id, self.tender_token diff --git a/src/openprocurement/tender/competitivedialogue/tests/stage2/contract.py b/src/openprocurement/tender/competitivedialogue/tests/stage2/contract.py index 2ce8f79973..91ced82f82 100644 --- a/src/openprocurement/tender/competitivedialogue/tests/stage2/contract.py +++ b/src/openprocurement/tender/competitivedialogue/tests/stage2/contract.py @@ -24,6 +24,11 @@ patch_tender_contract_status_by_owner, patch_tender_contract_status_by_others, patch_tender_contract_status_by_supplier, + create_tender_contract_document_by_supplier, + create_tender_contract_document_by_others, + put_tender_contract_document_by_supplier, + put_tender_contract_document_by_others, + patch_tender_contract_document_by_supplier, ) from openprocurement.tender.openua.tests.contract_blanks import ( # TenderStage2EU(UA)ContractResourceTest @@ -117,6 +122,12 @@ def setUp(self): self.contract_id = contract["id"] self.app.authorization = ("Basic", ("broker", "")) + test_create_tender_contract_document_by_supplier = snitch(create_tender_contract_document_by_supplier) + test_create_tender_contract_document_by_others = snitch(create_tender_contract_document_by_others) + test_put_tender_contract_document_by_supplier = snitch(put_tender_contract_document_by_supplier) + test_put_tender_contract_document_by_others = snitch(put_tender_contract_document_by_others) + test_patch_tender_contract_document_by_supplier = snitch(patch_tender_contract_document_by_supplier) + class TenderStage2UAContractResourceTest(BaseCompetitiveDialogUAStage2ContentWebTest): initial_status = "active.qualification" @@ -227,6 +238,12 @@ def setUp(self): self.contract_id = contract["id"] self.app.authorization = auth + test_create_tender_contract_document_by_supplier = snitch(create_tender_contract_document_by_supplier) + test_create_tender_contract_document_by_others = snitch(create_tender_contract_document_by_others) + test_put_tender_contract_document_by_supplier = snitch(put_tender_contract_document_by_supplier) + test_put_tender_contract_document_by_others = snitch(put_tender_contract_document_by_others) + test_patch_tender_contract_document_by_supplier = snitch(patch_tender_contract_document_by_supplier) + def suite(): suite = unittest.TestSuite() diff --git a/src/openprocurement/tender/esco/tests/contract.py b/src/openprocurement/tender/esco/tests/contract.py index eff7253898..a680e5edb4 100644 --- a/src/openprocurement/tender/esco/tests/contract.py +++ b/src/openprocurement/tender/esco/tests/contract.py @@ -15,6 +15,11 @@ patch_tender_contract_status_by_owner, patch_tender_contract_status_by_others, patch_tender_contract_status_by_supplier, + create_tender_contract_document_by_supplier, + create_tender_contract_document_by_others, + put_tender_contract_document_by_supplier, + put_tender_contract_document_by_others, + patch_tender_contract_document_by_supplier, ) from openprocurement.tender.openua.tests.contract_blanks import ( @@ -141,6 +146,12 @@ def setUp(self): self.contract_id = contract["id"] self.app.authorization = ("Basic", ("broker", "")) + test_create_tender_contract_document_by_supplier = snitch(create_tender_contract_document_by_supplier) + test_create_tender_contract_document_by_others = snitch(create_tender_contract_document_by_others) + test_put_tender_contract_document_by_supplier = snitch(put_tender_contract_document_by_supplier) + test_put_tender_contract_document_by_others = snitch(put_tender_contract_document_by_others) + test_patch_tender_contract_document_by_supplier = snitch(patch_tender_contract_document_by_supplier) + def suite(): suite = unittest.TestSuite() diff --git a/src/openprocurement/tender/limited/tests/contract.py b/src/openprocurement/tender/limited/tests/contract.py index 800dc175bd..1a54565143 100644 --- a/src/openprocurement/tender/limited/tests/contract.py +++ b/src/openprocurement/tender/limited/tests/contract.py @@ -36,6 +36,9 @@ patch_tender_contract, tender_contract_signature_date, award_id_change_is_not_allowed, + create_tender_contract_document, + patch_tender_contract_document, + put_tender_contract_document, ) from openprocurement.tender.belowthreshold.tests.contract_blanks import ( patch_tender_contract_value_vat_not_included, @@ -387,6 +390,10 @@ def setUp(self): response = self.app.get("/tenders/{}/contracts".format(self.tender_id)) self.contract_id = response.json["data"][0]["id"] + test_create_tender_contract_document = snitch(create_tender_contract_document) + test_patch_tender_contract_document = snitch(patch_tender_contract_document) + test_put_tender_contract_document = snitch(put_tender_contract_document) + class TenderContractNegotiationDocumentResourceTest(TenderContractDocumentResourceTest): initial_data = test_tender_negotiation_data diff --git a/src/openprocurement/tender/limited/tests/contract_blanks.py b/src/openprocurement/tender/limited/tests/contract_blanks.py index ce0051811c..4a0e5ac228 100644 --- a/src/openprocurement/tender/limited/tests/contract_blanks.py +++ b/src/openprocurement/tender/limited/tests/contract_blanks.py @@ -885,3 +885,254 @@ def create_tender_contract_negotiation_quick(self): {"data": {"status": "active", "value": {"valueAddedTaxIncluded": False}}}, ) self.assertEqual(response.status, "200 OK") + + +def create_tender_contract_document(self): + response = self.app.get("/tenders/{}/contracts/{}".format(self.tender_id, self.contract_id)) + self.assertEqual(response.json["data"]["status"], "pending") + + doc = self.db.get(self.tender_id) + for i in doc.get("awards", []): + if "complaintPeriod" in i: + i["complaintPeriod"]["endDate"] = i["complaintPeriod"]["startDate"] + if 'value' in doc["contracts"][0] and doc["contracts"][0]["value"]["valueAddedTaxIncluded"]: + doc["contracts"][0]["value"]["amountNet"] = str(float(doc["contracts"][0]["value"]["amount"]) - 1) + self.db.save(doc) + + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + upload_files=[("file", "name.doc", "content")], + ) + self.assertEqual(response.status, "201 Created") + self.assertEqual(response.content_type, "application/json") + doc_id = response.json["data"]["id"] + self.assertIn(doc_id, response.headers["Location"]) + self.assertEqual("name.doc", response.json["data"]["title"]) + key = response.json["data"]["url"].split("?")[-1] + + response = self.app.get("/tenders/{}/contracts/{}/documents".format(self.tender_id, self.contract_id)) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(doc_id, response.json["data"][0]["id"]) + self.assertEqual("name.doc", response.json["data"][0]["title"]) + + response = self.app.get("/tenders/{}/contracts/{}/documents?all=true".format(self.tender_id, self.contract_id)) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(doc_id, response.json["data"][0]["id"]) + self.assertEqual("name.doc", response.json["data"][0]["title"]) + + response = self.app.get( + "/tenders/{}/contracts/{}/documents/{}?download=some_id".format(self.tender_id, self.contract_id, doc_id), + status=404, + ) + self.assertEqual(response.status, "404 Not Found") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(response.json["status"], "error") + self.assertEqual( + response.json["errors"], [{u"description": u"Not Found", u"location": u"url", u"name": u"download"}] + ) + + response = self.app.get( + "/tenders/{}/contracts/{}/documents/{}?{}".format(self.tender_id, self.contract_id, doc_id, key) + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/msword") + self.assertEqual(response.content_length, 7) + self.assertEqual(response.body, "content") + + response = self.app.get("/tenders/{}/contracts/{}/documents/{}".format(self.tender_id, self.contract_id, doc_id)) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(doc_id, response.json["data"]["id"]) + self.assertEqual("name.doc", response.json["data"]["title"]) + + tender = self.db.get(self.tender_id) + tender["contracts"][-1]["status"] = "cancelled" + self.db.save(tender) + + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + upload_files=[("file", "name.doc", "content")], + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(response.json["errors"][0]["description"], "Can't add document in current contract status") + + self.set_status("{}".format(self.forbidden_contract_document_modification_actions_status)) + + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + upload_files=[("file", "name.doc", "content")], + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.content_type, "application/json") + self.assertEqual( + response.json["errors"][0]["description"], + "Can't add document in current ({}) tender status".format( + self.forbidden_contract_document_modification_actions_status + ), + ) + + +def put_tender_contract_document(self): + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + upload_files=[("file", "name.doc", "content")], + ) + self.assertEqual(response.status, "201 Created") + self.assertEqual(response.content_type, "application/json") + doc_id = response.json["data"]["id"] + self.assertIn(doc_id, response.headers["Location"]) + + response = self.app.put( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, self.tender_token + ), + status=404, + upload_files=[("invalid_name", "name.doc", "content")], + ) + self.assertEqual(response.status, "404 Not Found") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(response.json["status"], "error") + self.assertEqual(response.json["errors"], [{u"description": u"Not Found", u"location": u"body", u"name": u"file"}]) + + response = self.app.put( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, self.tender_token + ), + upload_files=[("file", "name.doc", "content2")], + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(doc_id, response.json["data"]["id"]) + key = response.json["data"]["url"].split("?")[-1] + + response = self.app.get( + "/tenders/{}/contracts/{}/documents/{}?{}".format(self.tender_id, self.contract_id, doc_id, key) + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/msword") + self.assertEqual(response.content_length, 8) + self.assertEqual(response.body, "content2") + + response = self.app.get("/tenders/{}/contracts/{}/documents/{}".format(self.tender_id, self.contract_id, doc_id)) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(doc_id, response.json["data"]["id"]) + self.assertEqual("name.doc", response.json["data"]["title"]) + + response = self.app.put( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, self.tender_token + ), + "content3", + content_type="application/msword", + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(doc_id, response.json["data"]["id"]) + key = response.json["data"]["url"].split("?")[-1] + + response = self.app.get( + "/tenders/{}/contracts/{}/documents/{}?{}".format(self.tender_id, self.contract_id, doc_id, key) + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/msword") + self.assertEqual(response.content_length, 8) + self.assertEqual(response.body, "content3") + + tender = self.db.get(self.tender_id) + tender["contracts"][-1]["status"] = "cancelled" + self.db.save(tender) + + response = self.app.put( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, self.tender_token + ), + upload_files=[("file", "name.doc", "content3")], + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(response.json["errors"][0]["description"], "Can't update document in current contract status") + + self.set_status("{}".format(self.forbidden_contract_document_modification_actions_status)) + + response = self.app.put( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, self.tender_token + ), + upload_files=[("file", "name.doc", "content3")], + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.content_type, "application/json") + self.assertEqual( + response.json["errors"][0]["description"], + "Can't update document in current ({}) tender status".format( + self.forbidden_contract_document_modification_actions_status + ), + ) + + +def patch_tender_contract_document(self): + response = self.app.post( + "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), + upload_files=[("file", "name.doc", "content")], + ) + self.assertEqual(response.status, "201 Created") + self.assertEqual(response.content_type, "application/json") + doc_id = response.json["data"]["id"] + self.assertIn(doc_id, response.headers["Location"]) + + response = self.app.patch_json( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, self.tender_token + ), + {"data": {"description": "document description"}}, + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(doc_id, response.json["data"]["id"]) + + response = self.app.get("/tenders/{}/contracts/{}/documents/{}".format(self.tender_id, self.contract_id, doc_id)) + self.assertEqual(response.status, "200 OK") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(doc_id, response.json["data"]["id"]) + self.assertEqual("document description", response.json["data"]["description"]) + + tender = self.db.get(self.tender_id) + tender["contracts"][-1]["status"] = "cancelled" + self.db.save(tender) + + response = self.app.patch_json( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, self.tender_token + ), + {"data": {"description": "document description"}}, + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.content_type, "application/json") + self.assertEqual(response.json["errors"][0]["description"], "Can't update document in current contract status") + + self.set_status("{}".format(self.forbidden_contract_document_modification_actions_status)) + + response = self.app.patch_json( + "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( + self.tender_id, self.contract_id, doc_id, self.tender_token + ), + {"data": {"description": "document description"}}, + status=403, + ) + self.assertEqual(response.status, "403 Forbidden") + self.assertEqual(response.content_type, "application/json") + self.assertEqual( + response.json["errors"][0]["description"], + "Can't update document in current ({}) tender status".format( + self.forbidden_contract_document_modification_actions_status + ), + ) diff --git a/src/openprocurement/tender/openeu/tests/contract.py b/src/openprocurement/tender/openeu/tests/contract.py index ac894fc68f..40650d08d9 100644 --- a/src/openprocurement/tender/openeu/tests/contract.py +++ b/src/openprocurement/tender/openeu/tests/contract.py @@ -14,6 +14,11 @@ patch_tender_contract_status_by_owner, patch_tender_contract_status_by_others, patch_tender_contract_status_by_supplier, + create_tender_contract_document_by_supplier, + create_tender_contract_document_by_others, + put_tender_contract_document_by_supplier, + put_tender_contract_document_by_others, + patch_tender_contract_document_by_supplier, ) from openprocurement.tender.openua.tests.contract_blanks import ( @@ -102,6 +107,12 @@ def setUp(self): self.contract_id = contract["id"] self.app.authorization = ("Basic", ("broker", "")) + test_create_tender_contract_document_by_supplier = snitch(create_tender_contract_document_by_supplier) + test_create_tender_contract_document_by_others = snitch(create_tender_contract_document_by_others) + test_put_tender_contract_document_by_supplier = snitch(put_tender_contract_document_by_supplier) + test_put_tender_contract_document_by_others = snitch(put_tender_contract_document_by_others) + test_patch_tender_contract_document_by_supplier = snitch(patch_tender_contract_document_by_supplier) + def suite(): suite = unittest.TestSuite() diff --git a/src/openprocurement/tender/openua/tests/contract.py b/src/openprocurement/tender/openua/tests/contract.py index 9a3519676d..2d54dcc3f9 100644 --- a/src/openprocurement/tender/openua/tests/contract.py +++ b/src/openprocurement/tender/openua/tests/contract.py @@ -22,6 +22,11 @@ patch_tender_contract_status_by_owner, patch_tender_contract_status_by_others, patch_tender_contract_status_by_supplier, + create_tender_contract_document_by_supplier, + create_tender_contract_document_by_others, + put_tender_contract_document_by_supplier, + put_tender_contract_document_by_others, + patch_tender_contract_document_by_supplier, ) @@ -131,6 +136,12 @@ def setUp(self): self.contract_id = contract["id"] self.app.authorization = auth + test_create_tender_contract_document_by_supplier = snitch(create_tender_contract_document_by_supplier) + test_create_tender_contract_document_by_others = snitch(create_tender_contract_document_by_others) + test_put_tender_contract_document_by_supplier = snitch(put_tender_contract_document_by_supplier) + test_put_tender_contract_document_by_others = snitch(put_tender_contract_document_by_others) + test_patch_tender_contract_document_by_supplier = snitch(patch_tender_contract_document_by_supplier) + def suite(): suite = unittest.TestSuite() diff --git a/src/openprocurement/tender/openuadefense/tests/contract.py b/src/openprocurement/tender/openuadefense/tests/contract.py index 6efebb1825..60826cf057 100644 --- a/src/openprocurement/tender/openuadefense/tests/contract.py +++ b/src/openprocurement/tender/openuadefense/tests/contract.py @@ -24,6 +24,11 @@ patch_tender_contract_status_by_owner, patch_tender_contract_status_by_others, patch_tender_contract_status_by_supplier, + create_tender_contract_document_by_supplier, + create_tender_contract_document_by_others, + put_tender_contract_document_by_supplier, + put_tender_contract_document_by_others, + patch_tender_contract_document_by_supplier, ) @@ -133,6 +138,12 @@ def setUp(self): self.contract_id = contract["id"] self.app.authorization = auth + test_create_tender_contract_document_by_supplier = snitch(create_tender_contract_document_by_supplier) + test_create_tender_contract_document_by_others = snitch(create_tender_contract_document_by_others) + test_put_tender_contract_document_by_supplier = snitch(put_tender_contract_document_by_supplier) + test_put_tender_contract_document_by_others = snitch(put_tender_contract_document_by_others) + test_patch_tender_contract_document_by_supplier = snitch(patch_tender_contract_document_by_supplier) + def suite(): suite = unittest.TestSuite() From f90bf3dd18cb0bab3ab6c7a0a5d2078c9fd83107 Mon Sep 17 00:00:00 2001 From: Vitalii Martyniak Date: Thu, 26 Mar 2020 07:45:51 +0200 Subject: [PATCH 13/13] Rename contract status to `pending.winner-signing` --- src/openprocurement/api/models.py | 2 +- .../belowthreshold/tests/contract_blanks.py | 76 +++++++++---------- .../tender/belowthreshold/views/contract.py | 4 +- .../belowthreshold/views/contract_document.py | 2 +- .../cfaselectionua/tests/contract_blanks.py | 24 +++--- .../tender/cfaselectionua/views/contract.py | 4 +- .../cfaselectionua/views/contract_document.py | 2 +- src/openprocurement/tender/core/validation.py | 6 +- .../tender/openua/views/contract.py | 4 +- .../tender/openua/views/contract_document.py | 2 +- 10 files changed, 63 insertions(+), 63 deletions(-) diff --git a/src/openprocurement/api/models.py b/src/openprocurement/api/models.py index 5ba3176854..e966fa48a0 100644 --- a/src/openprocurement/api/models.py +++ b/src/openprocurement/api/models.py @@ -673,7 +673,7 @@ class Contract(Model): description = StringType() # Contract description description_en = StringType() description_ru = StringType() - status = StringType(choices=["pending", "pending.winnerSigning", "terminated", "active", "cancelled"], default="pending") + status = StringType(choices=["pending", "pending.winner-signing", "terminated", "active", "cancelled"], default="pending") period = ModelType(Period) value = ModelType(Value) dateSigned = IsoDateTimeType() diff --git a/src/openprocurement/tender/belowthreshold/tests/contract_blanks.py b/src/openprocurement/tender/belowthreshold/tests/contract_blanks.py index 02cb7676f7..8db7f82eb7 100644 --- a/src/openprocurement/tender/belowthreshold/tests/contract_blanks.py +++ b/src/openprocurement/tender/belowthreshold/tests/contract_blanks.py @@ -560,10 +560,10 @@ def patch_tender_contract_status_by_owner(self): self.app.authorization = ("Basic", ("broker", "")) response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}}, + {"data": {"status": "pending.winner-signing"}}, ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract_id, self.tender_token), @@ -604,7 +604,7 @@ def patch_tender_contract_status_by_supplier(self): self.app.authorization = ("Basic", ("broker", "")) response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract_id, bid_token), - {"data": {"status": "pending.winnerSigning"}}, + {"data": {"status": "pending.winner-signing"}}, status=403 ) self.assertEqual(response.status, "403 Forbidden") @@ -627,10 +627,10 @@ def patch_tender_contract_status_by_supplier(self): # Tender onwer response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}}, + {"data": {"status": "pending.winner-signing"}}, ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") # Supplier @@ -684,7 +684,7 @@ def patch_tender_contract_status_by_others(self): self.app.authorization = ("Basic", ("broker", "")) response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract_id, bid_token), - {"data": {"status": "pending.winnerSigning"}}, + {"data": {"status": "pending.winner-signing"}}, status=403 ) self.assertEqual(response.status, "403 Forbidden") @@ -693,10 +693,10 @@ def patch_tender_contract_status_by_others(self): response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract_id, bid_token), @@ -970,10 +970,10 @@ def create_tender_contract_document(self): response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") response = self.app.post( "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), @@ -1082,10 +1082,10 @@ def create_tender_contract_document_by_supplier(self): # Tender owner response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") # Supplier response = self.app.post( @@ -1191,10 +1191,10 @@ def create_tender_contract_document_by_others(self): # Tender owner response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") # Bid onwer response = self.app.post( @@ -1254,10 +1254,10 @@ def put_tender_contract_document(self): response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") response = self.app.put( "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( @@ -1387,10 +1387,10 @@ def put_tender_contract_document_by_supplier(self): # Tender owner response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") # Supplier response = self.app.post( @@ -1517,10 +1517,10 @@ def put_tender_contract_document_by_others(self): # Tender owner response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") # Bid owner response = self.app.post( @@ -1545,10 +1545,10 @@ def patch_tender_contract_document(self): response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") response = self.app.patch_json( "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( @@ -1647,10 +1647,10 @@ def patch_tender_contract_document_by_supplier(self): response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") response = self.app.post( "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, bid_token), @@ -1717,10 +1717,10 @@ def patch_tender_contract_document_by_supplier(self): def lot2_create_tender_contract_document(self): response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") response = self.app.post( "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), @@ -1791,10 +1791,10 @@ def lot2_create_tender_contract_document_by_supplier(self): # Tender owner response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") # Supplier response = self.app.post( @@ -1851,10 +1851,10 @@ def lot2_create_tender_contract_document_by_others(self): # Tender owner response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") # Bid owner response = self.app.post( @@ -1891,10 +1891,10 @@ def lot2_put_tender_contract_document(self): response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") response = self.app.put( "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( @@ -1969,10 +1969,10 @@ def lot2_put_tender_contract_document_by_supplier(self): # Tender owner response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") # Supplier response = self.app.post( @@ -2044,10 +2044,10 @@ def lot2_patch_tender_contract_document(self): response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") response = self.app.patch_json( "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( @@ -2112,10 +2112,10 @@ def lot2_patch_tender_contract_document_by_supplier(self): # Tender owner response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") # Supplier response = self.app.post( diff --git a/src/openprocurement/tender/belowthreshold/views/contract.py b/src/openprocurement/tender/belowthreshold/views/contract.py index 44909c5a83..1dcccd3955 100644 --- a/src/openprocurement/tender/belowthreshold/views/contract.py +++ b/src/openprocurement/tender/belowthreshold/views/contract.py @@ -83,8 +83,8 @@ def patch(self): contract_status = self.request.context.status apply_patch(self.request, save=False, src=self.request.context.serialize()) if contract_status != self.request.context.status and \ - (contract_status not in ("pending", "pending.winnerSigning",) or \ - self.request.context.status not in ("active", "pending", "pending.winnerSigning",)): + (contract_status not in ("pending", "pending.winner-signing",) or \ + self.request.context.status not in ("active", "pending", "pending.winner-signing",)): raise_operation_error(self.request, "Can't update contract status") if self.request.context.status == "active" and not self.request.context.dateSigned: self.request.context.dateSigned = get_now() diff --git a/src/openprocurement/tender/belowthreshold/views/contract_document.py b/src/openprocurement/tender/belowthreshold/views/contract_document.py index d3019f9713..82d7aaccd9 100644 --- a/src/openprocurement/tender/belowthreshold/views/contract_document.py +++ b/src/openprocurement/tender/belowthreshold/views/contract_document.py @@ -47,7 +47,7 @@ def validate_contract_document(self, operation): ] ): raise_operation_error(self.request, "Can {} document only in active lot status".format(operation)) - if self.request.validated["contract"].status not in ["pending", "pending.winnerSigning", "active"]: + if self.request.validated["contract"].status not in ["pending", "pending.winner-signing", "active"]: raise_operation_error(self.request, "Can't {} document in current contract status".format(operation)) return True diff --git a/src/openprocurement/tender/cfaselectionua/tests/contract_blanks.py b/src/openprocurement/tender/cfaselectionua/tests/contract_blanks.py index 9386d4e32a..f984ed3f5c 100644 --- a/src/openprocurement/tender/cfaselectionua/tests/contract_blanks.py +++ b/src/openprocurement/tender/cfaselectionua/tests/contract_blanks.py @@ -587,10 +587,10 @@ def create_tender_contract_document(self): response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") response = self.app.post( "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), @@ -718,10 +718,10 @@ def put_tender_contract_document(self): response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") response = self.app.put( "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( @@ -840,10 +840,10 @@ def patch_tender_contract_document(self): response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") response = self.app.patch_json( "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( @@ -919,10 +919,10 @@ def patch_tender_contract_document(self): def lot2_create_tender_contract_document(self): response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") response = self.app.post( "/tenders/{}/contracts/{}/documents?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), @@ -995,10 +995,10 @@ def lot2_put_tender_contract_document(self): response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") response = self.app.put( "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( @@ -1063,10 +1063,10 @@ def lot2_patch_tender_contract_document(self): response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, self.contract_id, self.tender_token), - {"data": {"status": "pending.winnerSigning"}} + {"data": {"status": "pending.winner-signing"}} ) self.assertEqual(response.status, "200 OK") - self.assertEqual(response.json["data"]["status"], "pending.winnerSigning") + self.assertEqual(response.json["data"]["status"], "pending.winner-signing") response = self.app.patch_json( "/tenders/{}/contracts/{}/documents/{}?acc_token={}".format( diff --git a/src/openprocurement/tender/cfaselectionua/views/contract.py b/src/openprocurement/tender/cfaselectionua/views/contract.py index 58841b4f07..a795aeda86 100644 --- a/src/openprocurement/tender/cfaselectionua/views/contract.py +++ b/src/openprocurement/tender/cfaselectionua/views/contract.py @@ -81,8 +81,8 @@ def patch(self): contract_status = self.request.context.status apply_patch(self.request, save=False, src=self.request.context.serialize()) if contract_status != self.request.context.status and \ - (contract_status not in ("pending", "pending.winnerSigning",) or \ - self.request.context.status not in ("active", "pending", "pending.winnerSigning",)): + (contract_status not in ("pending", "pending.winner-signing",) or \ + self.request.context.status not in ("active", "pending", "pending.winner-signing",)): raise_operation_error(self.request, "Can't update contract status") if self.request.context.status == "active" and not self.request.context.dateSigned: self.request.context.dateSigned = get_now() diff --git a/src/openprocurement/tender/cfaselectionua/views/contract_document.py b/src/openprocurement/tender/cfaselectionua/views/contract_document.py index 8e818f8759..ff14b3c0e6 100644 --- a/src/openprocurement/tender/cfaselectionua/views/contract_document.py +++ b/src/openprocurement/tender/cfaselectionua/views/contract_document.py @@ -47,7 +47,7 @@ def validate_contract_document(self, operation): ] ): raise_operation_error(self.request, "Can {} document only in active lot status".format(operation)) - if self.request.validated["contract"].status not in ["pending", "pending.winnerSigning", "active"]: + if self.request.validated["contract"].status not in ["pending", "pending.winner-signing", "active"]: raise_operation_error(self.request, "Can't {} document in current contract status".format(operation)) return True diff --git a/src/openprocurement/tender/core/validation.py b/src/openprocurement/tender/core/validation.py index 6ae1b41962..3ba29efa4b 100644 --- a/src/openprocurement/tender/core/validation.py +++ b/src/openprocurement/tender/core/validation.py @@ -1208,7 +1208,7 @@ def validate_complaint_type_change(request): def validate_update_contract_status_by_supplier(request): if request.authenticated_role == "contract_supplier": data = request.validated["data"] - if "status" in data and data["status"] != "pending" or request.context.status != "pending.winnerSigning": + if "status" in data and data["status"] != "pending" or request.context.status != "pending.winner-signing": raise_operation_error(request, "Supplier can change status to `pending`") @@ -1216,12 +1216,12 @@ def validate_role_for_contract_document_operation(request): if request.authenticated_role not in ("tender_owner", "contract_supplier",): raise_operation_error(request, "Can {} document only buyer or supplier".format(OPERATIONS.get(request.method))) if request.authenticated_role == "contract_supplier" and \ - request.validated["contract"].status != "pending.winnerSigning": + request.validated["contract"].status != "pending.winner-signing": raise_operation_error( request, "Supplier can't {} document in current contract status".format(OPERATIONS.get(request.method)) ) if request.authenticated_role == "tender_owner" and \ - request.validated["contract"].status == "pending.winnerSigning": + request.validated["contract"].status == "pending.winner-signing": raise_operation_error( request, "Tender onwer can't {} document in current contract status".format(OPERATIONS.get(request.method)) ) diff --git a/src/openprocurement/tender/openua/views/contract.py b/src/openprocurement/tender/openua/views/contract.py index bf595c7cb8..e0eaa9b236 100644 --- a/src/openprocurement/tender/openua/views/contract.py +++ b/src/openprocurement/tender/openua/views/contract.py @@ -47,8 +47,8 @@ def patch(self): contract_status = self.request.context.status apply_patch(self.request, save=False, src=self.request.context.serialize()) if contract_status != self.request.context.status and \ - (contract_status not in ("pending", "pending.winnerSigning",) or \ - self.request.context.status not in ("active", "pending", "pending.winnerSigning",)): + (contract_status not in ("pending", "pending.winner-signing",) or \ + self.request.context.status not in ("active", "pending", "pending.winner-signing",)): raise_operation_error(self.request, "Can't update contract status") if self.request.context.status == "active" and not self.request.context.dateSigned: self.request.context.dateSigned = get_now() diff --git a/src/openprocurement/tender/openua/views/contract_document.py b/src/openprocurement/tender/openua/views/contract_document.py index 4d77bcafda..b31890aff7 100644 --- a/src/openprocurement/tender/openua/views/contract_document.py +++ b/src/openprocurement/tender/openua/views/contract_document.py @@ -37,7 +37,7 @@ def validate_contract_document(self, operation): ] ): raise_operation_error(self.request, "Can {} document only in active lot status".format(operation)) - if self.request.validated["contract"].status not in ["pending", "pending.winnerSigning", "active"]: + if self.request.validated["contract"].status not in ["pending", "pending.winner-signing", "active"]: raise_operation_error(self.request, "Can't {} document in current contract status".format(operation)) if any( [