def date_signed_on_change_creation(self): # test create change with date signed one_day_in_past = (get_now() - timedelta(days=1)).isoformat() response = self.app.post_json('/contracts/{}/changes?acc_token={}'.format(self.contract['id'], self.contract_token), {'data': {'rationale': u'причина зміни укр', 'rationale_en': u'change cause en', 'dateSigned': one_day_in_past, 'rationaleTypes': ['priceReduction'], 'contractNumber': u'№ 146'}}, status=403) self.assertIn("can't be earlier than contract dateSigned", response.json['errors'][0]["description"]) one_day_in_future = (get_now() + timedelta(days=1)).isoformat() response = self.app.post_json('/contracts/{}/changes?acc_token={}'.format(self.contract['id'], self.contract_token), {'data': {'rationale': u'причина зміни укр', 'rationale_en': u'change cause en', 'dateSigned': one_day_in_future, 'rationaleTypes': ['priceReduction'], 'contractNumber': u'№ 146'}}, status=422) self.assertEqual(response.json['errors'], [ {"location": "body", "name": "dateSigned", "description": [u"Contract signature date can't be in the future"]} ]) date = get_now().isoformat() response = self.app.post_json('/contracts/{}/changes?acc_token={}'.format(self.contract['id'], self.contract_token), {'data': {'rationale': u'причина зміни укр', 'rationale_en': u'change cause en', 'dateSigned': date, 'rationaleTypes': ['priceReduction'], 'contractNumber': u'№ 146'}}) self.assertEqual(response.status, '201 Created') self.assertEqual(response.content_type, 'application/json') change = response.json['data'] self.assertEqual(change['dateSigned'], date) response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change['id'], self.contract_token), {'data': {'status': 'active'}}) self.assertEqual(response.status, '200 OK')
def test_migrate_from0to1(self): set_db_schema_version(self.db, 0) with open(os.path.join(os.path.dirname(__file__), 'data/tender-contract-complete.json'), 'r') as df: data = json.loads(df.read()) t = Tender(data) t.store(self.db) tender = self.db.get(t.id) self.assertEqual(tender['awards'][0]['value'], data['awards'][0]['value']) self.assertEqual(tender['awards'][0]['suppliers'], data['awards'][0]['suppliers']) contract_data = deepcopy(tender['contracts'][0]) del contract_data['value'] del contract_data['suppliers'] contract_data['tender_id'] = tender['_id'] contract_data['tender_token'] = 'xxx' contract_data['procuringEntity'] = tender['procuringEntity'] contract = Contract(contract_data) contract.dateModified = get_now() contract.store(self.db) contract_data = self.db.get(contract.id) self.assertNotIn("value", contract_data) self.assertNotIn("suppliers", contract_data) migrate_data(self.app.app.registry, 2) migrated_item = self.db.get(contract.id) self.assertIn("value", migrated_item) self.assertEqual(migrated_item['value'], tender['awards'][0]['value']) self.assertIn("suppliers", migrated_item) self.assertEqual(migrated_item['suppliers'], tender['awards'][0]['suppliers'])
def collection_post(self): """Post a complaint """ tender = self.context complaint = self.request.validated['complaint'] if complaint.status == 'claim': validate_submit_claim_time(self.request) elif complaint.status == 'pending': validate_submit_complaint_time(self.request) complaint.dateSubmitted = get_now() complaint.type = 'complaint' else: complaint.status = 'draft' complaint.complaintID = '{}.{}{}'.format(tender.tenderID, self.server_id, self.complaints_len(tender) + 1) set_ownership(complaint, self.request) tender.complaints.append(complaint) if save_tender(self.request): self.LOGGER.info('Created tender complaint {}'.format(complaint.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_complaint_create'}, {'complaint_id': complaint.id})) self.request.response.status = 201 self.request.response.headers['Location'] = self.request.route_url('{}:Tender Complaints'.format(tender.procurementMethodType), tender_id=tender.id, complaint_id=complaint.id) return { 'data': complaint.serialize(tender.status), 'access': { 'token': complaint.owner_token } }
def collection_post(self): """Post a cancellation """ auction = self.request.validated['auction'] if auction.status in ['complete', 'cancelled', 'unsuccessful']: self.request.errors.add('body', 'data', 'Can\'t add cancellation in current ({}) auction status'.format(auction.status)) self.request.errors.status = 403 return cancellation = self.request.validated['cancellation'] cancellation.date = get_now() if any([i.status != 'active' for i in auction.lots if i.id == cancellation.relatedLot]): self.request.errors.add('body', 'data', 'Can add cancellation only in active lot status') self.request.errors.status = 403 return if cancellation.relatedLot and cancellation.status == 'active': self.cancel_lot(cancellation) elif cancellation.status == 'active': self.cancel_auction() auction.cancellations.append(cancellation) if save_auction(self.request): self.LOGGER.info('Created auction cancellation {}'.format(cancellation.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'auction_cancellation_create'}, {'cancellation_id': cancellation.id})) self.request.response.status = 201 route = self.request.matched_route.name.replace("collection_", "") self.request.response.headers['Location'] = self.request.current_route_url(_route_name=route, cancellation_id=cancellation.id, _query={}) return {'data': cancellation.serialize("view")}
def from0to1(registry): LOGGER.info("Start contracts migration.", extra={'MESSAGE_ID': 'migrate_data'}) results = registry.db.iterview('contracts/all', 2 ** 10, include_docs=True) docs = [] for i in results: doc = i.doc if "suppliers" not in doc: tender_id = doc['tender_id'] rel_award = doc['awardID'] tender_doc = registry.db.get(tender_id) rel_award = [aw for aw in tender_doc['awards'] if aw['id'] == doc['awardID']] if not rel_award: LOGGER.warn("Related award {} for contract {} not found!".format(doc['awardID'], doc['id']), extra={'MESSAGE_ID': 'migrate_data'}) continue rel_award = rel_award[0] doc['suppliers'] = rel_award['suppliers'] if "value" not in doc: doc['value'] = rel_award['value'] doc['dateModified'] = get_now().isoformat() docs.append(doc) if len(docs) >= 2 ** 7: registry.db.update(docs) docs = [] if docs: registry.db.update(docs) LOGGER.info("Contracts migration is finished.", extra={'MESSAGE_ID': 'migrate_data'})
def check_status(request): tender = request.validated['tender'] now = get_now() if tender.status == 'active.tendering' and tender.tenderPeriod.endDate <= now and \ not has_unanswered_complaints(tender) and not has_unanswered_questions(tender): for complaint in tender.complaints: check_complaint_status(request, complaint) LOGGER.info('Switched tender {} to {}'.format(tender['id'], 'active.pre-qualification'), extra=context_unpack(request, {'MESSAGE_ID': 'switched_tender_active.pre-qualification'})) tender.status = 'active.pre-qualification' tender.qualificationPeriod = type(tender).qualificationPeriod({'startDate': now}) check_initial_bids_count(request) prepare_qualifications(request) return elif tender.status == 'active.pre-qualification.stand-still' and tender.qualificationPeriod and tender.qualificationPeriod.endDate <= now and not any([ i.status in tender.block_complaint_status for q in tender.qualifications for i in q.complaints ]): LOGGER.info('Switched tender {} to {}'.format(tender['id'], 'active.stage2.pending'), extra=context_unpack(request, {'MESSAGE_ID': 'switched_tender_active_stage2_pending'})) tender.status = 'active.stage2.pending' check_initial_bids_count(request) return
def no_items_contract_change(self): data = deepcopy(self.initial_data) del data['items'] response = self.app.post_json('/contracts', {"data": data}) self.assertEqual(response.status, '201 Created') self.assertEqual(response.content_type, 'application/json') contract = response.json['data'] self.assertEqual(contract['status'], 'active') self.assertNotIn('items', contract) tender_token = data['tender_token'] response = self.app.patch_json('/contracts/{}/credentials?acc_token={}'.format(contract['id'], tender_token), {'data': ''}) self.assertEqual(response.status, '200 OK') token = response.json['access']['token'] response = self.app.post_json('/contracts/{}/changes?acc_token={}'.format(contract['id'], token), {'data': {'rationale': u'причина зміни укр', 'rationaleTypes': ['qualityImprovement']}}) self.assertEqual(response.status, '201 Created') change = response.json['data'] self.assertEqual(change['status'], 'pending') response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(contract['id'], change['id'], token), {'data': {'status': 'active', 'dateSigned': get_now().isoformat()}}) self.assertEqual(response.status, '200 OK') self.assertEqual(response.json['data']['status'], 'active') response = self.app.patch_json('/contracts/{}?acc_token={}'.format(contract['id'], token), {"data": {"status": "terminated", "amountPaid": {"amount": 100, "valueAddedTaxIncluded": True, "currency": "UAH"}}}) self.assertEqual(response.status, '200 OK') self.assertEqual(response.json['data']['status'], 'terminated') response = self.app.get('/contracts/{}'.format(contract['id'])) self.assertNotIn('items', response.json['data'])
def date_signed_on_change_creation_for_very_old_contracts_data(self): # prepare old contract data contract = self.db.get(self.contract['id']) contract['dateSigned'] = None self.db.save(contract) response = self.app.get('/contracts/{}?acc_token={}'.format(self.contract['id'], self.contract_token)) self.assertEqual(response.status, '200 OK') self.assertNotIn('dateSigned', response.json['data']) self.app.authorization = ('Basic', ('broker', '')) one_day_in_past = (get_now() - timedelta(days=1)).isoformat() response = self.app.post_json('/contracts/{}/changes?acc_token={}'.format(self.contract['id'], self.contract_token), {'data': {'rationale': u'причина зміни укр', 'rationale_en': u'change cause en', 'rationaleTypes': ['priceReduction'], 'contractNumber': u'№ 146', 'dateSigned': one_day_in_past}}) self.assertEqual(response.json['data']['dateSigned'], one_day_in_past) change = response.json['data'] response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change['id'], self.contract_token), {'data': {'status': 'active'}}) self.assertEqual(response.json['data']['status'], 'active') # prepare old contract change data contract = self.db.get(self.contract['id']) last_change = contract['changes'][-1] last_change['dateSigned'] = None self.db.save(contract) response = self.app.get('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], last_change['id'], self.contract_token)) self.assertEqual(response.status, '200 OK') self.assertNotIn('dateSigned', response.json['data']) response = self.app.post_json('/contracts/{}/changes?acc_token={}'.format(self.contract['id'], self.contract_token), {'data': {'rationale': u'третя причина зміни укр', 'rationale_en': u'third change cause en', 'rationaleTypes': ['priceReduction'], 'contractNumber': u'№ 148', 'dateSigned': one_day_in_past}}, status=403) self.assertEqual("Change dateSigned ({}) can't be earlier than last active change dateSigned ({})".format(one_day_in_past, last_change['date']), response.json['errors'][0]["description"]) valid_date = get_now().isoformat() response = self.app.post_json('/contracts/{}/changes?acc_token={}'.format(self.contract['id'], self.contract_token), {'data': {'rationale': u'третя причина зміни укр', 'rationale_en': u'third change cause en', 'rationaleTypes': ['priceReduction'], 'contractNumber': u'№ 148', 'dateSigned': valid_date}}) self.assertEqual(response.json['data']['dateSigned'], valid_date)
def go_to_enquiryPeriod_end(self): now = get_now() self.set_status('active.tendering', { "enquiryPeriod": { "startDate": (now - timedelta(days=28)).isoformat(), "endDate": (now - (timedelta(minutes=1) if SANDBOX_MODE else timedelta(days=1))).isoformat() }, "tenderPeriod": { "startDate": (now - timedelta(days=28)).isoformat(), "endDate": (now + (timedelta(minutes=2) if SANDBOX_MODE else timedelta(days=2))).isoformat() } })
def patch(self): """ Contract change edit """ change = self.request.validated['change'] data = self.request.validated['data'] if 'status' in data and data['status'] != change.status: # status change if data['status'] != 'active': self.request.errors.add('body', 'data', 'Can\'t update contract change in current ({}) status'.format(change.status)) self.request.errors.status = 403 return change['date'] = get_now() if apply_patch(self.request, src=change.serialize()): self.LOGGER.info('Updated contract change {}'.format(change.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'contract_change_patch'})) return {'data': change.serialize('view')}
def collection_post(self): """Add a lot """ auction = self.request.validated['auction'] if auction.status not in ['active.enquiries']: self.request.errors.add('body', 'data', 'Can\'t add lot in current ({}) auction status'.format(auction.status)) self.request.errors.status = 403 return lot = self.request.validated['lot'] lot.date = get_now() auction.lots.append(lot) if save_auction(self.request): self.LOGGER.info('Created auction lot {}'.format(lot.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'auction_lot_create'}, {'lot_id': lot.id})) self.request.response.status = 201 self.request.response.headers['Location'] = self.request.route_url('Auction Lots', auction_id=auction.id, lot_id=lot.id) return {'data': lot.serialize("view")}
def add_logging_context(event): request = event.request params = { 'API_VERSION': request.registry.settings.get('api_version', VERSION), 'TAGS': 'python,api', 'USER': str(request.authenticated_userid or ''), 'CURRENT_URL': request.url, 'CURRENT_PATH': request.path_info, 'REMOTE_ADDR': request.remote_addr or '', 'USER_AGENT': request.user_agent or '', 'REQUEST_METHOD': request.method, 'TIMESTAMP': get_now().isoformat(), 'REQUEST_ID': request.environ.get('REQUEST_ID', ''), 'CLIENT_REQUEST_ID': request.headers.get('X-Client-Request-ID', ''), } request.logging_context = params
def collection_post(self): """Add a lot """ if not self.validate_update_tender('add'): return lot = self.request.validated['lot'] lot.date = get_now() tender = self.request.validated['tender'] tender.lots.append(lot) if save_tender(self.request): self.LOGGER.info('Created tender lot {}'.format(lot.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_lot_create'}, {'lot_id': lot.id})) self.request.response.status = 201 self.request.response.headers['Location'] = self.request.route_url(self.route_name, tender_id=tender.id, lot_id=lot.id) return {'data': lot.serialize("view")}
def shouldStartAfter(self): if self.endDate: return tender = get_tender(self) lot = self.__parent__ if tender.status not in ['active.tendering', 'active.auction'] or lot.status != 'active': return if tender.status == 'active.auction' and lot.numberOfBids < 2: return if self.startDate and get_now() > calc_auction_end_time(lot.numberOfBids, self.startDate): return calc_auction_end_time(lot.numberOfBids, self.startDate).isoformat() else: decision_dates = [ datetime.combine(complaint.dateDecision.date() + timedelta(days=3), time(0, tzinfo=complaint.dateDecision.tzinfo)) for complaint in tender.complaints if complaint.dateDecision ] decision_dates.append(tender.tenderPeriod.endDate) return max(decision_dates).isoformat()
def create_change(self): response = self.app.post_json('/contracts/{}/changes?acc_token={}'.format(self.contract['id'], self.contract_token), {'data': {'rationale': u'причина зміни укр', 'rationale_en': 'change cause en', 'rationaleTypes': ['qualityImprovement']}}) self.assertEqual(response.status, '201 Created') self.assertEqual(response.content_type, 'application/json') change = response.json['data'] self.assertEqual(change['status'], 'pending') self.assertIn('date', change) response = self.app.get('/contracts/{}/changes'.format(self.contract['id'])) self.assertEqual(response.status, '200 OK') self.assertEqual(len(response.json['data']), 1) response = self.app.post_json('/contracts/{}/changes?acc_token={}'.format(self.contract['id'], self.contract_token), {'data': {'rationale': u'трататата', 'rationaleTypes': ['priceReduction']}}, status=403) self.assertEqual(response.status, '403 Forbidden') self.assertEqual(response.json['errors'], [ {"location": "body", "name": "data", "description": "Can't create new contract change while any (pending) change exists"} ]) response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change['id'], self.contract_token), {'data': {'status': 'active', 'dateSigned': get_now().isoformat()}}) self.assertEqual(response.status, '200 OK') self.assertEqual(response.json['data']['status'], 'active') response = self.app.post_json('/contracts/{}/changes?acc_token={}'.format(self.contract['id'], self.contract_token), {'data': {'rationale': u'трататата', 'rationaleTypes': ['non-existing-rationale']}}, status=422) self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.json['errors'], [ {"location": "body", "name": "rationaleTypes", "description": [["Value must be one of ['volumeCuts', 'itemPriceVariation', 'qualityImprovement', 'thirdParty', 'durationExtension', 'priceReduction', 'taxRate', 'fiscalYearExtension']."]]} ]) response = self.app.post_json('/contracts/{}/changes?acc_token={}'.format(self.contract['id'], self.contract_token), {'data': {'rationale': u'трататата', 'rationaleTypes': ['priceReduction']}}) self.assertEqual(response.status, '201 Created') change2 = response.json['data'] self.assertEqual(change2['status'], 'pending') response = self.app.get('/contracts/{}/changes'.format(self.contract['id'])) self.assertEqual(response.status, '200 OK') self.assertEqual(len(response.json['data']), 2)
def from1to2(registry): class Request(object): def __init__(self, registry): self.registry = registry len(registry.db.view('contracts/all', limit=1)) results = registry.db.iterview('contracts/all', 2 ** 10, include_docs=True, stale='update_after') docs = [] request = Request(registry) root = Root(request) for i in results: doc = i.doc if not all([i.get('url', '').startswith(registry.docservice_url) for i in doc.get('documents', [])]): contract = Contract(doc) contract.__parent__ = root doc = contract.to_primitive() doc['dateModified'] = get_now().isoformat() docs.append(doc) if len(docs) >= 2 ** 7: registry.db.update(docs) docs = [] if docs: registry.db.update(docs)
def contract_administrator_change(self): response = self.app.patch_json('/contracts/{}'.format(self.contract['id']), {'data': {'mode': u'test', "suppliers": [{ "contactPoint": { "email": "*****@*****.**", }, "address": {"postalCode": "79014"} }], 'procuringEntity': {"identifier": {"id": "11111111"}, "contactPoint": {"telephone": "102"}} }}) self.assertEqual(response.status, '200 OK') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['data']['mode'], u'test') self.assertEqual(response.json['data']["procuringEntity"]["identifier"]["id"], "11111111") self.assertEqual(response.json['data']["procuringEntity"]["contactPoint"]["telephone"], "102") self.assertEqual(response.json['data']["suppliers"][0]["contactPoint"]["email"], "*****@*****.**") self.assertEqual(response.json['data']["suppliers"][0]["contactPoint"]["telephone"], "+380 (322) 91-69-30") # old field value left untouchable self.assertEqual(response.json['data']["suppliers"][0]["address"]["postalCode"], "79014") self.assertEqual(response.json['data']["suppliers"][0]["address"]["countryName"], u"Україна") # old field value left untouchable # administrator has permissions to update only: mode, procuringEntity, suppliers response = self.app.patch_json('/contracts/{}'.format(self.contract['id']), {'data': { 'value': {'amount': 100500}, 'id': '1234' * 8, 'owner': 'kapitoshka', 'contractID': "UA-00-00-00", 'dateSigned': get_now().isoformat(), }}) self.assertEqual(response.body, 'null') response = self.app.get('/contracts/{}'.format(self.contract['id'])) self.assertEqual(response.json['data']['value']['amount'], 238) self.assertEqual(response.json['data']['id'], self.initial_data['id']) self.assertEqual(response.json['data']['owner'], self.initial_data['owner']) self.assertEqual(response.json['data']['contractID'], self.initial_data['contractID']) self.assertEqual(response.json['data']['dateSigned'], self.initial_data['dateSigned'])
def collection_post(self): """Add a lot """ auction = self.request.validated["auction"] if auction.status not in ["active.enquiries"]: self.request.errors.add( "body", "data", "Can't add lot in current ({}) auction status".format(auction.status) ) self.request.errors.status = 403 return lot = self.request.validated["lot"] lot.date = get_now() auction.lots.append(lot) if save_auction(self.request): self.LOGGER.info( "Created auction lot {}".format(lot.id), extra=context_unpack(self.request, {"MESSAGE_ID": "auction_lot_create"}, {"lot_id": lot.id}), ) self.request.response.status = 201 route = self.request.matched_route.name.replace("collection_", "") self.request.response.headers["Location"] = self.request.current_route_url( _route_name=route, lot_id=lot.id, _query={} ) return {"data": lot.serialize("view")}
def patch_tender_contract(self): response = self.app.patch_json('/contracts/{}'.format(self.contract['id']), {"data": {"title": "New Title"}}, status=403) self.assertEqual(response.status, '403 Forbidden') tender_token = self.initial_data['tender_token'] response = self.app.patch_json('/contracts/{}?acc_token={}'.format(self.contract['id'], tender_token), {"data": {"title": "New Title"}}, status=403) self.assertEqual(response.status, '403 Forbidden') response = self.app.patch_json('/contracts/{}/credentials?acc_token={}'.format(self.contract['id'], tender_token), {'data': ''}) self.assertEqual(response.status, '200 OK') token = response.json['access']['token'] response = self.app.patch_json('/contracts/{}?acc_token={}'.format(self.contract['id'], token), {"data": {"title": "New Title"}}) self.assertEqual(response.status, '200 OK') self.assertEqual(response.json['data']['title'], "New Title") # response = self.app.patch_json('/contracts/{}?acc_token={}'.format(contract['id'], token), # {"data": {"value": {"currency": "USD"}}}) # response = self.app.patch_json('/contracts/{}?acc_token={}'.format(contract['id'], token), # {"data": {"value": {"valueAddedTaxIncluded": False}}}) response = self.app.patch_json('/contracts/{}?acc_token={}'.format(self.contract['id'], token), {"data": {"amountPaid": {"amount": 900, "currency": "USD", "valueAddedTaxIncluded": False}}}) self.assertEqual(response.status, '200 OK') self.assertEqual(response.json['data']['amountPaid']['amount'], 900) self.assertEqual(response.json['data']['amountPaid']['currency'], "UAH") self.assertEqual(response.json['data']['amountPaid']['valueAddedTaxIncluded'], True) response = self.app.patch_json('/contracts/{}?acc_token={}'.format(self.contract['id'], token), {"data": {"value": {"amount": 235}}}) self.assertEqual(response.status, '200 OK') self.assertEqual(response.json['data']['value']['amount'], 235) self.assertEqual(response.json['data']['value']['currency'], "UAH") self.assertEqual(response.json['data']['value']['valueAddedTaxIncluded'], True) self.assertEqual(response.json['data']['amountPaid']['amount'], 900) self.assertEqual(response.json['data']['amountPaid']['currency'], "UAH") self.assertEqual(response.json['data']['amountPaid']['valueAddedTaxIncluded'], True) response = self.app.patch_json('/contracts/{}?acc_token={}'.format(self.contract['id'], token), {"data": {"value": {"currency": "USD", "valueAddedTaxIncluded": False}}}) self.assertEqual(response.status, '200 OK') self.assertEqual(response.json['data']['value']['currency'], "USD") self.assertEqual(response.json['data']['value']['valueAddedTaxIncluded'], False) self.assertEqual(response.json['data']['value']['amount'], 235) self.assertEqual(response.json['data']['amountPaid']['amount'], 900) self.assertEqual(response.json['data']['amountPaid']['currency'], "USD") self.assertEqual(response.json['data']['amountPaid']['valueAddedTaxIncluded'], False) custom_period_start_date = get_now().isoformat() custom_period_end_date = (get_now() + timedelta(days=3)).isoformat() response = self.app.patch_json('/contracts/{}?acc_token={}'.format(self.contract['id'], token), {"data": {"period": {'startDate': custom_period_start_date, 'endDate': custom_period_end_date}}}) self.assertEqual(response.status, '200 OK') response = self.app.patch_json('/contracts/{}?acc_token={}'.format(self.contract['id'], token), {"data": {"status": "terminated", "amountPaid": {"amount": 100500}, "terminationDetails": "sink"}}) self.assertEqual(response.status, '200 OK') self.assertEqual(response.json['data']['status'], 'terminated') self.assertEqual(response.json['data']['amountPaid']['amount'], 100500) self.assertEqual(response.json['data']['terminationDetails'], 'sink') response = self.app.patch_json('/contracts/{}?acc_token={}'.format(self.contract['id'], token), {"data": {"status": "active"}}, status=403) self.assertEqual(response.status, '403 Forbidden') response = self.app.patch_json('/contracts/{}?acc_token={}'.format(self.contract['id'], token), {"data": {"title": "fff"}}, status=403) self.assertEqual(response.status, '403 Forbidden') response = self.app.patch_json('/contracts/some_id', {"data": {"status": "active"}}, 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'contract_id'} ]) response = self.app.get('/contracts/{}'.format(self.contract['id'])) self.assertEqual(response.status, '200 OK') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['data']["status"], "terminated") self.assertEqual(response.json['data']["value"]['amount'], 235) self.assertEqual(response.json['data']['period']['startDate'], custom_period_start_date) self.assertEqual(response.json['data']['period']['endDate'], custom_period_end_date) self.assertEqual(response.json['data']['amountPaid']['amount'], 100500) self.assertEqual(response.json['data']['terminationDetails'], 'sink')
def validate_tender_auction_data(request): data = validate_patch_tender_data(request) tender = request.validated['tender'] if tender.status != 'active.auction': request.errors.add( 'body', 'data', 'Can\'t {} in current ({}) tender status'.format( 'report auction results' if request.method == 'POST' else 'update auction urls', tender.status)) request.errors.status = 403 raise error_handler(request.errors) lot_id = request.matchdict.get('auction_lot_id') if tender.lots and any( [i.status != 'active' for i in tender.lots if i.id == lot_id]): request.errors.add( 'body', 'data', 'Can {} only in active lot status'.format( 'report auction results' if request.method == 'POST' else 'update auction urls')) request.errors.status = 403 raise error_handler(request.errors) if data is not None: bids = data.get('bids', []) tender_bids_ids = [i.id for i in tender.bids] if len(bids) != len(tender.bids): request.errors.add( 'body', 'bids', "Number of auction results did not match the number of tender bids" ) request.errors.status = 422 raise error_handler(request.errors) if set([i['id'] for i in bids]) != set(tender_bids_ids): request.errors.add( 'body', 'bids', "Auction bids should be identical to the tender bids") request.errors.status = 422 raise error_handler(request.errors) data['bids'] = [ x for (y, x) in sorted( zip([tender_bids_ids.index(i['id']) for i in bids], bids)) ] if data.get('lots'): tender_lots_ids = [i.id for i in tender.lots] if len(data.get('lots', [])) != len(tender.lots): request.errors.add( 'body', 'lots', "Number of lots did not match the number of tender lots") request.errors.status = 422 raise error_handler(request.errors) if set([i['id'] for i in data.get('lots', [])]) != set( [i.id for i in tender.lots]): request.errors.add( 'body', 'lots', "Auction lots should be identical to the tender lots") request.errors.status = 422 raise error_handler(request.errors) data['lots'] = [ x if x['id'] == lot_id else {} for (y, x) in sorted( zip([ tender_lots_ids.index(i['id']) for i in data.get('lots', []) ], data.get('lots', []))) ] if tender.lots: for index, bid in enumerate(bids): if (getattr(tender.bids[index], 'status', 'active') or 'active') == 'active': if len(bid.get('lotValues', [])) != len( tender.bids[index].lotValues): request.errors.add('body', 'bids', [{ u'lotValues': [ u'Number of lots of auction results did not match the number of tender lots' ] }]) request.errors.status = 422 raise error_handler(request.errors) for lot_index, lotValue in enumerate( tender.bids[index].lotValues): if lotValue.relatedLot != bid.get( 'lotValues', [])[lot_index].get( 'relatedLot', None): request.errors.add('body', 'bids', [{ u'lotValues': [{ u'relatedLot': [ 'relatedLot should be one of lots of bid' ] }] }]) request.errors.status = 422 raise error_handler(request.errors) for bid_index, bid in enumerate(data['bids']): if 'lotValues' in bid: bid['lotValues'] = [ x if x['relatedLot'] == lot_id and (getattr( tender.bids[bid_index].lotValues[lotValue_index], 'status', 'active') or 'active') == 'active' else {} for lotValue_index, x in enumerate(bid['lotValues']) ] else: data = {} if request.method == 'POST': now = get_now().isoformat() if tender.lots: data['lots'] = [{ 'auctionPeriod': { 'endDate': now } } if i.id == lot_id else {} for i in tender.lots] else: data['auctionPeriod'] = {'endDate': now} request.validated['data'] = data
def time_shift(self, status, extra=None): now = get_now() tender = self.db.get(self.tender_id) data = {} if status == 'enquiryPeriod_ends': data.update({ "enquiryPeriod": { "startDate": (now - timedelta(days=28)).isoformat(), "endDate": (now - timedelta(days=1)).isoformat() }, "tenderPeriod": { "startDate": (now - timedelta(days=28)).isoformat(), "endDate": (now + timedelta(days=2)).isoformat() }, }) if status == 'active.pre-qualification': data.update({ "enquiryPeriod": { "startDate": (now - TENDERING_DURATION).isoformat(), "endDate": (now - QUESTIONS_STAND_STILL).isoformat() }, "tenderPeriod": { "startDate": (now - TENDERING_DURATION).isoformat(), "endDate": (now).isoformat(), } }) elif status == 'active.pre-qualification.stand-still': data.update({ "enquiryPeriod": { "startDate": (now - TENDERING_DURATION).isoformat(), "endDate": (now - QUESTIONS_STAND_STILL).isoformat() }, "tenderPeriod": { "startDate": (now - TENDERING_DURATION).isoformat(), "endDate": (now).isoformat(), }, "qualificationPeriod": { "startDate": (now).isoformat(), }, }) if 'lots' in tender and tender['lots']: data['lots'] = [] for index, lot in enumerate(tender['lots']): lot_data = {'id': lot['id']} if lot['status'] is 'active': lot_data["auctionPeriod"] = { "startDate": (now + COMPLAINT_STAND_STILL).isoformat() } data['lots'].append(lot_data) else: data.update({ "auctionPeriod": { "startDate": (now + COMPLAINT_STAND_STILL).isoformat() } }) elif status == 'active.auction': data.update({ "enquiryPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL).isoformat(), "endDate": (now - COMPLAINT_STAND_STILL - TENDERING_DURATION + QUESTIONS_STAND_STILL).isoformat() }, "tenderPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL).isoformat(), "endDate": (now - COMPLAINT_STAND_STILL).isoformat() }, "qualificationPeriod": { "startDate": (now - COMPLAINT_STAND_STILL).isoformat(), "endDate": (now).isoformat() } }) if 'lots' in tender and tender['lots']: data['lots'] = [] for index, lot in enumerate(tender['lots']): lot_data = {'id': lot['id']} if lot['status'] == 'active': lot_data["auctionPeriod"] = { "startDate": (now).isoformat() } data['lots'].append(lot_data) else: data.update({ "auctionPeriod": { "startDate": now.isoformat() } }) elif status == 'complete': data.update({ "enquiryPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL - timedelta(days=3)).isoformat(), "endDate": (now - QUESTIONS_STAND_STILL - COMPLAINT_STAND_STILL - timedelta(days=3)).isoformat() }, "tenderPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL - timedelta(days=3)).isoformat(), "endDate": (now - COMPLAINT_STAND_STILL - timedelta(days=3)).isoformat() }, "auctionPeriod": { "startDate": (now - timedelta(days=3)).isoformat(), "endDate": (now - timedelta(days=2)).isoformat() }, "awardPeriod": { "startDate": (now - timedelta(days=1)).isoformat(), "endDate": (now).isoformat() } }) if self.initial_lots: data.update({ 'lots': [ { "auctionPeriod": { "startDate": (now - timedelta(days=3)).isoformat(), "endDate": (now - timedelta(days=2)).isoformat() } } for i in self.initial_lots ] }) if extra: data.update(extra) tender.update(apply_data_patch(tender, data)) self.db.save(tender)
def create_tender_lot_qualification_complaint(self): response = self.app.post_json( "/tenders/{}/qualifications/{}/complaints?acc_token={}".format( self.tender_id, self.qualification_id, self.initial_bids_tokens.values()[0] ), { "data": test_complaint }, ) self.assertEqual(response.status, "201 Created") self.assertEqual(response.content_type, "application/json") complaint = response.json["data"] complaint_token = response.json["access"]["token"] self.assertIn("id", complaint) self.assertIn(complaint["id"], response.headers["Location"]) if RELEASE_2020_04_19 > get_now(): self.assertEqual(complaint["author"]["name"], self.author_data["name"]) else: self.assertEqual(response.json["data"]["status"], "draft") with change_auth(self.app, ("Basic", ("bot", ""))): response = self.app.patch_json( "/tenders/{}/qualifications/{}/complaints/{}".format( self.tender_id, self.qualification_id, complaint["id"]), {"data": {"status": "pending"}}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.content_type, "application/json") self.assertEqual(response.json["data"]["status"], "pending") # set complaint status to invalid to be able to cancel the tender with change_auth(self.app, ("Basic", ("reviewer", ""))): response = self.app.patch_json( "/tenders/{}/qualifications/{}/complaints/{}?acc_token={}".format( self.tender_id, self.qualification_id, complaint["id"], complaint_token ), {"data": { "status": "invalid", "rejectReason": "buyerViolationsCorrected" }}, ) self.assertEqual(response.status, "200 OK") if RELEASE_2020_04_19 > get_now(): # Test for old rules # In new rules there will be 403 error self.cancel_tender() response = self.app.post_json( "/tenders/{}/qualifications/{}/complaints?acc_token={}".format( self.tender_id, self.qualification_id, self.initial_bids_tokens.values()[0] ), {"data": test_draft_claim}, 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 complaint in current (cancelled) tender status" )
def validate_add_complaint_not_in_qualification_period(request): tender = request.validated['tender'] if tender.qualificationPeriod and \ (tender.qualificationPeriod.startDate and tender.qualificationPeriod.startDate > get_now() or tender.qualificationPeriod.endDate and tender.qualificationPeriod.endDate < get_now()): raise_operation_error(request, 'Can add complaint only in qualificationPeriod')
def patch(self): """Tender Edit (partial) For example here is how procuring entity can change number of items to be procured and total Value of a tender: .. sourcecode:: http PATCH /tenders/4879d3f8ee2443169b5fbbc9f89fa607 HTTP/1.1 Host: example.com Accept: application/json { "data": { "value": { "amount": 600 }, "itemsToBeProcured": [ { "quantity": 6 } ] } } And here is the response to be expected: .. sourcecode:: http HTTP/1.0 200 OK Content-Type: application/json { "data": { "id": "4879d3f8ee2443169b5fbbc9f89fa607", "tenderID": "UA-64e93250be76435397e8c992ed4214d1", "dateModified": "2014-10-27T08:12:34.956Z", "value": { "amount": 600 }, "itemsToBeProcured": [ { "quantity": 6 } ] } } """ tender = self.context config = getAdapter(tender, IContentConfigurator) data = self.request.validated['data'] now = get_now() if self.request.authenticated_role == 'tender_owner' and \ self.request.validated['tender_status'] == 'active.tendering': if 'tenderPeriod' in data and 'endDate' in data['tenderPeriod']: self.request.validated['tender'].tenderPeriod.import_data(data['tenderPeriod']) validate_tender_period_extension(self.request) self.request.registry.notify(TenderInitializeEvent(self.request.validated['tender'])) self.request.validated['data']["enquiryPeriod"] = \ self.request.validated['tender'].enquiryPeriod.serialize() apply_patch(self.request, save=False, src=self.request.validated['tender_src']) if self.request.authenticated_role == 'chronograph': check_status(self.request) elif self.request.authenticated_role == 'tender_owner' and tender.status == 'active.tendering': tender.invalidate_bids_data() elif self.request.authenticated_role == 'tender_owner' and \ self.request.validated['tender_status'] == 'active.pre-qualification' and \ tender.status == "active.pre-qualification.stand-still": active_lots = [lot.id for lot in tender.lots if lot.status == 'active'] if tender.lots else [None] if any([i['status'] in self.request.validated['tender'].block_complaint_status for q in self.request.validated['tender']['qualifications'] for i in q['complaints'] if q['lotID'] in active_lots]): raise_operation_error( self.request, 'Can\'t switch to \'active.pre-qualification.stand-still\' before resolve all complaints' ) if all_bids_are_reviewed(self.request): normalized_date = calculate_normalized_date(now, tender, True) tender.qualificationPeriod.endDate = calculate_business_date( normalized_date, config.qualification_complaint_stand_still, self.request.validated['tender'] ) tender.check_auction_time() else: raise_operation_error( self.request, 'Can\'t switch to \'active.pre-qualification.stand-still\' while not all bids are qualified' ) elif self.request.authenticated_role == 'tender_owner' and \ self.request.validated['tender_status'] == 'active.qualification' and \ tender.status == "active.qualification.stand-still": active_lots = [lot.id for lot in tender.lots if lot.status == 'active'] if tender.lots else [None] if any([i['status'] in self.request.validated['tender'].block_complaint_status for a in self.request.validated['tender']['awards'] for i in a['complaints'] if a['lotID'] in active_lots]): raise_operation_error( self.request, 'Can\'t switch to \'active.qualification.stand-still\' before resolve all complaints' ) if all_awards_are_reviewed(self.request): normalized_date = calculate_normalized_date(now, tender, True) tender.awardPeriod.endDate = calculate_business_date( normalized_date, config.qualification_complaint_stand_still, self.request.validated['tender'] ) for award in [a for a in tender.awards if a.status != 'cancelled']: award['complaintPeriod'] = { 'startDate': now.isoformat(), 'endDate': tender.awardPeriod.endDate.isoformat() } else: raise_operation_error( self.request, 'Can\'t switch to \'active.qualification.stand-still\' while not all awards are qualified' ) save_tender(self.request) self.LOGGER.info('Updated tender {}'.format(tender.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_patch'})) return {'data': tender.serialize(tender.status)}
def validate_dateSigned(self, data, value): if value and value > get_now(): raise ValidationError(u"Contract signature date can't be in the future")
def validate_dateSigned(self, data, value): if value and isinstance(data['__parent__'], Model): if value > get_now(): raise ValidationError( u"Contract signature date can't be in the future")
def create_tender_invalid(self): request_path = '/tenders' response = self.app.post(request_path, 'data', status=415) self.assertEqual(response.status, '415 Unsupported Media Type') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual(response.json['errors'], [{ u'description': u"Content-Type header should be one of ['application/json']", u'location': u'header', u'name': u'Content-Type' }]) response = self.app.post(request_path, 'data', content_type='application/json', status=422) self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual(response.json['errors'], [{ u'description': u'No JSON object could be decoded', u'location': u'body', u'name': u'data' }]) response = self.app.post_json(request_path, 'data', status=422) self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual(response.json['errors'], [{ u'description': u'Data not available', u'location': u'body', u'name': u'data' }]) response = self.app.post_json(request_path, {'not_data': {}}, status=422) self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual(response.json['errors'], [{ u'description': u'Data not available', u'location': u'body', u'name': u'data' }]) response = self.app.post_json(request_path, {'data': []}, status=422) self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual(response.json['errors'], [{ u'description': u'Data not available', u'location': u'body', u'name': u'data' }]) response = self.app.post_json( request_path, {'data': { 'procurementMethodType': 'invalid_value' }}, status=415) self.assertEqual(response.status, '415 Unsupported Media Type') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual(response.json['errors'], [{ u'description': u'Not implemented', u'location': u'data', u'name': u'procurementMethodType' }]) response = self.app.post_json(request_path, { 'data': { 'procurementMethodType': 'esco.EU', 'invalid_field': 'invalid_value' } }, status=422) self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual(response.json['errors'], [{ u'description': u'Rogue field', u'location': u'body', u'name': u'invalid_field' }]) response = self.app.post_json(request_path, { 'data': { 'procurementMethodType': 'esco.EU', 'minValue': 'invalid_value' } }, status=422) self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual(response.json['errors'], [{ u'description': [ u'Please use a mapping for this field or Value instance instead of unicode.' ], u'location': u'body', u'name': u'minValue' }]) response = self.app.post_json(request_path, { 'data': { 'procurementMethodType': 'esco.EU', 'procurementMethod': 'invalid_value' } }, status=422) self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertIn( { u'description': [u"Value must be one of ['open', 'selective', 'limited']."], u'location': u'body', u'name': u'procurementMethod' }, response.json['errors']) self.assertIn( { u'description': [u'This field is required.'], u'location': u'body', u'name': u'tenderPeriod' }, response.json['errors']) self.assertIn( { u'description': [u'This field is required.'], u'location': u'body', u'name': u'minimalStep' }, response.json['errors']) self.assertIn( { u'description': [u'This field is required.'], u'location': u'body', u'name': u'items' }, response.json['errors']) self.assertIn( { u'description': [u'This field is required.'], u'location': u'body', u'name': u'minValue' }, response.json['errors']) self.assertIn( { u'description': [u'This field is required.'], u'location': u'body', u'name': u'items' }, response.json['errors']) response = self.app.post_json(request_path, { 'data': { 'procurementMethodType': 'esco.EU', 'enquiryPeriod': { 'endDate': 'invalid_value' } } }, status=422) self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual(response.json['errors'], [{ u'description': { u'endDate': [u"Could not parse invalid_value. Should be ISO8601."] }, u'location': u'body', u'name': u'enquiryPeriod' }]) response = self.app.post_json(request_path, { 'data': { 'procurementMethodType': 'esco.EU', 'enquiryPeriod': { 'endDate': '9999-12-31T23:59:59.999999' } } }, status=422) self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual(response.json['errors'], [{ u'description': { u'endDate': [u'date value out of range'] }, u'location': u'body', u'name': u'enquiryPeriod' }]) data = self.initial_data['tenderPeriod'] self.initial_data['tenderPeriod'] = { 'startDate': '2014-10-31T00:00:00', 'endDate': '2014-10-01T00:00:00' } response = self.app.post_json(request_path, {'data': self.initial_data}, status=422) self.initial_data['tenderPeriod'] = data self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual(response.json['errors'], [{ u'description': { u'startDate': [u'period should begin before its end'] }, u'location': u'body', u'name': u'tenderPeriod' }]) self.initial_data['tenderPeriod']['startDate'] = ( get_now() - timedelta(minutes=30)).isoformat() response = self.app.post_json(request_path, {'data': self.initial_data}, status=422) del self.initial_data['tenderPeriod']['startDate'] self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual(response.json['errors'], [{ u'description': [u'tenderPeriod.startDate should be in greater than current date'], u'location': u'body', u'name': u'tenderPeriod' }]) now = get_now() self.initial_data['awardPeriod'] = { 'startDate': now.isoformat(), 'endDate': now.isoformat() } response = self.app.post_json(request_path, {'data': self.initial_data}, status=422) del self.initial_data['awardPeriod'] self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual( response.json['errors'], [{ u'description': [u'period should begin after tenderPeriod'], u'location': u'body', u'name': u'awardPeriod' }]) self.initial_data['auctionPeriod'] = { 'startDate': (now + timedelta(days=35)).isoformat(), 'endDate': (now + timedelta(days=35)).isoformat() } self.initial_data['awardPeriod'] = { 'startDate': (now + timedelta(days=34)).isoformat(), 'endDate': (now + timedelta(days=34)).isoformat() } response = self.app.post_json(request_path, {'data': self.initial_data}, status=422) del self.initial_data['auctionPeriod'] del self.initial_data['awardPeriod'] self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual( response.json['errors'], [{ u'description': [u'period should begin after auctionPeriod'], u'location': u'body', u'name': u'awardPeriod' }]) data = self.initial_data['minimalStep'] self.initial_data['minimalStep'] = {'amount': '1000.0'} response = self.app.post_json(request_path, {'data': self.initial_data}, status=422) self.initial_data['minimalStep'] = data self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual( response.json['errors'], [{ u'description': [u'value should be less than minValue of tender'], u'location': u'body', u'name': u'minimalStep' }]) data = self.initial_data['minimalStep'] self.initial_data['minimalStep'] = { 'amount': '100.0', 'valueAddedTaxIncluded': False } response = self.app.post_json(request_path, {'data': self.initial_data}, status=422) self.initial_data['minimalStep'] = data self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual(response.json['errors'], [{ u'description': [ u'valueAddedTaxIncluded should be identical to valueAddedTaxIncluded of minValue of tender' ], u'location': u'body', u'name': u'minimalStep' }]) data = self.initial_data['minimalStep'] self.initial_data['minimalStep'] = {'amount': '100.0', 'currency': "USD"} response = self.app.post_json(request_path, {'data': self.initial_data}, status=422) self.initial_data['minimalStep'] = data self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual(response.json['errors'], [{ u'description': [u'currency should be identical to currency of minValue of tender'], u'location': u'body', u'name': u'minimalStep' }]) data = self.initial_data["items"][0].pop("additionalClassifications") if get_now() > CPV_ITEMS_CLASS_FROM: cpv_code = self.initial_data["items"][0]['classification']['id'] self.initial_data["items"][0]['classification']['id'] = '99999999-9' response = self.app.post_json(request_path, {'data': self.initial_data}, status=422) self.initial_data["items"][0]["additionalClassifications"] = data if get_now() > CPV_ITEMS_CLASS_FROM: self.initial_data["items"][0]['classification']['id'] = cpv_code self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual(response.json['errors'], [{ u'description': [{ u'additionalClassifications': [u'This field is required.'] }], u'location': u'body', u'name': u'items' }]) data = self.initial_data["items"][0]["additionalClassifications"][0][ "scheme"] self.initial_data["items"][0]["additionalClassifications"][0][ "scheme"] = 'Не ДКПП' if get_now() > CPV_ITEMS_CLASS_FROM: cpv_code = self.initial_data["items"][0]['classification']['id'] self.initial_data["items"][0]['classification']['id'] = '99999999-9' response = self.app.post_json(request_path, {'data': self.initial_data}, status=422) self.initial_data["items"][0]["additionalClassifications"][0][ "scheme"] = data if get_now() > CPV_ITEMS_CLASS_FROM: self.initial_data["items"][0]['classification']['id'] = cpv_code self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') if get_now() > CPV_ITEMS_CLASS_FROM: self.assertEqual(response.json['errors'], [{ u'description': [{ u'additionalClassifications': [ u"One of additional classifications should be one of [ДК003, ДК015, ДК018, specialNorms]." ] }], u'location': u'body', u'name': u'items' }]) else: self.assertEqual(response.json['errors'], [{ u'description': [{ u'additionalClassifications': [ u"One of additional classifications should be one of [ДКПП, NONE, ДК003, ДК015, ДК018]." ] }], u'location': u'body', u'name': u'items' }]) data = self.initial_data["procuringEntity"]["contactPoint"]["telephone"] del self.initial_data["procuringEntity"]["contactPoint"]["telephone"] response = self.app.post_json(request_path, {'data': self.initial_data}, status=422) self.initial_data["procuringEntity"]["contactPoint"]["telephone"] = data self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual(response.json['errors'], [{ u'description': { u'contactPoint': { u'email': [u'telephone or email should be present'] } }, u'location': u'body', u'name': u'procuringEntity' }]) data = self.initial_data["items"][0].copy() classification = data['classification'].copy() classification["id"] = u'19212310-1' data['classification'] = classification self.initial_data["items"] = [self.initial_data["items"][0], data] response = self.app.post_json(request_path, {'data': self.initial_data}, status=422) self.initial_data["items"] = self.initial_data["items"][:1] self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual(response.json['errors'], [{ u'description': [u'CPV group of items be identical'], u'location': u'body', u'name': u'items' }]) data = deepcopy(self.initial_data) del data["items"][0]['deliveryDate'] response = self.app.post_json(request_path, {'data': data}, status=422) self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.json['status'], 'error') self.assertEqual( response.json['errors'], [{ u'description': [{ u'deliveryDate': [u'This field is required.'] }], u'location': u'body', u'name': u'items' }])
def patch_tender_contract(self): response = self.app.get("/tenders/{}/contracts".format(self.tender_id)) contract = response.json["data"][0] self.assertEqual(contract["value"]["amountNet"], self.expected_contract_amount) response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "value": { "amountNet": contract["value"]["amount"] - 1 } }}, ) self.assertEqual(response.status, "200 OK") fake_contractID = "myselfID" fake_items_data = [{"description": "New Description"}] fake_suppliers_data = [{"name": "New Name"}] self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), { "data": { "contractID": fake_contractID, "items": fake_items_data, "suppliers": fake_suppliers_data } }, ) response = self.app.get("/tenders/{}/contracts/{}".format( self.tender_id, contract["id"])) self.assertNotEqual(fake_contractID, response.json["data"]["contractID"]) self.assertNotEqual(fake_items_data, response.json["data"]["items"]) self.assertNotEqual(fake_suppliers_data, response.json["data"]["suppliers"]) patch_fields = { "currency": "USD", "amountPerformance": 0, "yearlyPaymentsPercentage": 0, "annualCostsReduction": 0, "contractDuration": { "years": 9 }, } for field, value in patch_fields.items(): response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format( self.tender_id, contract["id"], self.tender_token), {"data": { "value": { field: value } }}, status=403, ) self.assertEqual(response.status_code, 403, field) self.assertEqual(response.json["errors"][0]["description"], "Can't update {} for contract value".format(field)) response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "value": { "amountNet": self.expected_contract_amount + 1 } }}, status=403, ) self.assertEqual(response.status_code, 403) self.assertEqual( response.json["errors"][0]["description"], "Amount should be greater than amountNet and differ by no more than 20.0%", ) response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "value": { "amountNet": 10 } }}, status=403, ) self.assertEqual(response.status_code, 403) self.assertIn( "Amount should be greater than amountNet and differ by no more than 20.0%", response.json["errors"][0]["description"], ) response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "value": { "amountNet": self.expected_contract_amount - 1 } }}, ) self.assertEqual(response.status_code, 200) response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "status": "active" }}, status=403, ) self.assertEqual(response.status, "403 Forbidden") self.assertEqual(response.content_type, "application/json") self.assertIn("Can't sign contract before stand-still period end (", response.json["errors"][0]["description"]) self.set_status("complete", {"status": "active.awarded"}) token = self.initial_bids_tokens[self.initial_bids[0]["id"]] response = self.app.post_json( "/tenders/{}/awards/{}/complaints?acc_token={}".format( self.tender_id, self.award_id, token), {"data": test_draft_complaint}, ) self.assertEqual(response.status, "201 Created") complaint = response.json["data"] owner_token = response.json["access"]["token"] if get_now() < RELEASE_2020_04_19: response = self.app.patch_json( "/tenders/{}/awards/{}/complaints/{}?acc_token={}".format( self.tender_id, self.award_id, complaint["id"], owner_token), {"data": { "status": "pending" }}, ) else: with change_auth(self.app, ("Basic", ("bot", ""))): response = self.app.patch_json( "/tenders/{}/awards/{}/complaints/{}".format( self.tender_id, self.award_id, complaint["id"]), {"data": { "status": "pending" }}, ) self.assertEqual(response.status, "200 OK") tender = self.db.get(self.tender_id) for i in tender.get("awards", []): i["complaintPeriod"]["endDate"] = i["complaintPeriod"]["startDate"] self.db.save(tender) response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "dateSigned": i["complaintPeriod"]["endDate"] }}, status=422, ) self.assertEqual(response.status, "422 Unprocessable Entity") self.assertEqual( response.json["errors"], [{ "description": [ "Contract signature date should be after award complaint period end date ({})" .format(i["complaintPeriod"]["endDate"]) ], "location": "body", "name": "dateSigned", }], ) one_hour_in_future = (get_now() + timedelta(hours=1)).isoformat() response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "dateSigned": one_hour_in_future }}, status=422, ) self.assertEqual(response.status, "422 Unprocessable Entity") self.assertEqual( response.json["errors"], [{ "description": ["Contract signature date can't be in the future"], "location": "body", "name": "dateSigned", }], ) custom_signature_date = get_now().isoformat() response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "dateSigned": custom_signature_date }}, ) self.assertEqual(response.status, "200 OK") response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "status": "active" }}, status=403, ) self.assertEqual(response.status, "403 Forbidden") self.assertEqual(response.content_type, "application/json") self.assertEqual(response.json["errors"][0]["description"], "Can't sign contract before reviewing all complaints") with change_auth(self.app, ("Basic", ("reviewer", ""))): response = self.app.patch_json( "/tenders/{}/awards/{}/complaints/{}?acc_token={}".format( self.tender_id, self.award_id, complaint["id"], owner_token), { "data": { "status": "invalid", "rejectReason": "buyerViolationsCorrected" } }, ) self.assertEqual(response.status, "200 OK") 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.content_type, "application/json") self.assertEqual(response.json["data"]["status"], "active") response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), { "data": { "value": { "annualCostsReduction": [780.5] * 21, "yearlyPaymentsPercentage": 0.9, "contractDuration": { "years": 10 }, }, "contractID": "myselfID", "title": "New Title", "items": [{ "description": "New Description" }], "suppliers": [{ "name": "New Name" }], } }, status=403, ) self.assertEqual(response.status, "403 Forbidden") self.assertEqual( response.json["errors"][0]["description"], "Can't update contract in current (complete) tender status") response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "status": "active" }}, 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 contract in current (complete) tender status") response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "status": "pending" }}, 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 contract in current (complete) tender status") response = self.app.patch_json("/tenders/{}/contracts/some_id".format( self.tender_id), {"data": { "status": "active" }}, 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"], [{ "description": "Not Found", "location": "url", "name": "contract_id" }]) response = self.app.patch_json("/tenders/some_id/contracts/some_id", {"data": { "status": "active" }}, 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"], [{ "description": "Not Found", "location": "url", "name": "tender_id" }]) response = self.app.get("/tenders/{}/contracts/{}".format( self.tender_id, contract["id"])) self.assertEqual(response.status, "200 OK") self.assertEqual(response.content_type, "application/json") self.assertEqual(response.json["data"]["status"], "active") self.assertEqual(response.json["data"]["value"]["amountPerformance"], self.expected_contract_amountPerformance) self.assertEqual(response.json["data"]["value"]["amount"], self.expected_contract_amount) self.assertNotEqual(response.json["data"]["value"]["amountNet"], response.json["data"]["value"]["amount"]) self.assertEqual(response.json["data"]["value"]["amountNet"], self.expected_contract_amount - 1)
def patch_eu(self): """Tender Edit (partial) For example here is how procuring entity can change number of items to be procured and total Value of a tender: .. sourcecode:: http PATCH /tenders/4879d3f8ee2443169b5fbbc9f89fa607 HTTP/1.1 Host: example.com Accept: application/json { "data": { "value": { "amount": 600 }, "itemsToBeProcured": [ { "quantity": 6 } ] } } And here is the response to be expected: .. sourcecode:: http HTTP/1.0 200 OK Content-Type: application/json { "data": { "id": "4879d3f8ee2443169b5fbbc9f89fa607", "tenderID": "UA-64e93250be76435397e8c992ed4214d1", "dateModified": "2014-10-27T08:12:34.956Z", "value": { "amount": 600 }, "itemsToBeProcured": [ { "quantity": 6 } ] } } """ tender = self.context data = self.request.validated["data"] if (self.request.authenticated_role == "tender_owner" and self.request.validated["tender_status"] == "active.tendering"): if "tenderPeriod" in data and "endDate" in data["tenderPeriod"]: self.request.validated["tender"].tenderPeriod.import_data( data["tenderPeriod"]) validate_tender_period_extension(self.request) self.request.registry.notify( TenderInitializeEvent(self.request.validated["tender"])) self.request.validated["data"][ "enquiryPeriod"] = self.request.validated[ "tender"].enquiryPeriod.serialize() apply_patch(self.request, save=False, src=self.request.validated["tender_src"]) if self.request.authenticated_role == "chronograph": check_status(self.request) elif self.request.authenticated_role == "tender_owner" and tender.status == "active.tendering": tender.invalidate_bids_data() elif (self.request.authenticated_role == "tender_owner" and self.request.validated["tender_status"] == "active.pre-qualification" and tender.status == "active.pre-qualification.stand-still"): if all_bids_are_reviewed(self.request): tender.qualificationPeriod.endDate = calculate_tender_business_date( get_now(), COMPLAINT_STAND_STILL, self.request.validated["tender"]) tender.check_auction_time() else: raise_operation_error( self.request, "Can't switch to 'active.pre-qualification.stand-still' while not all bids are qualified" ) elif (self.request.authenticated_role == "tender_owner" and self.request.validated["tender_status"] == "active.pre-qualification" and tender.status != "active.pre-qualification.stand-still"): raise_operation_error(self.request, "Can't update tender status") save_tender(self.request) self.LOGGER.info("Updated tender {}".format(tender.id), extra=context_unpack(self.request, {"MESSAGE_ID": "tender_patch"})) return {"data": tender.serialize(tender.status)}
def add_next_award(request): tender = request.validated["tender"] now = get_now() if not tender.awardPeriod: tender.awardPeriod = type(tender).awardPeriod({}) if not tender.awardPeriod.startDate: tender.awardPeriod.startDate = now if tender.lots: statuses = set() for lot in tender.lots: if lot.status != "active": continue lot_awards = [i for i in tender.awards if i.lotID == lot.id] if lot_awards and lot_awards[-1].status in ["pending", "active"]: statuses.add(lot_awards[-1].status if lot_awards else "unsuccessful") continue lot_items = [i.id for i in tender.items if i.relatedLot == lot.id] features = [ i for i in (tender.features or []) if i.featureOf == "tenderer" or i.featureOf == "lot" and i.relatedItem == lot.id or i.featureOf == "item" and i.relatedItem in lot_items ] codes = [i.code for i in features] bids = [ { "id": bid.id, "value": [i for i in bid.lotValues if lot.id == i.relatedLot][0].value, "tenderers": bid.tenderers, "parameters": [i for i in bid.parameters if i.code in codes], "date": [i for i in bid.lotValues if lot.id == i.relatedLot][0].date, } for bid in tender.bids if lot.id in [i.relatedLot for i in bid.lotValues] ] if not bids: lot.status = "unsuccessful" statuses.add("unsuccessful") continue unsuccessful_awards = [i.bid_id for i in lot_awards if i.status == "unsuccessful"] bids = chef(bids, features, unsuccessful_awards) if bids: bid = bids[0] award = type(tender).awards.model_class( { "bid_id": bid["id"], "lotID": lot.id, "status": "pending", "value": bid["value"], "date": get_now(), "suppliers": bid["tenderers"], "complaintPeriod": {"startDate": now.isoformat()}, } ) award.__parent__ = tender tender.awards.append(award) request.response.headers["Location"] = request.route_url( "{}:Tender Awards".format(tender.procurementMethodType), tender_id=tender.id, award_id=award["id"] ) statuses.add("pending") else: statuses.add("unsuccessful") if statuses.difference(set(["unsuccessful", "active"])): tender.awardPeriod.endDate = None tender.status = "active.qualification" else: tender.awardPeriod.endDate = now tender.status = "active.awarded" else: if not tender.awards or tender.awards[-1].status not in ["pending", "active"]: unsuccessful_awards = [i.bid_id for i in tender.awards if i.status == "unsuccessful"] bids = chef(tender.bids, tender.features or [], unsuccessful_awards) if bids: bid = bids[0].serialize() award = type(tender).awards.model_class( { "bid_id": bid["id"], "status": "pending", "date": get_now(), "value": bid["value"], "suppliers": bid["tenderers"], "complaintPeriod": {"startDate": get_now().isoformat()}, } ) award.__parent__ = tender tender.awards.append(award) request.response.headers["Location"] = request.route_url( "{}:Tender Awards".format(tender.procurementMethodType), tender_id=tender.id, award_id=award["id"] ) if tender.awards[-1].status == "pending": tender.awardPeriod.endDate = None tender.status = "active.qualification" else: tender.awardPeriod.endDate = now tender.status = "active.awarded"
def contract_change_document(self): response = self.app.patch_json('/contracts/{}?acc_token={}'.format(self.contract_id, self.contract_token), {"data": {"status": "active"}}) self.assertEqual(response.status, '200 OK') response = self.app.post('/contracts/{}/documents?acc_token={}'.format( self.contract_id, self.contract_token), upload_files=[('file', str(Header(u'укр.doc', 'utf-8')), '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(u'укр.doc', response.json["data"]["title"]) self.assertEqual(response.json["data"]["documentOf"], "contract") self.assertNotIn("documentType", response.json["data"]) response = self.app.patch_json('/contracts/{}/documents/{}?acc_token={}'.format(self.contract_id, doc_id, self.contract_token), {"data": { "documentOf": "change", "relatedItem": '1234' * 8, }}, status=422) self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.json['errors'], [ {"location": "body", "name": "relatedItem", "description": ["relatedItem should be one of changes"]}]) response = self.app.post_json('/contracts/{}/changes?acc_token={}'.format(self.contract['id'], self.contract_token), {'data': {'rationale': u'причина зміни укр', 'rationale_en': 'change cause en', 'rationaleTypes': ['priceReduction']}}) self.assertEqual(response.status, '201 Created') self.assertEqual(response.content_type, 'application/json') change = response.json['data'] response = self.app.patch_json('/contracts/{}/documents/{}?acc_token={}'.format(self.contract_id, doc_id, self.contract_token), {"data": { "documentOf": "change", "relatedItem": change['id'], }}) self.assertEqual(response.status, '200 OK') self.assertEqual(response.content_type, 'application/json') self.assertEqual(doc_id, response.json["data"]["id"]) self.assertEqual(response.json["data"]["documentOf"], 'change') self.assertEqual(response.json["data"]["relatedItem"], change['id']) response = self.app.put('/contracts/{}/documents/{}?acc_token={}'.format( self.contract_id, doc_id, self.contract_token), upload_files=[('file', str(Header(u'укр2.doc', 'utf-8')), 'content2')]) self.assertEqual(response.status, '200 OK') self.assertEqual(response.content_type, 'application/json') self.assertEqual(doc_id, response.json["data"]["id"]) response = self.app.patch_json('/contracts/{}/changes/{}?acc_token={}'.format(self.contract['id'], change['id'], self.contract_token), {'data': {'status': 'active', 'dateSigned': get_now().isoformat()}}) self.assertEqual(response.status, '200 OK') response = self.app.post('/contracts/{}/documents?acc_token={}'.format( self.contract_id, self.contract_token), upload_files=[('file', str(Header(u'укр2.doc', 'utf-8')), 'content2')]) self.assertEqual(response.status, '201 Created') doc_id = response.json["data"]['id'] response = self.app.patch_json('/contracts/{}/documents/{}?acc_token={}'.format(self.contract_id, doc_id, self.contract_token), {"data": { "documentOf": "change", "relatedItem": change['id'], }}, status=403) self.assertEqual(response.status, '403 Forbidden') self.assertEqual(response.json['errors'], [ {"location": "body", "name": "data", "description": "Can't add document to 'active' change"}])
def create_change(self): response = self.app.post_json( "/contracts/{}/changes?acc_token={}".format(self.contract["id"], self.contract_token), { "data": { "rationale": "причина зміни укр", "rationale_en": "change cause en", "rationaleTypes": ["qualityImprovement"], } }, ) self.assertEqual(response.status, "201 Created") self.assertEqual(response.content_type, "application/json") change = response.json["data"] self.assertEqual(change["status"], "pending") self.assertIn("date", change) response = self.app.get("/contracts/{}/changes".format(self.contract["id"])) self.assertEqual(response.status, "200 OK") self.assertEqual(len(response.json["data"]), 1) response = self.app.post_json( "/contracts/{}/changes?acc_token={}".format(self.contract["id"], self.contract_token), {"data": {"rationale": "трататата", "rationaleTypes": ["priceReduction"]}}, status=403, ) self.assertEqual(response.status, "403 Forbidden") self.assertEqual( response.json["errors"], [ { "location": "body", "name": "data", "description": "Can't create new contract change while any (pending) change exists", } ], ) response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"status": "active", "dateSigned": get_now().isoformat()}}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.json["data"]["status"], "active") response = self.app.post_json( "/contracts/{}/changes?acc_token={}".format(self.contract["id"], self.contract_token), {"data": {"rationale": "трататата", "rationaleTypes": ["non-existing-rationale"]}}, status=422, ) self.assertEqual(response.status, "422 Unprocessable Entity") self.assertEqual( response.json["errors"], [ { "location": "body", "name": "rationaleTypes", "description": [ [ "Value must be one of ['volumeCuts', 'itemPriceVariation', 'qualityImprovement', 'thirdParty', 'durationExtension', 'priceReduction', 'taxRate', 'fiscalYearExtension']." ] ], } ], ) response = self.app.post_json( "/contracts/{}/changes?acc_token={}".format(self.contract["id"], self.contract_token), {"data": {"rationale": "трататата", "rationaleTypes": ["priceReduction"]}}, ) self.assertEqual(response.status, "201 Created") change2 = response.json["data"] self.assertEqual(change2["status"], "pending") response = self.app.get("/contracts/{}/changes".format(self.contract["id"])) self.assertEqual(response.status, "200 OK") self.assertEqual(len(response.json["data"]), 2)
def patch_tender_contract(self): self.app.authorization = ("Basic", ("token", "")) response = self.app.get("/tenders/{}/contracts".format(self.tender_id)) contract = response.json["data"][0] self.set_status("active.awarded", start_end="end") response = self.app.post_json( "/tenders/{}/awards/{}/complaints?acc_token={}".format( self.tender_id, self.award_id, self.tender_token), { "data": { "title": "complaint title", "description": "complaint description", "author": test_author, "status": "claim", } }, status=404, ) self.assertEqual(response.status, "404 Not Found") self.assertEqual(response.content_type, "text/plain") tender = self.db.get(self.tender_id) self.db.save(tender) response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "value": { "amountNet": contract["value"]["amount"] - 1 } }}, ) self.assertEqual(response.status, "200 OK") self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), { "data": { "contractID": "myselfID", "items": [{ "description": "New Description" }], "suppliers": [{ "name": "New Name" }], } }, ) response = self.app.get("/tenders/{}/contracts/{}".format( self.tender_id, contract["id"])) self.assertEqual(response.json["data"]["contractID"], contract["contractID"]) self.assertEqual(response.json["data"]["items"], contract["items"]) self.assertEqual(response.json["data"]["suppliers"], contract["suppliers"]) response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "value": { "currency": "USD" } }}, status=403, ) self.assertEqual(response.status, "403 Forbidden") self.assertEqual(response.json["errors"][0]["description"], "Can't update currency for contract value") one_hour_in_furure = (get_now() + timedelta(hours=1)).isoformat() response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "dateSigned": one_hour_in_furure }}, status=422, ) self.assertEqual(response.status, "422 Unprocessable Entity") self.assertEqual( response.json["errors"], [{ u"description": [u"Contract signature date can't be in the future"], u"location": u"body", u"name": u"dateSigned", }], ) custom_signature_date = get_now().isoformat() response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "dateSigned": custom_signature_date }}, ) self.assertEqual(response.status, "200 OK") 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.content_type, "application/json") self.assertEqual(response.json["data"]["status"], "active") response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "value": { "amount": 232 } }}, status=403, ) self.assertEqual(response.status, "403 Forbidden") self.assertEqual( response.json["errors"][0]["description"], "Can't update contract in current (complete) tender status") response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "contractID": "myselfID" }}, status=403, ) self.assertEqual(response.status, "403 Forbidden") self.assertEqual( response.json["errors"][0]["description"], "Can't update contract in current (complete) tender status") response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "items": [{ "description": "New Description" }] }}, status=403, ) self.assertEqual(response.status, "403 Forbidden") self.assertEqual( response.json["errors"][0]["description"], "Can't update contract in current (complete) tender status") response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "suppliers": [{ "name": "New Name" }] }}, status=403, ) self.assertEqual(response.status, "403 Forbidden") self.assertEqual( response.json["errors"][0]["description"], "Can't update contract in current (complete) tender status") response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "status": "active" }}, 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 contract in current (complete) tender status") response = self.app.patch_json( "/tenders/{}/contracts/some_id?acc_token={}".format( self.tender_id, self.tender_token), {"data": { "status": "active" }}, 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"contract_id" }]) response = self.app.patch_json( "/tenders/some_id/contracts/some_id?acc_token={}".format( self.tender_token), {"data": { "status": "active" }}, 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"tender_id" }]) response = self.app.get("/tenders/{}/contracts/{}".format( self.tender_id, contract["id"])) self.assertEqual(response.status, "200 OK") self.assertEqual(response.content_type, "application/json") self.assertEqual(response.json["data"]["status"], "active") self.assertEqual(response.json["data"]["contractID"], contract["contractID"]) self.assertEqual(response.json["data"]["items"], contract["items"]) self.assertEqual(response.json["data"]["suppliers"], contract["suppliers"]) self.assertEqual(response.json["data"]["dateSigned"], custom_signature_date)
def patch_change(self): response = self.app.post_json( "/contracts/{}/changes?acc_token={}".format(self.contract["id"], self.contract_token), { "data": { "rationale": "причина зміни укр", "rationale_en": "change cause en", "rationaleTypes": ["priceReduction"], "contractNumber": "№ 146", } }, ) self.assertEqual(response.status, "201 Created") self.assertEqual(response.content_type, "application/json") change = response.json["data"] self.assertEqual(change["status"], "pending") self.assertEqual(change["contractNumber"], "№ 146") creation_date = change["date"] now = get_now().isoformat() response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"date": now}}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.body, b"null") response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"rationale_ru": "шота на руськом"}}, ) self.assertEqual(response.status, "200 OK") self.assertIn("rationale_ru", response.json["data"]) first_patch_date = response.json["data"]["date"] self.assertEqual(first_patch_date, creation_date) response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"rationale_en": "another cause desctiption"}}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.json["data"]["rationale_en"], "another cause desctiption") second_patch_date = response.json["data"]["date"] self.assertEqual(first_patch_date, second_patch_date) response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"rationaleTypes": ["fiscalYearExtension", "priceReduction"]}}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.json["data"]["rationaleTypes"], ["fiscalYearExtension", "priceReduction"]) response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"rationaleTypes": ["fiscalYearExtension", "volumeCuts", "taxRate"]}}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.json["data"]["rationaleTypes"], ["fiscalYearExtension", "volumeCuts", "taxRate"]) response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"rationaleTypes": "fiscalYearExtension"}}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.json["data"]["rationaleTypes"], ["fiscalYearExtension"]) response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"rationaleTypes": "fiscalYearExtension, volumeCuts"}}, status=422, ) self.assertEqual(response.status, "422 Unprocessable Entity") self.assertEqual( response.json["errors"], [ { "location": "body", "name": "rationaleTypes", "description": [ [ "Value must be one of ['volumeCuts', 'itemPriceVariation', 'qualityImprovement', 'thirdParty', 'durationExtension', 'priceReduction', 'taxRate', 'fiscalYearExtension']." ] ], } ], ) response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"rationaleTypes": []}}, status=422, ) self.assertEqual(response.status, "422 Unprocessable Entity") self.assertEqual( response.json["errors"], [{"location": "body", "name": "rationaleTypes", "description": ["Please provide at least 1 item."]}], ) response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"id": "1234" * 8}}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.body, b"null") self.app.authorization = None response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"rationale_en": "la-la-la"}}, status=403, ) self.assertEqual(response.status, "403 Forbidden") self.app.authorization = ("Basic", ("broker", "")) response = self.app.patch_json( "/contracts/{}/changes/{}".format(self.contract["id"], change["id"]), {"data": {"rationale_en": "la-la-la"}}, status=403, ) self.assertEqual(response.status, "403 Forbidden") response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"status": "active", "dateSigned": get_now().isoformat()}}, ) self.assertEqual(response.status, "200 OK") self.assertNotEqual(response.json["data"]["date"], creation_date) self.assertNotEqual(response.json["data"]["date"], first_patch_date) self.assertNotEqual(response.json["data"]["date"], second_patch_date) response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"status": "pending"}}, status=403, ) self.assertEqual(response.status, "403 Forbidden")
def validate_scheme(self, data, scheme): schematics_document = get_schematics_document(data['__parent__']) if (schematics_document.get('revisions')[0].date if schematics_document.get('revisions') else get_now()) > CPV_BLOCK_FROM and scheme != u'ДК021': raise ValidationError(BaseType.MESSAGES['choices'].format(unicode([u'ДК021'])))
def change_date_signed(self): response = self.app.post_json( "/contracts/{}/changes?acc_token={}".format(self.contract["id"], self.contract_token), { "data": { "rationale": "причина зміни укр", "rationale_en": "change cause en", "rationaleTypes": ["priceReduction"], "contractNumber": "№ 146", } }, ) self.assertEqual(response.status, "201 Created") self.assertEqual(response.content_type, "application/json") change = response.json["data"] self.assertEqual(change["status"], "pending") self.assertEqual(change["contractNumber"], "№ 146") self.app.authorization = ("Basic", ("broker", "")) response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"status": "active"}}, status=403, ) self.assertEqual(response.status, "403 Forbidden") self.assertEqual( response.json["errors"], [ { "location": "body", "name": "data", "description": "Can't update contract change status. 'dateSigned' is required.", } ], ) response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"dateSigned": "12-14-11"}}, status=422, ) self.assertEqual( response.json["errors"], [{"location": "body", "name": "dateSigned", "description": ["Could not parse 12-14-11. Should be ISO8601."]}], ) valid_date1_raw = get_now() valid_date1 = valid_date1_raw.isoformat() response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"dateSigned": valid_date1}}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.json["data"]["dateSigned"], valid_date1) one_day_in_past = (get_now() - timedelta(days=1)).isoformat() response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"dateSigned": one_day_in_past}}, status=403, ) self.assertIn("can't be earlier than contract dateSigned", response.json["errors"][0]["description"]) response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"status": "active"}}, ) self.assertEqual(response.status, "200 OK") response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"dateSigned": get_now().isoformat()}}, status=403, ) self.assertEqual( response.json["errors"], [ { "location": "body", "name": "data", "description": "Can't update contract change in current (active) status", } ], ) response = self.app.get("/contracts/{}/changes/{}".format(self.contract["id"], change["id"])) change1 = response.json["data"] self.assertEqual(change1["dateSigned"], valid_date1) response = self.app.post_json( "/contracts/{}/changes?acc_token={}".format(self.contract["id"], self.contract_token), { "data": { "rationale": "iнша причина зміни укр", "rationale_en": "another change cause en", "rationaleTypes": ["priceReduction"], "contractNumber": "№ 147", } }, ) self.assertEqual(response.status, "201 Created") self.assertEqual(response.content_type, "application/json") change2 = response.json["data"] self.assertEqual(change["status"], "pending") one_day_in_future = (get_now() + timedelta(days=1)).isoformat() response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change2["id"], self.contract_token), {"data": {"dateSigned": one_day_in_future}}, status=422, ) self.assertEqual( response.json["errors"], [ { "location": "body", "name": "dateSigned", "description": ["Contract signature date can't be in the future"], } ], ) smaller_than_last_change = (valid_date1_raw - timedelta(seconds=1)).isoformat() response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change2["id"], self.contract_token), {"data": {"dateSigned": smaller_than_last_change}}, status=403, ) self.assertEqual( "Change dateSigned ({}) can't be earlier than last active change dateSigned ({})".format( smaller_than_last_change, valid_date1 ), response.json["errors"][0]["description"], ) date = get_now().isoformat() response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change2["id"], self.contract_token), {"data": {"dateSigned": date}}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.json["data"]["dateSigned"], date) # date update request valid_date2_raw = get_now() valid_date2 = valid_date2_raw.isoformat() response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change2["id"], self.contract_token), {"data": {"dateSigned": valid_date2}}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.json["data"]["dateSigned"], valid_date2) response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change2["id"], self.contract_token), {"data": {"status": "active"}}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.json["data"]["dateSigned"], valid_date2) response = self.app.post_json( "/contracts/{}/changes?acc_token={}".format(self.contract["id"], self.contract_token), { "data": { "rationale": "третя причина зміни укр", "rationale_en": "third change cause en", "rationaleTypes": ["priceReduction"], "contractNumber": "№ 148", } }, ) self.assertEqual(response.status, "201 Created") change3 = response.json["data"] self.assertEqual(change["status"], "pending") smaller_than_last_change = (valid_date2_raw - timedelta(seconds=1)).isoformat() response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change3["id"], self.contract_token), {"data": {"dateSigned": smaller_than_last_change}}, status=403, ) self.assertEqual( "Change dateSigned ({}) can't be earlier than last active change dateSigned ({})".format( smaller_than_last_change, valid_date2 ), response.json["errors"][0]["description"], ) date = get_now().isoformat() response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change3["id"], self.contract_token), {"data": {"dateSigned": date}}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.json["data"]["dateSigned"], date) response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change3["id"], self.contract_token), {"data": {"status": "active"}}, ) self.assertEqual(response.status, "200 OK") response = self.app.patch_json( "/contracts/{}?acc_token={}".format(self.contract["id"], self.contract_token), {"data": {"value": {"amountNet": self.contract["value"]["amount"] - 1}}}, ) response = self.app.patch_json( "/contracts/{}?acc_token={}".format(self.contract["id"], self.contract_token), {"data": {"status": "terminated", "amountPaid": {"amount": 15, "amountNet": 14}}}, ) self.assertEqual(response.status, "200 OK")
def validate_update_tender(self): tender = self.request.validated['tender'] if calculate_business_date(get_now(), TENDERING_EXTRA_PERIOD, tender, True) > tender.tenderPeriod.endDate: raise_operation_error(self.request, 'tenderPeriod should be extended by {0.days} working days'.format(TENDERING_EXTRA_PERIOD)) return True
def date_signed_on_change_creation(self): # test create change with date signed one_day_in_past = (get_now() - timedelta(days=1)).isoformat() response = self.app.post_json( "/contracts/{}/changes?acc_token={}".format(self.contract["id"], self.contract_token), { "data": { "rationale": "причина зміни укр", "rationale_en": "change cause en", "dateSigned": one_day_in_past, "rationaleTypes": ["priceReduction"], "contractNumber": "№ 146", } }, status=403, ) self.assertIn("can't be earlier than contract dateSigned", response.json["errors"][0]["description"]) one_day_in_future = (get_now() + timedelta(days=1)).isoformat() response = self.app.post_json( "/contracts/{}/changes?acc_token={}".format(self.contract["id"], self.contract_token), { "data": { "rationale": "причина зміни укр", "rationale_en": "change cause en", "dateSigned": one_day_in_future, "rationaleTypes": ["priceReduction"], "contractNumber": "№ 146", } }, status=422, ) self.assertEqual( response.json["errors"], [ { "location": "body", "name": "dateSigned", "description": ["Contract signature date can't be in the future"], } ], ) date = get_now().isoformat() response = self.app.post_json( "/contracts/{}/changes?acc_token={}".format(self.contract["id"], self.contract_token), { "data": { "rationale": "причина зміни укр", "rationale_en": "change cause en", "dateSigned": date, "rationaleTypes": ["priceReduction"], "contractNumber": "№ 146", } }, ) self.assertEqual(response.status, "201 Created") self.assertEqual(response.content_type, "application/json") change = response.json["data"] self.assertEqual(change["dateSigned"], date) response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"status": "active"}}, ) self.assertEqual(response.status, "200 OK")
def test_docs(self): request_path = '/tenders?opt_pretty=1' # Exploring basic rules with open(TARGET_DIR + 'tender-listing.http', 'w') as self.app.file_obj: self.app.authorization = None response = self.app.get(request_path) self.assertEqual(response.status, '200 OK') self.app.file_obj.write("\n") with open(TARGET_DIR + 'tender-post-attempt.http', 'w') as self.app.file_obj: response = self.app.post(request_path, 'data', status=415) self.assertEqual(response.status, '415 Unsupported Media Type') with open(TARGET_DIR + 'tender-post-attempt-json.http', 'w') as self.app.file_obj: self.app.authorization = ('Basic', ('broker', '')) response = self.app.post(request_path, 'data', content_type='application/json', status=422) self.assertEqual(response.status, '422 Unprocessable Entity') # Creating tender lot = deepcopy(test_lots[0]) lot['id'] = uuid4().hex lot['value'] = test_tender_data['value'] lot['minimalStep'] = test_tender_data['minimalStep'] second_item = deepcopy(test_tender_data['items'][0]) second_item['unit']['code'] = '44617100-8' test_tender_data['items'] = [test_tender_data['items'][0], second_item] test_tender_data['lots'] = [lot] for item in test_tender_data['items']: item['relatedLot'] = lot['id'] test_tender_data.update({ "tenderPeriod": { "endDate": (get_now() + timedelta(days=31)).isoformat() } }) self.app.authorization = ('Basic', ('broker', '')) with open(TARGET_DIR + 'tender-post-attempt-json-data.http', 'w') as self.app.file_obj: response = self.app.post_json('/tenders?opt_pretty=1', {'data': test_tender_data}) self.assertEqual(response.status, '201 Created') tender = response.json['data'] owner_token = response.json['access']['token'] self.tender_id = tender['id'] self.set_status('active.enquiries') with open(TARGET_DIR + 'blank-tender-view.http', 'w') as self.app.file_obj: response = self.app.get('/tenders/{}'.format(tender['id'])) self.assertEqual(response.status, '200 OK') # Let DB index new tender self.app.get('/tenders') sleep(2) with open(TARGET_DIR + 'initial-tender-listing.http', 'w') as self.app.file_obj: response = self.app.get('/tenders') self.assertEqual(response.status, '200 OK') self.app.authorization = ('Basic', ('broker', '')) # Modifying tender tenderPeriod_endDate = get_now() + timedelta(days=30, seconds=10) with open(TARGET_DIR + 'patch-items-value-periods.http', 'w') as self.app.file_obj: response = self.app.patch_json( '/tenders/{}?acc_token={}'.format(tender['id'], owner_token), { 'data': { "tenderPeriod": { "endDate": tenderPeriod_endDate.isoformat() } } }) with open(TARGET_DIR + 'tender-listing-after-patch.http', 'w') as self.app.file_obj: self.app.authorization = None response = self.app.get(request_path) self.assertEqual(response.status, '200 OK') self.app.authorization = ('Basic', ('broker', '')) # Setting Bid guarantee with open(TARGET_DIR + 'set-bid-guarantee.http', 'w') as self.app.file_obj: response = self.app.patch_json( '/tenders/{}/lots/{}?acc_token={}'.format( self.tender_id, lot['id'], owner_token), {"data": { "guarantee": { "amount": 8, "currency": "USD" } }}) self.assertEqual(response.status, '200 OK') self.assertIn('guarantee', response.json['data']) # Uploading documentation with open(TARGET_DIR + 'upload-tender-notice.http', 'w') as self.app.file_obj: response = self.app.post( '/tenders/{}/documents?acc_token={}'.format( self.tender_id, owner_token), upload_files=[('file', u'Notice.pdf', 'content')]) self.assertEqual(response.status, '201 Created') doc_id = response.json["data"]["id"] with open(TARGET_DIR + 'tender-documents.http', 'w') as self.app.file_obj: response = self.app.get( '/tenders/{}/documents/{}?acc_token={}'.format( self.tender_id, doc_id, owner_token)) self.assertEqual(response.status, '200 OK') with open(TARGET_DIR + 'upload-award-criteria.http', 'w') as self.app.file_obj: response = self.app.post( '/tenders/{}/documents?acc_token={}'.format( self.tender_id, owner_token), upload_files=[('file', u'AwardCriteria.pdf', 'content')]) self.assertEqual(response.status, '201 Created') doc_id = response.json["data"]["id"] with open(TARGET_DIR + 'tender-documents-2.http', 'w') as self.app.file_obj: response = self.app.get( '/tenders/{}/documents?acc_token={}'.format( self.tender_id, owner_token)) self.assertEqual(response.status, '200 OK') with open(TARGET_DIR + 'update-award-criteria.http', 'w') as self.app.file_obj: response = self.app.put( '/tenders/{}/documents/{}?acc_token={}'.format( self.tender_id, doc_id, owner_token), upload_files=[('file', 'AwardCriteria-2.pdf', 'content2')]) self.assertEqual(response.status, '200 OK') with open(TARGET_DIR + 'tender-documents-3.http', 'w') as self.app.file_obj: response = self.app.get('/tenders/{}/documents'.format( self.tender_id)) self.assertEqual(response.status, '200 OK') # Enquiries with open(TARGET_DIR + 'ask-question.http', 'w') as self.app.file_obj: response = self.app.post_json('/tenders/{}/questions'.format( self.tender_id), {'data': question}, status=201) question_id = response.json['data']['id'] self.assertEqual(response.status, '201 Created') with open(TARGET_DIR + 'answer-question.http', 'w') as self.app.file_obj: response = self.app.patch_json( '/tenders/{}/questions/{}?acc_token={}'.format( self.tender_id, question_id, owner_token), { "data": { "answer": "Таблицю додано в файлі \"Kalorijnist.xslx\"" } }, status=200) self.assertEqual(response.status, '200 OK') with open(TARGET_DIR + 'list-question.http', 'w') as self.app.file_obj: response = self.app.get('/tenders/{}/questions'.format( self.tender_id)) self.assertEqual(response.status, '200 OK') with open(TARGET_DIR + 'get-answer.http', 'w') as self.app.file_obj: response = self.app.get('/tenders/{}/questions/{}'.format( self.tender_id, question_id)) self.assertEqual(response.status, '200 OK') self.time_shift('enquiryPeriod_ends') self.app.authorization = ('Basic', ('broker', '')) with open(TARGET_DIR + 'ask-question-after-enquiry-period.http', 'w') as self.app.file_obj: response = self.app.post_json('/tenders/{}/questions'.format( self.tender_id), {'data': question}, status=403) self.assertEqual(response.status, '403 Forbidden') with open( TARGET_DIR + 'update-tender-after-enqiery-with-update-periods.http', 'w') as self.app.file_obj: tenderPeriod_endDate = get_now() + timedelta(days=8) response = self.app.patch_json( '/tenders/{}?acc_token={}'.format(tender['id'], owner_token), { 'data': { "value": { "amount": 501, "currency": u"UAH" }, "tenderPeriod": { "endDate": tenderPeriod_endDate.isoformat() } } }) self.assertEqual(response.status, '200 OK') # Registering bid bids_access = {} with open(TARGET_DIR + 'register-bidder.http', 'w') as self.app.file_obj: bid['lotValues'][0]['relatedLot'] = lot['id'] response = self.app.post_json( '/tenders/{}/bids'.format(self.tender_id), {'data': bid}) bid1_id = response.json['data']['id'] bids_access[bid1_id] = response.json['access']['token'] self.assertEqual(response.status, '201 Created') with open(TARGET_DIR + 'activate-bidder.http', 'w') as self.app.file_obj: response = self.app.patch_json( '/tenders/{}/bids/{}?acc_token={}'.format( self.tender_id, bid1_id, bids_access[bid1_id]), {"data": { "status": "pending" }}) self.assertEqual(response.status, '200 OK') # Proposal Uploading with open(TARGET_DIR + 'upload-bid-proposal.http', 'w') as self.app.file_obj: response = self.app.post( '/tenders/{}/bids/{}/documents?acc_token={}'.format( self.tender_id, bid1_id, bids_access[bid1_id]), upload_files=[('file', 'Proposal.pdf', 'content')]) self.assertEqual(response.status, '201 Created') with open(TARGET_DIR + 'upload-bid-private-proposal.http', 'w') as self.app.file_obj: response = self.app.post( '/tenders/{}/bids/{}/documents?acc_token={}'.format( self.tender_id, bid1_id, bids_access[bid1_id]), upload_files=[('file', 'Proposal_top_secrets.pdf', 'content')]) self.assertEqual(response.status, '201 Created') priv_doc_id = response.json['data']['id'] # set confidentiality properties with open(TARGET_DIR + 'mark-bid-doc-private.http', 'w') as self.app.file_obj: response = self.app.patch_json( '/tenders/{}/bids/{}/documents/{}?acc_token={}'.format( self.tender_id, bid1_id, priv_doc_id, bids_access[bid1_id]), { 'data': { 'confidentiality': 'buyerOnly', 'confidentialityRationale': 'Only our company sells badgers with pink hair.', } }) self.assertEqual(response.status, '200 OK') with open(TARGET_DIR + 'bidder-documents.http', 'w') as self.app.file_obj: response = self.app.get( '/tenders/{}/bids/{}/documents?acc_token={}'.format( self.tender_id, bid1_id, bids_access[bid1_id])) with open(TARGET_DIR + 'upload-bid-financial-document-proposal.http', 'w') as self.app.file_obj: response = self.app.post( '/tenders/{}/bids/{}/financial_documents?acc_token={}'.format( self.tender_id, bid1_id, bids_access[bid1_id]), upload_files=[('file', 'financial_doc.pdf', '1000$')]) self.assertEqual(response.status, '201 Created') response = self.app.post( '/tenders/{}/bids/{}/financial_documents?acc_token={}'.format( self.tender_id, bid1_id, bids_access[bid1_id]), upload_files=[('file', 'financial_doc2.pdf', '1000$')]) self.assertEqual(response.status, '201 Created') # financial_doc_id = response.json['data']['id'] with open(TARGET_DIR + 'bidder-financial-documents.http', 'w') as self.app.file_obj: response = self.app.get( '/tenders/{}/bids/{}/financial_documents?acc_token={}'.format( self.tender_id, bid1_id, bids_access[bid1_id])) self.assertEqual(response.status, '200 OK') with open(TARGET_DIR + 'upload-bid-eligibility-document-proposal.http', 'w') as self.app.file_obj: response = self.app.post( '/tenders/{}/bids/{}/eligibility_documents?acc_token={}'. format(self.tender_id, bid1_id, bids_access[bid1_id]), upload_files=[('file', 'eligibility_doc.pdf', 'content')]) self.assertEqual(response.status, '201 Created') with open( TARGET_DIR + 'upload-bid-qualification-document-proposal.http', 'w') as self.app.file_obj: response = self.app.post( '/tenders/{}/bids/{}/qualification_documents?acc_token={}'. format(self.tender_id, bid1_id, bids_access[bid1_id]), upload_files=[('file', 'qualification_document.pdf', 'content') ]) self.assertEqual(response.status, '201 Created') self.qualification_doc_id = response.json['data']['id'] # patch bid document by user with open( TARGET_DIR + 'upload-bid-qualification-document-proposal-updated.http', 'w') as self.app.file_obj: response = self.app.put( '/tenders/{}/bids/{}/qualification_documents/{}?acc_token={}'. format(self.tender_id, bid1_id, self.qualification_doc_id, bids_access[bid1_id]), upload_files=[('file', 'qualification_document2.pdf', 'content')]) self.assertEqual(response.status, '200 OK') with open(TARGET_DIR + 'bidder-view-financial-documents.http', 'w') as self.app.file_obj: response = self.app.get('/tenders/{}/bids/{}?acc_token={}'.format( self.tender_id, bid1_id, bids_access[bid1_id])) self.assertEqual(response.status, '200 OK') response = self.app.patch_json( '/tenders/{}?acc_token={}'.format(tender['id'], owner_token), {'data': { "value": { 'amount': 501.0 } }}) self.assertEqual(response.status, '200 OK') # Bid invalidation with open(TARGET_DIR + 'bidder-after-changing-tender.http', 'w') as self.app.file_obj: response = self.app.get('/tenders/{}/bids/{}?acc_token={}'.format( self.tender_id, bid1_id, bids_access[bid1_id])) self.assertEqual(response.status, '200 OK') # Bid confirmation with open(TARGET_DIR + 'bidder-activate-after-changing-tender.http', 'w') as self.app.file_obj: response = self.app.patch_json( '/tenders/{}/bids/{}?acc_token={}'.format( self.tender_id, bid1_id, bids_access[bid1_id]), {'data': { "status": "pending" }}) self.assertEqual(response.status, '200 OK') with open(TARGET_DIR + 'register-2nd-bidder.http', 'w') as self.app.file_obj: bid2['lotValues'][0]['relatedLot'] = lot['id'] response = self.app.post_json( '/tenders/{}/bids'.format(self.tender_id), {'data': bid2}) bid2_id = response.json['data']['id'] bids_access[bid2_id] = response.json['access']['token'] self.assertEqual(response.status, '201 Created') bid_document2.update({ 'confidentiality': 'buyerOnly', 'confidentialityRationale': 'Only our company sells badgers with pink hair.' }) bid3["documents"] = [bid_document, bid_document2] bid3['lotValues'][0]['relatedLot'] = lot['id'] for document in bid3['documents']: document['url'] = self.generate_docservice_url() for document in bid3['eligibilityDocuments']: document['url'] = self.generate_docservice_url() for document in bid3['financialDocuments']: document['url'] = self.generate_docservice_url() for document in bid3['qualificationDocuments']: document['url'] = self.generate_docservice_url() with open(TARGET_DIR + 'register-3rd-bidder.http', 'w') as self.app.file_obj: response = self.app.post_json( '/tenders/{}/bids'.format(self.tender_id), {'data': bid3}) bid3_id = response.json['data']['id'] bids_access[bid3_id] = response.json['access']['token'] self.assertEqual(response.status, '201 Created') with open(TARGET_DIR + 'register-4rd-bidder.http', 'w') as self.app.file_obj: response = self.app.post_json( '/tenders/{}/bids'.format(self.tender_id), {'data': bid3}) bid4_id = response.json['data']['id'] bids_access[bid4_id] = response.json['access']['token'] self.assertEqual(response.status, '201 Created') # Pre-qualification self.set_status('active.pre-qualification') auth = self.app.authorization self.app.authorization = ('Basic', ('chronograph', '')) response = self.app.patch_json('/tenders/{}'.format(self.tender_id), {'data': { "id": self.tender_id }}) self.app.authorization = auth with open(TARGET_DIR + 'qualifications-listing.http', 'w') as self.app.file_obj: response = self.app.get('/tenders/{}/qualifications'.format( self.tender_id)) self.assertEqual(response.status, "200 OK") qualifications = response.json['data'] self.assertEqual(len(qualifications), 4) self.assertEqual(qualifications[0]['bidID'], bid1_id) self.assertEqual(qualifications[1]['bidID'], bid2_id) self.assertEqual(qualifications[2]['bidID'], bid3_id) self.assertEqual(qualifications[3]['bidID'], bid4_id) with open(TARGET_DIR + 'approve-qualification1.http', 'w') as self.app.file_obj: response = self.app.patch_json( '/tenders/{}/qualifications/{}?acc_token={}'.format( self.tender_id, qualifications[0]['id'], owner_token), { "data": { "status": "active", "qualified": True, "eligible": True } }) self.assertEqual(response.status, "200 OK") with open(TARGET_DIR + 'approve-qualification2.http', 'w') as self.app.file_obj: response = self.app.patch_json( '/tenders/{}/qualifications/{}?acc_token={}'.format( self.tender_id, qualifications[1]['id'], owner_token), { "data": { "status": "active", "qualified": True, "eligible": True } }) self.assertEqual(response.status, "200 OK") with open(TARGET_DIR + 'approve-qualification4.http', 'w') as self.app.file_obj: response = self.app.patch_json( '/tenders/{}/qualifications/{}?acc_token={}'.format( self.tender_id, qualifications[3]['id'], owner_token), { "data": { "status": "active", "qualified": True, "eligible": True } }) self.assertEqual(response.status, "200 OK") with open(TARGET_DIR + 'reject-qualification3.http', 'w') as self.app.file_obj: response = self.app.patch_json( '/tenders/{}/qualifications/{}?acc_token={}'.format( self.tender_id, qualifications[2]['id'], owner_token), {"data": { "status": "unsuccessful" }}) self.assertEqual(response.status, "200 OK") with open(TARGET_DIR + 'qualificated-bids-view.http', 'w') as self.app.file_obj: response = self.app.get('/tenders/{}/bids?acc_token={}'.format( self.tender_id, owner_token)) self.assertEqual(response.status, "200 OK") with open(TARGET_DIR + 'rejected-bid-view.http', 'w') as self.app.file_obj: response = self.app.get('/tenders/{}/bids/{}?acc_token={}'.format( self.tender_id, bid3_id, owner_token)) self.assertEqual(response.status, "200 OK") # active.pre-qualification.stand-still with open(TARGET_DIR + 'pre-qualification-confirmation.http', 'w') as self.app.file_obj: response = self.app.patch_json( '/tenders/{}?acc_token={}'.format(self.tender_id, owner_token), {"data": { "status": "active.pre-qualification.stand-still" }}) self.assertEqual(response.status, "200 OK") self.assertEqual(response.json['data']['status'], "active.pre-qualification.stand-still") # Auction self.set_status('active.auction') self.app.authorization = ('Basic', ('auction', '')) auction_url = u'{}/tenders/{}_{}'.format(self.auctions_url, self.tender_id, lot['id']) patch_data = { 'lots': [{ 'auctionUrl': auction_url, }], 'bids': [{ "id": bid1_id, "lotValues": [{ "participationUrl": u'{}?key_for_bid={}'.format(auction_url, bid1_id) }] }, { "id": bid2_id, "lotValues": [{ "participationUrl": u'{}?key_for_bid={}'.format(auction_url, bid2_id) }] }, { "id": bid3_id, }, { "id": bid4_id, "lotValues": [{ "participationUrl": u'{}?key_for_bid={}'.format(auction_url, bid4_id) }] }] } response = self.app.patch_json( '/tenders/{}/auction/{}?acc_token={}'.format( self.tender_id, lot['id'], owner_token), {'data': patch_data}) self.assertEqual(response.status, '200 OK') self.app.authorization = ('Basic', ('broker', '')) with open(TARGET_DIR + 'auction-url.http', 'w') as self.app.file_obj: response = self.app.get('/tenders/{}'.format(self.tender_id)) self.assertEqual(response.status, '200 OK') with open(TARGET_DIR + 'bidder-participation-url.http', 'w') as self.app.file_obj: response = self.app.get('/tenders/{}/bids/{}?acc_token={}'.format( self.tender_id, bid1_id, bids_access[bid1_id])) self.assertEqual(response.status, '200 OK') with open(TARGET_DIR + 'bidder2-participation-url.http', 'w') as self.app.file_obj: response = self.app.get('/tenders/{}/bids/{}?acc_token={}'.format( self.tender_id, bid2_id, bids_access[bid2_id])) self.assertEqual(response.status, '200 OK') with open(TARGET_DIR + 'bidder4-participation-url.http', 'w') as self.app.file_obj: response = self.app.get('/tenders/{}/bids/{}?acc_token={}'.format( self.tender_id, bid4_id, bids_access[bid4_id])) self.assertEqual(response.status, '200 OK') # Confirming qualification self.app.authorization = ('Basic', ('auction', '')) response = self.app.get('/tenders/{}/auction'.format(self.tender_id)) auction_bids_data = response.json['data']['bids'] response = self.app.post_json( '/tenders/{}/auction/{}'.format(self.tender_id, lot['id']), {'data': { 'bids': auction_bids_data }}) self.app.authorization = ('Basic', ('broker', '')) with open(TARGET_DIR + 'qualifications-list.http', 'w') as self.app.file_obj: response = self.app.get('/tenders/{}/awards?acc_token={}'.format( self.tender_id, owner_token)) # get pending award award_ids = [ i['id'] for i in response.json['data'] if i['status'] == 'pending' ] with open(TARGET_DIR + 'confirm-qualification.http', 'w') as self.app.file_obj: self.app.patch_json( '/tenders/{}/awards/{}?acc_token={}'.format( self.tender_id, award_ids[0], owner_token), { "data": { "status": "active", "qualified": True, "eligible": True } }) self.assertEqual(response.status, '200 OK') # Fill Agreement unit prices for award_id in award_ids[1:]: self.app.patch_json( '/tenders/{}/awards/{}?acc_token={}'.format( self.tender_id, award_id, owner_token), { "data": { "status": "active", "qualified": True, "eligible": True } }) # patch award to cancelled with open(TARGET_DIR + 'patch-award-cancelled.http', 'w') as self.app.file_obj: self.app.patch_json( '/tenders/{}/awards/{}?acc_token={}'.format( self.tender_id, award_ids[0], owner_token), {'data': { "status": "cancelled" }}) self.assertEqual(response.status, '200 OK') with open(TARGET_DIR + 'qualifications-list2.http', 'w') as self.app.file_obj: response = self.app.get('/tenders/{}/awards?acc_token={}'.format( self.tender_id, owner_token)) # get pending award award_ids = [ i['id'] for i in response.json['data'] if i['status'] == 'pending' ] # patch pending award to unsuccessful with open(TARGET_DIR + 'patch-award-unsuccessful.http', 'w') as self.app.file_obj: self.app.patch_json( '/tenders/{}/awards/{}?acc_token={}'.format( self.tender_id, award_ids[0], owner_token), {'data': { "status": "unsuccessful" }}) self.assertEqual(response.status, '200 OK') # patch unsuccessful award to cancelled with open(TARGET_DIR + 'patch-award-unsuccessful-cancelled.http', 'w') as self.app.file_obj: self.app.patch_json( '/tenders/{}/awards/{}?acc_token={}'.format( self.tender_id, award_ids[0], owner_token), {'data': { "status": "cancelled" }}) self.assertEqual(response.status, '200 OK') with open(TARGET_DIR + 'qualifications-list3.http', 'w') as self.app.file_obj: response = self.app.get('/tenders/{}/awards?acc_token={}'.format( self.tender_id, owner_token)) # get pending award award_ids = [ i['id'] for i in response.json['data'] if i['status'] == 'pending' ] with open(TARGET_DIR + 'confirm-qualification2.http', 'w') as self.app.file_obj: self.app.patch_json( '/tenders/{}/awards/{}?acc_token={}'.format( self.tender_id, award_ids[0], owner_token), { "data": { "status": "active", "qualified": True, "eligible": True } }) self.assertEqual(response.status, '200 OK') for award_id in award_ids[1:]: self.app.patch_json( '/tenders/{}/awards/{}?acc_token={}'.format( self.tender_id, award_id, owner_token), { "data": { "status": "active", "qualified": True, "eligible": True } }) self.set_status('active.awarded') with open(TARGET_DIR + 'upload-prices-document.http', 'w') as self.app.file_obj: response = self.app.post( '/tenders/{}/bids/{}/financial_documents?acc_token={}'.format( self.tender_id, bid1_id, bids_access[bid1_id]), upload_files=[('file', 'prices.xlsx', '<raw_file_data>')]) with open(TARGET_DIR + 'agreements-list.http', 'w') as self.app.file_obj: response = self.app.get('/tenders/{}/agreements'.format( self.tender_id)) agreement_id = response.json['data'][0]['id'] with open(TARGET_DIR + 'agreement-contracts-list.http', 'w') as self.app.file_obj: response = self.app.get( '/tenders/{}/agreements/{}/contracts?acc_token={}'.format( self.tender_id, agreement_id, owner_token)) contracts = response.json['data'] i = 1 for contract in contracts: j = 0.5 unit_prices = [] for unit_price in contract['unitPrices']: unit_prices.append({ 'relatedItem': unit_price['relatedItem'], 'value': { 'amount': j } }) with open( TARGET_DIR + 'agreement-contract-unitprices{}.http'.format(i), 'w') as self.app.file_obj: response = self.app.patch_json( '/tenders/{}/agreements/{}/contracts/{}?acc_token={}'. format(self.tender_id, agreement_id, contract['id'], owner_token), {'data': { 'unitPrices': unit_prices }}) i += 1 # Time travel to agreement.contractPeriod.clarificationsUntil tender = self.db.get(self.tender_id) tender['contractPeriod']['startDate'] = ( get_now() - CLARIFICATIONS_UNTIL_PERIOD - timedelta(days=1)).isoformat() tender['contractPeriod']['clarificationsUntil'] = ( get_now() - timedelta(days=1)).isoformat() self.db.save(tender) # Uploading contract documentation with open(TARGET_DIR + 'tender-agreement-upload-document.http', 'w') as self.app.file_obj: response = self.app.post( '/tenders/{}/agreements/{}/documents?acc_token={}'.format( self.tender_id, agreement_id, owner_token), upload_files=[('file', 'agreement_first_document.doc', 'content')]) self.assertEqual(response.status, '201 Created') with open(TARGET_DIR + 'tender-agreement-get-documents.http', 'w') as self.app.file_obj: response = self.app.get( '/tenders/{}/agreements/{}/documents'.format( self.tender_id, agreement_id)) self.assertEqual(response.status, '200 OK') with open(TARGET_DIR + 'tender-agreement-upload-second-document.http', 'w') as self.app.file_obj: response = self.app.post( '/tenders/{}/agreements/{}/documents?acc_token={}'.format( self.tender_id, agreement_id, owner_token), upload_files=[('file', 'agreement_second_document.doc', 'content')]) self.assertEqual(response.status, '201 Created') self.document_id = response.json['data']['id'] with open(TARGET_DIR + 'tender-agreement-patch-document.http', 'w') as self.app.file_obj: response = self.app.patch_json( '/tenders/{}/agreements/{}/documents/{}?acc_token={}'.format( self.tender_id, agreement_id, self.document_id, owner_token), { 'data': { "language": 'en', 'title_en': 'Title of Document', 'description_en': 'Description of Document' } }) self.assertEqual(response.status, '200 OK') with open(TARGET_DIR + 'tender-agreement-get-documents-again.http', 'w') as self.app.file_obj: response = self.app.get( '/tenders/{}/agreements/{}/documents'.format( self.tender_id, agreement_id)) self.assertEqual(response.status, '200 OK') with open(TARGET_DIR + 'tender-agreement-get.http', 'w') as self.app.file_obj: response = self.app.get( '/tenders/{}/agreements/{}?acc_token={}'.format( self.tender_id, agreement_id, owner_token)) self.assertEqual(response.status, '200 OK') # Agreement signing self.tick() with open(TARGET_DIR + 'tender-agreement-sign-date.http', 'w') as self.app.file_obj: response = self.app.patch_json( '/tenders/{}/agreements/{}?acc_token={}'.format( self.tender_id, agreement_id, owner_token), {"data": { "dateSigned": get_now().isoformat() }}) self.assertIn('dateSigned', response.json['data']) with open(TARGET_DIR + 'tender-agreement-sign.http', 'w') as self.app.file_obj: response = self.app.patch_json( '/tenders/{}/agreements/{}?acc_token={}'.format( self.tender_id, agreement_id, owner_token), { "data": { "status": "active", "period": { "startDate": get_now().isoformat(), "endDate": (get_now() + timedelta(days=4 * 365)).isoformat() } } }) self.assertEqual(response.json['data']['status'], 'active') with open(TARGET_DIR + 'tender-completed.http', 'w') as self.app.file_obj: response = self.app.get('/tenders/{}'.format(self.tender_id)) self.assertEqual(response.json['data']['status'], 'complete') # self.contract_id = response.json['data'][0]['id'] # Rollback agreement signing tender = self.db.get(self.tender_id) tender['status'] = 'active.tendering' tender['agreements'][0]['status'] = 'pending' self.db.save(tender) # Preparing the cancellation request with open(TARGET_DIR + 'prepare-cancellation.http', 'w') as self.app.file_obj: response = self.app.post_json( '/tenders/{}/cancellations?acc_token={}'.format( self.tender_id, owner_token), { 'data': { 'reason': 'cancellation reason', 'reasonType': 'noDemand' } }) self.assertEqual(response.status, '201 Created') cancellation_id = response.json['data']['id'] with open(TARGET_DIR + 'update-cancellation-reasonType.http', 'w') as self.app.file_obj: response = self.app.patch_json( '/tenders/{}/cancellations/{}?acc_token={}'.format( self.tender_id, cancellation_id, owner_token), {"data": { 'reasonType': 'unFixable' }}) self.assertEqual(response.status, '200 OK') # Filling cancellation with protocol and supplementary documentation with open(TARGET_DIR + 'upload-cancellation-doc.http', 'w') as self.app.file_obj: response = self.app.post( '/tenders/{}/cancellations/{}/documents?acc_token={}'.format( self.tender_id, cancellation_id, owner_token), upload_files=[('file', u'Notice.pdf', 'content')]) cancellation_doc_id = response.json['data']['id'] self.assertEqual(response.status, '201 Created') with open(TARGET_DIR + 'patch-cancellation.http', 'w') as self.app.file_obj: response = self.app.patch_json( '/tenders/{}/cancellations/{}/documents/{}?acc_token={}'. format(self.tender_id, cancellation_id, cancellation_doc_id, owner_token), {'data': { "description": 'Changed description' }}) self.assertEqual(response.status, '200 OK') with open(TARGET_DIR + 'update-cancellation-doc.http', 'w') as self.app.file_obj: response = self.app.put( '/tenders/{}/cancellations/{}/documents/{}?acc_token={}'. format(self.tender_id, cancellation_id, cancellation_doc_id, owner_token), upload_files=[('file', 'Notice-2.pdf', 'content2')]) self.assertEqual(response.status, '200 OK') # Activating the request and cancelling tender with open(TARGET_DIR + 'pending-cancellation.http', 'w') as self.app.file_obj: response = self.app.patch_json( '/tenders/{}/cancellations/{}?acc_token={}'.format( self.tender_id, cancellation_id, owner_token), {'data': { "status": "pending" }}) self.assertEqual(response.status, '200 OK') self.tick(delta=timedelta(days=11)) self.check_chronograph() with open(TARGET_DIR + 'active-cancellation.http', 'w') as self.app.file_obj: response = self.app.get( '/tenders/{}/cancellations/{}?acc_token={}'.format( self.tender_id, cancellation_id, owner_token)) self.assertEqual(response.status, '200 OK') # transfer agreement to unsuccessful tender = self.db.get(self.tender_id) tender['status'] = 'active.awarded' tender['agreements'][0]['status'] = 'pending' del tender['cancellations'] self.db.save(tender) with open(TARGET_DIR + 'agreement-unsuccessful.http', 'w') as self.app.file_obj: response = self.app.patch_json( '/tenders/{}/agreements/{}?acc_token={}'.format( self.tender_id, agreement_id, owner_token), {"data": { "status": "unsuccessful" }}) self.assertEqual(response.status, '200 OK') with open(TARGET_DIR + 'tender-unsuccessful.http', 'w') as self.app.file_obj: response = self.app.get('/tenders/{}'.format(self.tender_id)) self.assertEqual(response.status, '200 OK')
def change_date_signed_very_old_contracts_data(self): # prepare old contract data contract = self.databases.contracts.get(self.contract["id"]) contract["dateSigned"] = None self.databases.contracts.save(contract) response = self.app.get("/contracts/{}?acc_token={}".format(self.contract["id"], self.contract_token)) self.assertEqual(response.status, "200 OK") self.assertNotIn("dateSigned", response.json["data"]) self.app.authorization = ("Basic", ("broker", "")) response = self.app.post_json( "/contracts/{}/changes?acc_token={}".format(self.contract["id"], self.contract_token), { "data": { "rationale": "причина зміни укр", "rationale_en": "change cause en", "rationaleTypes": ["priceReduction"], "contractNumber": "№ 146", } }, ) self.assertEqual(response.status, "201 Created") self.assertEqual(response.content_type, "application/json") change = response.json["data"] self.assertEqual(change["status"], "pending") response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"status": "active"}}, status=403, ) self.assertEqual(response.status, "403 Forbidden") self.assertEqual( response.json["errors"], [ { "location": "body", "name": "data", "description": "Can't update contract change status. 'dateSigned' is required.", } ], ) one_day_in_past = (get_now() - timedelta(days=1)).isoformat() response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"status": "active", "dateSigned": one_day_in_past}}, ) self.assertEqual(response.json["data"]["status"], "active") self.assertEqual(response.json["data"]["dateSigned"], one_day_in_past) response = self.app.post_json( "/contracts/{}/changes?acc_token={}".format(self.contract["id"], self.contract_token), { "data": { "rationale": "iнша причина зміни укр", "rationale_en": "another change cause en", "rationaleTypes": ["priceReduction"], "contractNumber": "№ 147", } }, ) self.assertEqual(response.status, "201 Created") self.assertEqual(response.content_type, "application/json") change2 = response.json["data"] self.assertEqual(change["status"], "pending") two_days_in_past = (get_now() - timedelta(days=2)).isoformat() response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change2["id"], self.contract_token), {"data": {"dateSigned": two_days_in_past}}, status=403, ) self.assertEqual( "Change dateSigned ({}) can't be earlier than last active change dateSigned ({})".format( two_days_in_past, one_day_in_past ), response.json["errors"][0]["description"], ) valid_date = get_now().isoformat() response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change2["id"], self.contract_token), {"data": {"status": "active", "dateSigned": valid_date}}, ) self.assertEqual(response.json["data"]["status"], "active") self.assertEqual(response.json["data"]["dateSigned"], valid_date) # prepare old contract change data contract = self.databases.contracts.get(self.contract["id"]) last_change = contract["changes"][-1] last_change["dateSigned"] = None self.databases.contracts.save(contract) response = self.app.get( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], last_change["id"], self.contract_token) ) self.assertEqual(response.status, "200 OK") self.assertNotIn("dateSigned", response.json["data"]) response = self.app.post_json( "/contracts/{}/changes?acc_token={}".format(self.contract["id"], self.contract_token), { "data": { "rationale": "третя причина зміни укр", "rationale_en": "third change cause en", "rationaleTypes": ["priceReduction"], "contractNumber": "№ 148", } }, ) self.assertEqual(response.status, "201 Created") change3 = response.json["data"] self.assertEqual(change["status"], "pending") response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change3["id"], self.contract_token), {"data": {"dateSigned": two_days_in_past}}, status=403, ) self.assertEqual( "Change dateSigned ({}) can't be earlier than last active change dateSigned ({})".format( two_days_in_past, last_change["date"] ), response.json["errors"][0]["description"], ) valid_date2 = get_now().isoformat() response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change3["id"], self.contract_token), {"data": {"status": "active", "dateSigned": valid_date2}}, ) self.assertEqual(response.json["data"]["status"], "active") self.assertEqual(response.json["data"]["dateSigned"], valid_date2)
def switch_bid_status_unsuccessul_to_active(self): bid_id, bid_token = self.initial_bids_tokens.items()[0] response = self.app.get("/tenders/{}/qualifications?acc_token={}".format(self.tender_id, self.tender_token)) self.assertEqual(response.content_type, "application/json") qualifications = response.json["data"] self.assertEqual(len(qualifications), 3) qualification_id = "" for qualification in qualifications: status = "active" if qualification["bidID"] == bid_id: status = "unsuccessful" qualification_id = qualification["id"] response = self.app.patch_json( "/tenders/{}/qualifications/{}?acc_token={}".format(self.tender_id, qualification["id"], self.tender_token), {"data": {"status": status, "qualified": True, "eligible": True}}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.json["data"]["status"], status) response = self.app.patch_json( "/tenders/{}?acc_token={}".format(self.tender_id, self.tender_token), {"data": {"status": "active.pre-qualification.stand-still"}}, ) self.assertEqual(response.status, "200 OK") end_date = parse_date(response.json["data"]["qualificationPeriod"]["endDate"]) date = parse_date(response.json["data"]["date"]) duration = (end_date - date).total_seconds() if SANDBOX_MODE: duration = ceil(duration) * 1440 duration = duration / 86400 # days float self.assertEqual(int(duration), 5) # create complaint response = self.app.post_json( "/tenders/{}/qualifications/{}/complaints?acc_token={}".format(self.tender_id, qualification_id, bid_token), { "data": test_complaint }, ) self.assertEqual(response.status, "201 Created") self.assertEqual(response.content_type, "application/json") complaint = response.json["data"] complaint_token = response.json["access"]["token"] if RELEASE_2020_04_19 < get_now(): self.assertEqual(response.json["data"]["status"], "draft") with change_auth(self.app, ("Basic", ("bot", ""))): response = self.app.patch_json( "/tenders/{}/qualifications/{}/complaints/{}".format( self.tender_id, qualification_id, complaint["id"]), {"data": {"status": "pending"}}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.content_type, "application/json") self.assertEqual(response.json["data"]["status"], "pending") self.app.authorization = ("Basic", ("reviewer", "")) now = get_now() data = {"status": "accepted"} if RELEASE_2020_04_19 < now: data.update({ "reviewDate": now.isoformat(), "reviewPlace": "some", }) response = self.app.patch_json( "/tenders/{}/qualifications/{}/complaints/{}".format(self.tender_id, qualification_id, complaint["id"]), {"data": data}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.content_type, "application/json") self.assertEqual(response.json["data"]["status"], "accepted") if RELEASE_2020_04_19 < now: self.assertEqual(response.json["data"]["reviewPlace"], "some") self.assertEqual(response.json["data"]["reviewDate"], now.isoformat()) response = self.app.patch_json( "/tenders/{}/qualifications/{}/complaints/{}".format(self.tender_id, qualification_id, complaint["id"]), {"data": {"status": "satisfied"}}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.content_type, "application/json") self.assertEqual(response.json["data"]["status"], "satisfied") # Cancell qualification self.app.authorization = ("Basic", ("broker", "")) response = self.app.patch_json( "/tenders/{}/qualifications/{}?acc_token={}".format(self.tender_id, qualification_id, self.tender_token), {"data": {"status": "cancelled"}}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.json["data"]["status"], "cancelled") new_qualification_id = response.headers["location"].split("/")[-1] response = self.app.patch_json( "/tenders/{}/qualifications/{}?acc_token={}".format(self.tender_id, new_qualification_id, self.tender_token), {"data": {"status": "active", "qualified": True, "eligible": True}}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.json["data"]["status"], "active") response = self.app.get("/tenders/{}/bids".format(self.tender_id)) for b in response.json["data"]: self.assertEqual(b["status"], "active")
def patch(self): """Tender Edit (partial) For example here is how procuring entity can change number of items to be procured and total Value of a tender: .. sourcecode:: http PATCH /tenders/4879d3f8ee2443169b5fbbc9f89fa607 HTTP/1.1 Host: example.com Accept: application/json { "data": { "value": { "amount": 600 }, "itemsToBeProcured": [ { "quantity": 6 } ] } } And here is the response to be expected: .. sourcecode:: http HTTP/1.0 200 OK Content-Type: application/json { "data": { "id": "4879d3f8ee2443169b5fbbc9f89fa607", "tenderID": "UA-64e93250be76435397e8c992ed4214d1", "dateModified": "2014-10-27T08:12:34.956Z", "value": { "amount": 600 }, "itemsToBeProcured": [ { "quantity": 6 } ] } } """ tender = self.context data = self.request.validated['data'] if self.request.authenticated_role == 'tender_owner' and \ self.request.validated['tender_status'] in ['active.tendering', STAGE2_STATUS]: if 'tenderPeriod' in data and 'endDate' in data['tenderPeriod']: self.request.validated['tender'].tenderPeriod.import_data(data['tenderPeriod']) validate_tender_period_extension(self.request) self.request.registry.notify(TenderInitializeEvent(self.request.validated['tender'])) self.request.validated['data']["enquiryPeriod"] = self.request.validated['tender'].enquiryPeriod.serialize() apply_patch(self.request, save=False, src=self.request.validated['tender_src']) if self.request.authenticated_role == 'chronograph': check_status_eu(self.request) elif self.request.authenticated_role == 'tender_owner' and tender.status == 'active.tendering': tender.invalidate_bids_data() elif self.request.authenticated_role == 'tender_owner' and \ self.request.validated['tender_status'] == 'active.pre-qualification' and \ tender.status == "active.pre-qualification.stand-still": if all_bids_are_reviewed(self.request): tender.qualificationPeriod.endDate = calculate_business_date(get_now(), COMPLAINT_STAND_STILL, self.request.validated['tender']) tender.check_auction_time() else: raise_operation_error(self.request, 'Can\'t switch to \'active.pre-qualification.stand-still\' while not all bids are qualified') save_tender(self.request) self.LOGGER.info('Updated tender {}'.format(tender.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_patch'})) return {'data': tender.serialize(tender.status)}
def validate_add_complaint_not_in_complaint_period(request): if request.context.complaintPeriod and \ (request.context.complaintPeriod.startDate and request.context.complaintPeriod.startDate > get_now() or request.context.complaintPeriod.endDate and request.context.complaintPeriod.endDate < get_now()): request.errors.add('body', 'data', 'Can add complaint only in complaintPeriod') request.errors.status = 403 raise error_handler(request.errors)
def listing(self): response = self.app.get('/contracts') self.assertEqual(response.status, '200 OK') self.assertEqual(len(response.json['data']), 0) contracts = [] for i in range(3): data = deepcopy(self.initial_data) data['id'] = uuid4().hex offset = get_now().isoformat() response = self.app.post_json('/contracts', {'data': data}) self.assertEqual(response.status, '201 Created') self.assertEqual(response.content_type, 'application/json') contracts.append(response.json['data']) ids = ','.join([i['id'] for i in contracts]) while True: response = self.app.get('/contracts') self.assertEqual(response.status, '200 OK') self.assertTrue(ids.startswith(','.join([i['id'] for i in response.json['data']]))) if len(response.json['data']) == 3: break self.assertEqual(len(response.json['data']), 3) self.assertEqual(','.join([i['id'] for i in response.json['data']]), ids) self.assertEqual(set(response.json['data'][0]), set([u'id', u'dateModified'])) self.assertEqual(set([i['id'] for i in response.json['data']]), set([i['id'] for i in contracts])) self.assertEqual(set([i['dateModified'] for i in response.json['data']]), set([i['dateModified'] for i in contracts])) self.assertEqual([i['dateModified'] for i in response.json['data']], sorted([i['dateModified'] for i in contracts])) response = self.app.get('/contracts?offset={}'.format(offset)) self.assertEqual(response.status, '200 OK') self.assertEqual(len(response.json['data']), 1) response = self.app.get('/contracts?limit=2') self.assertEqual(response.status, '200 OK') self.assertNotIn('prev_page', response.json) self.assertEqual(len(response.json['data']), 2) response = self.app.get(response.json['next_page']['path'].replace(ROUTE_PREFIX, '')) self.assertEqual(response.status, '200 OK') self.assertIn('descending=1', response.json['prev_page']['uri']) self.assertEqual(len(response.json['data']), 1) response = self.app.get(response.json['next_page']['path'].replace(ROUTE_PREFIX, '')) self.assertEqual(response.status, '200 OK') self.assertIn('descending=1', response.json['prev_page']['uri']) self.assertEqual(len(response.json['data']), 0) response = self.app.get('/contracts', params=[('opt_fields', 'contractID')]) self.assertEqual(response.status, '200 OK') self.assertEqual(len(response.json['data']), 3) self.assertEqual(set(response.json['data'][0]), set([u'id', u'dateModified', u'contractID'])) self.assertIn('opt_fields=contractID', response.json['next_page']['uri']) response = self.app.get('/contracts?descending=1') self.assertEqual(response.status, '200 OK') self.assertEqual(response.content_type, 'application/json') self.assertEqual(len(response.json['data']), 3) self.assertEqual(set(response.json['data'][0]), set([u'id', u'dateModified'])) self.assertEqual(set([i['id'] for i in response.json['data']]), set([i['id'] for i in contracts])) self.assertEqual([i['dateModified'] for i in response.json['data']], sorted([i['dateModified'] for i in contracts], reverse=True)) response = self.app.get('/contracts?descending=1&limit=2') self.assertEqual(response.status, '200 OK') self.assertNotIn('descending=1', response.json['prev_page']['uri']) self.assertEqual(len(response.json['data']), 2) response = self.app.get(response.json['next_page']['path'].replace(ROUTE_PREFIX, '')) self.assertEqual(response.status, '200 OK') self.assertNotIn('descending=1', response.json['prev_page']['uri']) self.assertEqual(len(response.json['data']), 1) response = self.app.get(response.json['next_page']['path'].replace(ROUTE_PREFIX, '')) self.assertEqual(response.status, '200 OK') self.assertNotIn('descending=1', response.json['prev_page']['uri']) self.assertEqual(len(response.json['data']), 0) test_contract_data2 = deepcopy(self.initial_data) test_contract_data2['mode'] = 'test' response = self.app.post_json('/contracts', {'data': test_contract_data2}) self.assertEqual(response.status, '201 Created') self.assertEqual(response.content_type, 'application/json') while True: response = self.app.get('/contracts?mode=test') self.assertEqual(response.status, '200 OK') if len(response.json['data']) == 1: break self.assertEqual(len(response.json['data']), 1) response = self.app.get('/contracts?mode=_all_') self.assertEqual(response.status, '200 OK') self.assertEqual(len(response.json['data']), 4) response = self.app.get('/contracts?mode=_all_&opt_fields=status') self.assertEqual(response.status, '200 OK') self.assertEqual(len(response.json['data']), 4)
def contract_items_change(self): tender_token = self.initial_data['tender_token'] response = self.app.patch_json('/contracts/{}/credentials?acc_token={}'.format(self.contract['id'], tender_token), {'data': ''}) self.assertEqual(response.status, '200 OK') token = response.json['access']['token'] response = self.app.get('/contracts/{}'.format(self.contract['id'])) self.assertEqual(response.status, '200 OK') self.assertEqual(response.content_type, 'application/json') items = response.json['data']["items"] response = self.app.patch_json('/contracts/{}?acc_token={}'.format(self.contract['id'], token), {"data": {"items": [{ "quantity": 12, 'description': 'тапочки для тараканів' }]}}) self.assertEqual(response.status, '200 OK') self.assertEqual(response.json['data']['items'][0]['quantity'], 12) self.assertEqual(response.json['data']['items'][0]['description'], u'тапочки для тараканів') # add one more item item = deepcopy(items[0]) item['quantity'] = 11 response = self.app.patch_json('/contracts/{}?acc_token={}'.format(self.contract['id'], token), {"data": {"items": [{}, item]}}, status=422) self.assertEqual(response.status, '422 Unprocessable Entity') self.assertEqual(response.json['errors'], [ {"location": "body", "name": "items", "description": ["Item id should be uniq for all items"]} ]) #item['id'] = uuid4().hex #response = self.app.patch_json('/contracts/{}?acc_token={}'.format(self.contract['id'], token), #{"data": {"items": [{}, item]}}) #self.assertEqual(len(response.json['data']['items']), 2) # try to change classification response = self.app.patch_json('/contracts/{}?acc_token={}'.format(self.contract['id'], token), {"data": {"items": [{ 'classification': {'id': '19433000-0'}, }]}}) self.assertEqual(response.status, '200 OK') self.assertEqual(response.json, None) # add additional classification item_classific = deepcopy(self.initial_data['items'][0]['classification']) response = self.app.patch_json('/contracts/{}?acc_token={}'.format(self.contract['id'], token), {"data": {"items": [{ 'additionalClassifications': [{}, item_classific], }]}}) self.assertEqual(response.status, '200 OK') self.assertEqual(response.json, None) # update item fields startDate = get_now().isoformat() endDate = (get_now() + timedelta(days=90)).isoformat() response = self.app.patch_json('/contracts/{}?acc_token={}'.format(self.contract['id'], token), {"data": {"items": [{ 'deliveryAddress': {u"postalCode": u"79011", u"streetAddress": u"вул. Літаючого Хом’яка",}, 'deliveryDate': {u"startDate": startDate, u"endDate": endDate} }]}}) self.assertEqual(response.json['data']['items'][0]['deliveryAddress']['postalCode'], u"79011") self.assertEqual(response.json['data']['items'][0]['deliveryAddress']['streetAddress'], u"вул. Літаючого Хом’яка") self.assertEqual(response.json['data']['items'][0]['deliveryAddress']['region'], u"м. Київ") self.assertEqual(response.json['data']['items'][0]['deliveryAddress']['locality'], u"м. Київ") self.assertEqual(response.json['data']['items'][0]['deliveryAddress']['countryName'], u"Україна") self.assertEqual(response.json['data']['items'][0]['deliveryDate']['startDate'], startDate) self.assertEqual(response.json['data']['items'][0]['deliveryDate']['endDate'], endDate) # remove first item #response = self.app.patch_json('/contracts/{}?acc_token={}'.format(self.contract['id'], token), #{"data": {"items": [item_2]}}) #self.assertEqual(len(response.json['data']['items']), 1) #self.assertEqual(response.json['data']['items'][0], item_2) # try to remove all items response = self.app.patch_json('/contracts/{}?acc_token={}'.format(self.contract['id'], token), {"data": {"items": []}}, status=422) self.assertEqual(response.status, '422 Unprocessable Entity')
def check_tender_status(request): tender = request.validated["tender"] now = get_now() if tender.lots: for lot in tender.lots: if lot.status != "active": continue lot_awards = [i for i in tender.awards if i.lotID == lot.id] if not lot_awards: continue last_award = lot_awards[-1] if last_award.status == "unsuccessful": LOGGER.info( "Switched lot {} of tender {} to {}".format(lot.id, tender.id, "unsuccessful"), extra=context_unpack(request, {"MESSAGE_ID": "switched_lot_unsuccessful"}, {"LOT_ID": lot.id}), ) lot.status = "unsuccessful" continue elif last_award.status == "active" and any( [i.status == "active" and i.awardID == last_award.id for i in tender.contracts] ): LOGGER.info( "Switched lot {} of tender {} to {}".format(lot.id, tender.id, "complete"), extra=context_unpack(request, {"MESSAGE_ID": "switched_lot_complete"}, {"LOT_ID": lot.id}), ) lot.status = "complete" statuses = set([lot.status for lot in tender.lots]) if statuses == set(["cancelled"]): LOGGER.info( "Switched tender {} to {}".format(tender.id, "cancelled"), extra=context_unpack(request, {"MESSAGE_ID": "switched_tender_cancelled"}), ) tender.status = "cancelled" elif not statuses.difference(set(["unsuccessful", "cancelled"])): LOGGER.info( "Switched tender {} to {}".format(tender.id, "unsuccessful"), extra=context_unpack(request, {"MESSAGE_ID": "switched_tender_unsuccessful"}), ) tender.status = "unsuccessful" elif not statuses.difference(set(["complete", "unsuccessful", "cancelled"])): LOGGER.info( "Switched tender {} to {}".format(tender.id, "complete"), extra=context_unpack(request, {"MESSAGE_ID": "switched_tender_complete"}), ) tender.status = "complete" else: last_award_status = tender.awards[-1].status if tender.awards else "" if last_award_status == "unsuccessful": LOGGER.info( "Switched tender {} to {}".format(tender.id, "unsuccessful"), extra=context_unpack(request, {"MESSAGE_ID": "switched_tender_unsuccessful"}), ) tender.status = "unsuccessful" if ( tender.contracts and any([contract.status == "active" for contract in tender.contracts]) and not any([contract.status == "pending" for contract in tender.contracts]) ): tender.status = "complete"
def patch_tender_contract(self): response = self.app.get("/tenders/{}/contracts".format(self.tender_id)) contract = response.json["data"][0] fake_contractID = "myselfID" fake_items_data = [{"description": "New Description"}] fake_suppliers_data = [{"name": "New Name"}] response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "value": { "amountNet": contract["value"]["amount"] - 1 } }}, ) self.assertEqual(response.status, "200 OK") self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), { "data": { "contractID": fake_contractID, "items": fake_items_data, "suppliers": fake_suppliers_data } }, ) response = self.app.get("/tenders/{}/contracts/{}".format( self.tender_id, contract["id"])) self.assertNotEqual(fake_contractID, response.json["data"]["contractID"]) self.assertNotEqual(fake_items_data, response.json["data"]["items"]) self.assertNotEqual(fake_suppliers_data, response.json["data"]["suppliers"]) response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "value": { "currency": "USD" } }}, status=403, ) self.assertEqual(response.status, "403 Forbidden") self.assertEqual(response.json["errors"][0]["description"], "Can't update currency for contract value") response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "status": "active" }}, status=403, ) self.assertEqual(response.status, "403 Forbidden") self.assertEqual(response.content_type, "application/json") self.assertIn("Can't sign contract before stand-still period end (", response.json["errors"][0]["description"]) self.set_status("complete", {"status": "active.awarded"}) token = self.initial_bids_tokens[self.initial_bids[0]["id"]] response = self.app.post_json( "/tenders/{}/awards/{}/complaints?acc_token={}".format( self.tender_id, self.award_id, token), {"data": test_draft_complaint}, ) self.assertEqual(response.status, "201 Created") complaint = response.json["data"] owner_token = response.json["access"]["token"] response = self.app.patch_json( "/tenders/{}/awards/{}/complaints/{}?acc_token={}".format( self.tender_id, self.award_id, complaint["id"], owner_token), {"data": { "status": "pending" }}, ) self.assertEqual(response.status, "200 OK") tender = self.db.get(self.tender_id) for i in tender.get("awards", []): i["complaintPeriod"]["endDate"] = i["complaintPeriod"]["startDate"] self.db.save(tender) response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "dateSigned": i["complaintPeriod"]["endDate"] }}, status=422, ) self.assertEqual(response.status, "422 Unprocessable Entity") self.assertEqual( response.json["errors"], [{ u"description": [ u"Contract signature date should be after award complaint period end date ({})" .format(i["complaintPeriod"]["endDate"]) ], u"location": u"body", u"name": u"dateSigned", }], ) one_hour_in_furure = (get_now() + timedelta(hours=1)).isoformat() response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "dateSigned": one_hour_in_furure }}, status=422, ) self.assertEqual(response.status, "422 Unprocessable Entity") self.assertEqual( response.json["errors"], [{ u"description": [u"Contract signature date can't be in the future"], u"location": u"body", u"name": u"dateSigned", }], ) custom_signature_date = get_now().isoformat() response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "dateSigned": custom_signature_date }}, ) self.assertEqual(response.status, "200 OK") response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "status": "active" }}, status=403, ) self.assertEqual(response.status, "403 Forbidden") self.assertEqual(response.content_type, "application/json") self.assertEqual(response.json["errors"][0]["description"], "Can't sign contract before reviewing all complaints") response = self.app.patch_json( "/tenders/{}/awards/{}/complaints/{}?acc_token={}".format( self.tender_id, self.award_id, complaint["id"], owner_token), {"data": { "status": "stopping", "cancellationReason": "reason" }}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.content_type, "application/json") self.assertEqual(response.json["data"]["status"], "stopping") authorization = self.app.authorization self.app.authorization = ("Basic", ("reviewer", "")) now = get_now() data = {"status": "stopped"} if RELEASE_2020_04_19 < now: data.update({ "status": "declined", "rejectReason": "tenderCancelled", "rejectReasonDescription": "reject reason description" }) response = self.app.patch_json( "/tenders/{}/awards/{}/complaints/{}".format(self.tender_id, self.award_id, complaint["id"]), {"data": data}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.json["data"]["status"], data["status"]) self.app.authorization = authorization 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.content_type, "application/json") self.assertEqual(response.json["data"]["status"], "active") response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), { "data": { "value": { "amount": 232 }, "contractID": "myselfID", "title": "New Title", "items": [{ "description": "New Description" }], "suppliers": [{ "name": "New Name" }], } }, status=403, ) self.assertEqual(response.status, "403 Forbidden") self.assertEqual( response.json["errors"][0]["description"], "Can't update contract in current (complete) tender status") response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "status": "active" }}, 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 contract in current (complete) tender status") response = self.app.patch_json( "/tenders/{}/contracts/{}?acc_token={}".format(self.tender_id, contract["id"], self.tender_token), {"data": { "status": "pending" }}, 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 contract in current (complete) tender status") response = self.app.patch_json("/tenders/{}/contracts/some_id".format( self.tender_id), {"data": { "status": "active" }}, 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"contract_id" }]) response = self.app.patch_json("/tenders/some_id/contracts/some_id", {"data": { "status": "active" }}, 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"tender_id" }]) response = self.app.get("/tenders/{}/contracts/{}".format( self.tender_id, contract["id"])) self.assertEqual(response.status, "200 OK") self.assertEqual(response.content_type, "application/json") self.assertEqual(response.json["data"]["status"], "active")
def wrapper(): return PeriodStartEndRequired({"startDate": get_now(), "endDate": calculate_business_date(get_now(), tendering_duration)})
def time_shift(self, status, extra=None): now = get_now() tender = self.db.get(self.tender_id) data = {} if status == 'enquiryPeriod_ends': data.update({ "enquiryPeriod": { "startDate": (now - timedelta(days=28)).isoformat(), "endDate": (now - timedelta(days=1)).isoformat() }, "tenderPeriod": { "startDate": (now - timedelta(days=28)).isoformat(), "endDate": (now + timedelta(days=2)).isoformat() }, }) if status == 'active.pre-qualification': data.update({ "enquiryPeriod": { "startDate": (now - TENDERING_DURATION).isoformat(), "endDate": (now - QUESTIONS_STAND_STILL).isoformat() }, "tenderPeriod": { "startDate": (now - TENDERING_DURATION).isoformat(), "endDate": (now).isoformat(), } }) elif status == 'active.pre-qualification.stand-still': data.update({ "enquiryPeriod": { "startDate": (now - TENDERING_DURATION).isoformat(), "endDate": (now - QUESTIONS_STAND_STILL).isoformat() }, "tenderPeriod": { "startDate": (now - TENDERING_DURATION).isoformat(), "endDate": (now).isoformat(), }, "qualificationPeriod": { "startDate": (now).isoformat(), }, }) if 'lots' in tender and tender['lots']: data['lots'] = [] for index, lot in enumerate(tender['lots']): lot_data = {'id': lot['id']} if lot['status'] is 'active': lot_data["auctionPeriod"] = { "startDate": (now + COMPLAINT_STAND_STILL).isoformat() } data['lots'].append(lot_data) else: data.update({ "auctionPeriod": { "startDate": (now + COMPLAINT_STAND_STILL).isoformat() } }) elif status == 'active.auction': data.update({ "enquiryPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL).isoformat(), "endDate": (now - COMPLAINT_STAND_STILL - TENDERING_DURATION + QUESTIONS_STAND_STILL).isoformat() }, "tenderPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL).isoformat(), "endDate": (now - COMPLAINT_STAND_STILL).isoformat() }, "qualificationPeriod": { "startDate": (now - COMPLAINT_STAND_STILL).isoformat(), "endDate": (now).isoformat() } }) if 'lots' in tender and tender['lots']: data['lots'] = [] for index, lot in enumerate(tender['lots']): lot_data = {'id': lot['id']} if lot['status'] == 'active': lot_data["auctionPeriod"] = { "startDate": (now).isoformat() } data['lots'].append(lot_data) else: data.update({"auctionPeriod": {"startDate": now.isoformat()}}) elif status == 'complete': data.update({ "enquiryPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL - timedelta(days=3)).isoformat(), "endDate": (now - QUESTIONS_STAND_STILL - COMPLAINT_STAND_STILL - timedelta(days=3)).isoformat() }, "tenderPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL - timedelta(days=3)).isoformat(), "endDate": (now - COMPLAINT_STAND_STILL - timedelta(days=3)).isoformat() }, "auctionPeriod": { "startDate": (now - timedelta(days=3)).isoformat(), "endDate": (now - timedelta(days=2)).isoformat() }, "awardPeriod": { "startDate": (now - timedelta(days=1)).isoformat(), "endDate": (now).isoformat() } }) if self.initial_lots: data.update({ 'lots': [{ "auctionPeriod": { "startDate": (now - timedelta(days=3)).isoformat(), "endDate": (now - timedelta(days=2)).isoformat() } } for i in self.initial_lots] }) if extra: data.update(extra) tender.update(apply_data_patch(tender, data)) self.db.save(tender)
def listing_changes(self): response = self.app.get("/qualifications") self.assertEqual(response.status, "200 OK") self.assertEqual(len(response.json["data"]), 0) qualifications = [] data = deepcopy(self.initial_submission_data) tenderer_ids = ["00037256", "00037257", "00037258"] for i in tenderer_ids: data["tenderers"][0]["identifier"]["id"] = i offset = get_now().isoformat() response = self.app.post_json("/submissions", {"data": data}) self.assertEqual(response.status, "201 Created") self.assertEqual(response.content_type, "application/json") response = self.app.patch_json( "/submissions/{}?acc_token={}".format( response.json["data"]["id"], response.json["access"]["token"]), {"data": { "status": "active" }}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.content_type, "application/json") qualification_id = response.json["data"]["qualificationID"] response = self.app.patch_json( "/qualifications/{}?acc_token={}".format(qualification_id, self.framework_token), {"data": { "status": "active" }}) qualifications.append(response.json["data"]) ids = ",".join([i["id"] for i in qualifications]) while True: response = self.app.get("/qualifications?feed=changes") self.assertTrue( ids.startswith(",".join([i["id"] for i in response.json["data"]]))) if len(response.json["data"]) == 3: break self.assertEqual(",".join([i["id"] for i in response.json["data"]]), ids) self.assertEqual(response.status, "200 OK") self.assertEqual(len(response.json["data"]), 3) self.assertEqual(set(response.json["data"][0]), set(["id", "dateModified"])) self.assertEqual(set([i["id"] for i in response.json["data"]]), set([i["id"] for i in qualifications])) self.assertEqual(set([i["dateModified"] for i in response.json["data"]]), set([i["dateModified"] for i in qualifications])) self.assertEqual([i["dateModified"] for i in response.json["data"]], sorted([i["dateModified"] for i in qualifications])) response = self.app.get("/qualifications?feed=changes&limit=2") self.assertEqual(response.status, "200 OK") self.assertNotIn("prev_page", response.json) self.assertEqual(len(response.json["data"]), 2) response = self.app.get(response.json["next_page"]["path"].replace( ROUTE_PREFIX, "")) self.assertEqual(response.status, "200 OK") self.assertIn("descending=1", response.json["prev_page"]["uri"]) self.assertEqual(len(response.json["data"]), 1) response = self.app.get(response.json["next_page"]["path"].replace( ROUTE_PREFIX, "")) self.assertEqual(response.status, "200 OK") self.assertIn("descending=1", response.json["prev_page"]["uri"]) self.assertEqual(len(response.json["data"]), 0) response = self.app.get("/qualifications?feed=changes", params=[("opt_fields", "status")]) self.assertEqual(response.status, "200 OK") self.assertEqual(len(response.json["data"]), 3) self.assertEqual(set(response.json["data"][0]), set(["id", "dateModified", "status"])) self.assertIn("opt_fields=status", response.json["next_page"]["uri"]) response = self.app.get("/qualifications?feed=changes", params=[("opt_fields", "status,owner")]) self.assertEqual(response.status, "200 OK") self.assertEqual(len(response.json["data"]), 3) self.assertEqual(set(response.json["data"][0]), set(["id", "dateModified", "status"])) self.assertIn("opt_fields=status", response.json["next_page"]["uri"]) response = self.app.get("/qualifications?feed=changes&descending=1") self.assertEqual(response.status, "200 OK") self.assertEqual(response.content_type, "application/json") self.assertEqual(len(response.json["data"]), 3) self.assertEqual(set(response.json["data"][0]), set(["id", "dateModified"])) self.assertEqual(set([i["id"] for i in response.json["data"]]), set([i["id"] for i in qualifications])) self.assertEqual([i["dateModified"] for i in response.json["data"]], sorted([i["dateModified"] for i in qualifications], reverse=True)) response = self.app.get( "/qualifications?feed=changes&descending=1&limit=2") self.assertEqual(response.status, "200 OK") self.assertNotIn("descending=1", response.json["prev_page"]["uri"]) self.assertEqual(len(response.json["data"]), 2) response = self.app.get(response.json["next_page"]["path"].replace( ROUTE_PREFIX, "")) self.assertEqual(response.status, "200 OK") self.assertNotIn("descending=1", response.json["prev_page"]["uri"]) self.assertEqual(len(response.json["data"]), 1) response = self.app.get(response.json["next_page"]["path"].replace( ROUTE_PREFIX, "")) self.assertEqual(response.status, "200 OK") self.assertNotIn("descending=1", response.json["prev_page"]["uri"]) self.assertEqual(len(response.json["data"]), 0)
def validate_add_question(request): tender = request.validated['tender'] if tender.status != 'active.enquiries' or tender.enquiryPeriod.startDate and get_now() < tender.enquiryPeriod.startDate or get_now() > tender.enquiryPeriod.endDate: raise_operation_error(request, 'Can add question only in enquiryPeriod')
def patch_submission_pending(self): response = self.app.patch_json( "/submissions/{}?acc_token={}".format(self.submission_id, self.submission_token), {"data": { "status": "active" }}, ) self.assertEqual(response.status, "200 OK") self.assertEqual(response.content_type, "application/json") qualification_id = response.json["data"]["qualificationID"] qualification_ignore_patch_data = { "date": (get_now() + timedelta(days=2)).isoformat(), "dateModified": (get_now() + timedelta(days=1)).isoformat(), "submissionID": "0" * 32, "qualificationType": "changed", } response = self.app.patch_json( "/qualifications/{}?acc_token={}".format(qualification_id, self.framework_token), {"data": qualification_ignore_patch_data}) self.assertEqual(response.status, "200 OK") self.assertEqual(response.content_type, "application/json") qualification = self.app.get( "/qualifications/{}".format(qualification_id)).json["data"] for field in qualification_ignore_patch_data: self.assertNotEqual(qualification.get(field, ""), qualification_ignore_patch_data[field]) qualification_patch_data = {"status": "active"} response = self.app.patch_json( "/qualifications/{}?acc_token={}".format(qualification_id, self.framework_token), {"data": qualification_patch_data}) self.assertEqual(response.status, "200 OK") self.assertEqual(response.content_type, "application/json") qualification = self.app.get("/qualifications/{}".format( qualification_id, self.framework_token)).json["data"] self.assertEqual(qualification["status"], "active") submission = self.app.get("/submissions/{}".format( self.submission_id)).json["data"] self.assertEqual(submission["status"], "complete") submission_data = deepcopy(self.initial_submission_data) submission_data["tenderers"][0]["identifier"]["id"] = "00037258" response = self.app.post_json("/submissions", {"data": submission_data}) self.assertEqual(response.status, "201 Created") submission_id = response.json["data"]["id"] submission_token = response.json["access"]["token"] response = self.app.patch_json( "/submissions/{}?acc_token={}".format(submission_id, submission_token), {"data": { "status": "active" }}, ) self.assertEqual(response.status, "200 OK") qualification_id = response.json["data"]["qualificationID"] response = self.app.patch_json( "/qualifications/{}?acc_token={}".format(qualification_id, self.framework_token), {"data": { "status": "unsuccessful" }}) self.assertEqual(response.status, "200 OK") self.assertEqual(response.content_type, "application/json") qualification = self.app.get("/qualifications/{}".format( qualification_id, self.framework_token)).json["data"] self.assertEqual(qualification["status"], "unsuccessful") submission = self.app.get( "/submissions/{}".format(submission_id)).json["data"] self.assertEqual(submission["status"], "complete")
# -*- coding: utf-8 -*- from copy import deepcopy from datetime import timedelta from openprocurement.api.utils import get_now from openprocurement.tender.belowthreshold.tests.base import set_tender_multi_buyers from openprocurement.tender.pricequotation.constants import PMT from openprocurement.api.constants import SANDBOX_MODE now = get_now() PERIODS = { "active.tendering": { "start": { "tenderPeriod": { "startDate": -timedelta(), "endDate": timedelta(days=8) }, }, "end": { "tenderPeriod": { "startDate": - timedelta(days=8), "endDate": timedelta() }, }, }, "active.qualification": { "start": { "tenderPeriod": {
def time_shift(self, status, extra=None): now = get_now() tender = self.db.get(self.tender_id) self.tender_document = tender data = {} if status == "enquiryPeriod_ends": data.update({ "enquiryPeriod": { "startDate": (now - timedelta(days=28)).isoformat(), "endDate": (now - timedelta(days=1)).isoformat(), }, "tenderPeriod": { "startDate": (now - timedelta(days=28)).isoformat(), "endDate": (now + timedelta(days=2)).isoformat(), }, }) if status == "active.pre-qualification": data.update({ "enquiryPeriod": { "startDate": (now - TENDERING_DURATION).isoformat(), "endDate": (now - QUESTIONS_STAND_STILL).isoformat(), }, "tenderPeriod": { "startDate": (now - TENDERING_DURATION).isoformat(), "endDate": (now).isoformat() }, }) elif status == "active.pre-qualification.stand-still": data.update({ "enquiryPeriod": { "startDate": (now - TENDERING_DURATION).isoformat(), "endDate": (now - QUESTIONS_STAND_STILL).isoformat(), }, "tenderPeriod": { "startDate": (now - TENDERING_DURATION).isoformat(), "endDate": (now).isoformat() }, "qualificationPeriod": { "startDate": (now).isoformat() }, }) if "lots" in tender and tender["lots"]: data["lots"] = [] for index, lot in enumerate(tender["lots"]): lot_data = {"id": lot["id"]} if lot["status"] is "active": lot_data["auctionPeriod"] = { "startDate": (now + COMPLAINT_STAND_STILL).isoformat() } data["lots"].append(lot_data) else: data.update({ "auctionPeriod": { "startDate": (now + COMPLAINT_STAND_STILL).isoformat() } }) elif status == "active.auction": data.update({ "enquiryPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL).isoformat(), "endDate": (now - COMPLAINT_STAND_STILL - TENDERING_DURATION + QUESTIONS_STAND_STILL).isoformat(), }, "tenderPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL).isoformat(), "endDate": (now - COMPLAINT_STAND_STILL).isoformat(), }, "qualificationPeriod": { "startDate": (now - COMPLAINT_STAND_STILL).isoformat(), "endDate": (now).isoformat(), }, }) if "lots" in tender and tender["lots"]: data["lots"] = [] for index, lot in enumerate(tender["lots"]): lot_data = {"id": lot["id"]} if lot["status"] == "active": lot_data["auctionPeriod"] = { "startDate": (now).isoformat() } data["lots"].append(lot_data) else: data.update({"auctionPeriod": {"startDate": now.isoformat()}}) elif status == "complete": data.update({ "enquiryPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL - timedelta(days=3)).isoformat(), "endDate": (now - QUESTIONS_STAND_STILL - COMPLAINT_STAND_STILL - timedelta(days=3)).isoformat(), }, "tenderPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL - timedelta(days=3)).isoformat(), "endDate": (now - COMPLAINT_STAND_STILL - timedelta(days=3)).isoformat(), }, "auctionPeriod": { "startDate": (now - timedelta(days=3)).isoformat(), "endDate": (now - timedelta(days=2)).isoformat(), }, "awardPeriod": { "startDate": (now - timedelta(days=1)).isoformat(), "endDate": (now).isoformat() }, }) if self.initial_lots: data.update({ "lots": [{ "auctionPeriod": { "startDate": (now - timedelta(days=3)).isoformat(), "endDate": (now - timedelta(days=2)).isoformat(), } } for i in self.initial_lots] }) if extra: data.update(extra) self.tender_document_patch = data self.save_changes()
def update_status(self, status, extra=None): now = get_now() data = {"status": status} if status == "active.tendering": data.update({ "enquiryPeriod": { "startDate": (now - timedelta(days=1)).isoformat(), "endDate": (now + TENDERING_DURATION - QUESTIONS_STAND_STILL).isoformat(), }, "tenderPeriod": { "startDate": (now - timedelta(days=1)).isoformat(), "endDate": (now + TENDERING_DURATION).isoformat(), }, }) elif status == "active.pre-qualification": data.update({ "enquiryPeriod": { "startDate": (now - TENDERING_DURATION - timedelta(days=1)).isoformat(), "endDate": (now - QUESTIONS_STAND_STILL).isoformat(), }, "tenderPeriod": { "startDate": (now - TENDERING_DURATION - timedelta(days=1)).isoformat(), "endDate": (now).isoformat(), }, "qualificationPeriod": { "startDate": (now).isoformat() }, }) elif status == "active.qualification": data.update({ "enquiryPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL - timedelta(days=2)).isoformat(), "endDate": (now - QUESTIONS_STAND_STILL - COMPLAINT_STAND_STILL - timedelta(days=1)).isoformat(), }, "tenderPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL - timedelta(days=2)).isoformat(), "endDate": (now - COMPLAINT_STAND_STILL - timedelta(days=1)).isoformat(), }, "auctionPeriod": { "startDate": (now - timedelta(days=1)).isoformat(), "endDate": (now).isoformat() }, "awardPeriod": { "startDate": (now).isoformat() }, }) if self.initial_lots: data.update({ "lots": [{ "auctionPeriod": { "startDate": (now - timedelta(days=1)).isoformat(), "endDate": (now).isoformat(), } } for i in self.initial_lots] }) elif status == "active.pre-qualification.stand-still": data.update({ "enquiryPeriod": { "startDate": (now - TENDERING_DURATION - timedelta(days=1)).isoformat(), "endDate": (now - QUESTIONS_STAND_STILL).isoformat(), }, "tenderPeriod": { "startDate": (now - TENDERING_DURATION - timedelta(days=1)).isoformat(), "endDate": (now).isoformat(), }, "qualificationPeriod": { "startDate": (now).isoformat() }, "auctionPeriod": { "startDate": (now + COMPLAINT_STAND_STILL).isoformat() }, }) elif status == "active.stage2.pending": data.update({ "enquiryPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL - timedelta(days=1)).isoformat(), "endDate": (now - COMPLAINT_STAND_STILL - TENDERING_DURATION + QUESTIONS_STAND_STILL).isoformat(), }, "tenderPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL - timedelta(days=1)).isoformat(), "endDate": (now - COMPLAINT_STAND_STILL).isoformat(), }, "qualificationPeriod": { "startDate": (now - COMPLAINT_STAND_STILL).isoformat(), "endDate": (now).isoformat(), }, }) if self.initial_lots: data.update({ "lots": [{ "auctionPeriod": { "startDate": (now).isoformat() } } for i in self.initial_lots] }) elif status == "active.auction": data.update({ "enquiryPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL - timedelta(days=1)).isoformat(), "endDate": (now - COMPLAINT_STAND_STILL - TENDERING_DURATION + QUESTIONS_STAND_STILL).isoformat(), }, "tenderPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL - timedelta(days=1)).isoformat(), "endDate": (now - COMPLAINT_STAND_STILL).isoformat(), }, "qualificationPeriod": { "startDate": (now - COMPLAINT_STAND_STILL).isoformat(), "endDate": (now).isoformat(), }, "auctionPeriod": { "startDate": (now).isoformat() }, }) if self.initial_lots: data.update({ "lots": [{ "auctionPeriod": { "startDate": (now).isoformat() } } for i in self.initial_lots] }) elif status == "active.awarded": data.update({ "enquiryPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL - timedelta(days=3)).isoformat(), "endDate": (now - QUESTIONS_STAND_STILL - COMPLAINT_STAND_STILL - timedelta(days=2)).isoformat(), }, "tenderPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL - timedelta(days=3)).isoformat(), "endDate": (now - COMPLAINT_STAND_STILL - timedelta(days=2)).isoformat(), }, "auctionPeriod": { "startDate": (now - timedelta(days=2)).isoformat(), "endDate": (now - timedelta(days=1)).isoformat(), }, "awardPeriod": { "startDate": (now - timedelta(days=1)).isoformat(), "endDate": (now).isoformat() }, }) if self.initial_lots: data.update({ "lots": [{ "auctionPeriod": { "startDate": (now - timedelta(days=2)).isoformat(), "endDate": (now - timedelta(days=1)).isoformat(), } } for i in self.initial_lots] }) elif status == "complete": data.update({ "enquiryPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL - timedelta(days=4)).isoformat(), "endDate": (now - QUESTIONS_STAND_STILL - COMPLAINT_STAND_STILL - timedelta(days=3)).isoformat(), }, "tenderPeriod": { "startDate": (now - TENDERING_DURATION - COMPLAINT_STAND_STILL - timedelta(days=4)).isoformat(), "endDate": (now - COMPLAINT_STAND_STILL - timedelta(days=3)).isoformat(), }, "auctionPeriod": { "startDate": (now - timedelta(days=3)).isoformat(), "endDate": (now - timedelta(days=2)).isoformat(), }, "awardPeriod": { "startDate": (now - timedelta(days=1)).isoformat(), "endDate": (now).isoformat() }, }) if self.initial_lots: data.update({ "lots": [{ "auctionPeriod": { "startDate": (now - timedelta(days=3)).isoformat(), "endDate": (now - timedelta(days=2)).isoformat(), } } for i in self.initial_lots] }) self.tender_document_patch = data if extra: self.tender_document_patch.update(extra) self.save_changes()
def update_status(self, status, extra=None): now = get_now() data = {"status": status} if status == "active.tendering": data.update({ "enquiryPeriod": { "startDate": (now).isoformat(), "endDate": (now + timedelta(days=13)).isoformat(), }, "tenderPeriod": { "startDate": (now).isoformat(), "endDate": (now + timedelta(days=16)).isoformat() }, }) elif status == "active.auction": data.update({ "enquiryPeriod": { "startDate": (now - timedelta(days=16)).isoformat(), "endDate": (now - timedelta(days=3)).isoformat(), }, "tenderPeriod": { "startDate": (now - timedelta(days=16)).isoformat(), "endDate": (now).isoformat() }, "auctionPeriod": { "startDate": (now).isoformat() }, }) if self.initial_lots: data.update({ "lots": [{ "auctionPeriod": { "startDate": (now).isoformat() } } for i in self.initial_lots] }) elif status == "active.qualification": data.update({ "enquiryPeriod": { "startDate": (now - timedelta(days=17)).isoformat(), "endDate": (now - timedelta(days=4)).isoformat(), }, "tenderPeriod": { "startDate": (now - timedelta(days=17)).isoformat(), "endDate": (now - timedelta(days=1)).isoformat(), }, "auctionPeriod": { "startDate": (now - timedelta(days=1)).isoformat(), "endDate": (now).isoformat() }, "awardPeriod": { "startDate": (now).isoformat() }, }) if self.initial_lots: data.update({ "lots": [{ "auctionPeriod": { "startDate": (now - timedelta(days=1)).isoformat(), "endDate": (now).isoformat(), } } for i in self.initial_lots] }) elif status == "active.awarded": data.update({ "enquiryPeriod": { "startDate": (now - timedelta(days=17)).isoformat(), "endDate": (now - timedelta(days=4)).isoformat(), }, "tenderPeriod": { "startDate": (now - timedelta(days=17)).isoformat(), "endDate": (now - timedelta(days=1)).isoformat(), }, "auctionPeriod": { "startDate": (now - timedelta(days=1)).isoformat(), "endDate": (now).isoformat() }, "awardPeriod": { "startDate": (now).isoformat(), "endDate": (now).isoformat() }, }) if self.initial_lots: data.update({ "lots": [{ "auctionPeriod": { "startDate": (now - timedelta(days=1)).isoformat(), "endDate": (now).isoformat(), } } for i in self.initial_lots] }) elif status == "complete": data.update({ "enquiryPeriod": { "startDate": (now - timedelta(days=25)).isoformat(), "endDate": (now - timedelta(days=11)).isoformat(), }, "tenderPeriod": { "startDate": (now - timedelta(days=25)).isoformat(), "endDate": (now - timedelta(days=8)).isoformat(), }, "auctionPeriod": { "startDate": (now - timedelta(days=8)).isoformat(), "endDate": (now - timedelta(days=7)).isoformat(), }, "awardPeriod": { "startDate": (now - timedelta(days=7)).isoformat(), "endDate": (now - timedelta(days=7)).isoformat(), }, }) if self.initial_lots: data.update({ "lots": [{ "auctionPeriod": { "startDate": (now - timedelta(days=8)).isoformat(), "endDate": (now - timedelta(days=7)).isoformat(), } } for i in self.initial_lots] }) self.tender_document_patch = data if extra: self.tender_document_patch.update(extra) self.save_changes()
def validate_id(self, data, code): schematics_document = get_schematics_document(data['__parent__']) if (schematics_document.get('revisions')[0].date if schematics_document.get('revisions') else get_now()) > ATC_INN_CLASSIFICATIONS_FROM: if data.get('scheme') == u'ATC' and code not in ATC_CODES: raise ValidationError(BaseType.MESSAGES['choices'].format(unicode(ATC_CODES))) elif data.get('scheme') == u'INN' and code not in INN_CODES: raise ValidationError(BaseType.MESSAGES['choices'].format(unicode(INN_CODES)))
def patch(self): """Post a complaint resolution for award """ tender = self.request.validated['tender'] data = self.request.validated['data'] complaintPeriod = self.request.validated['award'].complaintPeriod is_complaintPeriod = complaintPeriod.startDate < get_now( ) and complaintPeriod.endDate > get_now( ) if complaintPeriod.endDate else complaintPeriod.startDate < get_now( ) # complaint_owner if self.request.authenticated_role == 'complaint_owner' and self.context.status in [ 'draft', 'claim', 'answered' ] and data.get('status', self.context.status) == 'cancelled': apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateCanceled = get_now() elif self.request.authenticated_role == 'complaint_owner' and self.context.status in [ 'pending', 'accepted' ] and data.get('status', self.context.status) == 'stopping': apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateCanceled = get_now() elif self.request.authenticated_role == 'complaint_owner' and is_complaintPeriod and self.context.status == 'draft' and data.get( 'status', self.context.status) == self.context.status: apply_patch(self.request, save=False, src=self.context.serialize()) elif self.request.authenticated_role == 'complaint_owner' and is_complaintPeriod and self.context.status == 'draft' and data.get( 'status', self.context.status) == 'claim': if self.request.validated[ 'award'].status == 'unsuccessful' and self.request.validated[ 'award'].bid_id != self.context.bid_id: raise_operation_error( self.request, 'Can add claim only on unsuccessful award of your bid') apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateSubmitted = get_now() elif self.request.authenticated_role == 'complaint_owner' and is_complaintPeriod and self.context.status == 'draft' and data.get( 'status', self.context.status) == 'pending': if not any([ i.status == 'active' for i in tender.awards if i.lotID == self.request.validated['award'].lotID ]): raise_operation_error( self.request, 'Complaint submission is allowed only after award activation.' ) apply_patch(self.request, save=False, src=self.context.serialize()) self.context.type = 'complaint' self.context.dateSubmitted = get_now() elif self.request.authenticated_role == 'complaint_owner' and self.context.status == 'answered' and data.get( 'status', self.context.status) == self.context.status: apply_patch(self.request, save=False, src=self.context.serialize()) # tender_owner elif self.request.authenticated_role == 'tender_owner' and self.context.status in [ 'pending', 'accepted' ]: apply_patch(self.request, save=False, src=self.context.serialize()) elif self.request.authenticated_role == 'tender_owner' and self.context.status in [ 'claim', 'satisfied' ] and data.get('status', self.context.status) == self.context.status: apply_patch(self.request, save=False, src=self.context.serialize()) elif self.request.authenticated_role == 'tender_owner' and self.context.status == 'claim' and data.get( 'resolution', self.context.resolution) and data.get( 'resolutionType', self.context.resolutionType) and data.get( 'status', self.context.status) == 'answered': if len(data.get('resolution', self.context.resolution)) < 20: raise_operation_error( self.request, 'Can\'t update complaint: resolution too short') apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateAnswered = get_now() elif self.request.authenticated_role == 'tender_owner' and self.context.status == 'satisfied' and data.get( 'tendererAction', self.context.tendererAction) and data.get( 'status', self.context.status) == 'resolved': apply_patch(self.request, save=False, src=self.context.serialize()) # aboveThresholdReviewers elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status in [ 'pending', 'accepted', 'stopping' ] and data.get('status', self.context.status) == self.context.status: apply_patch(self.request, save=False, src=self.context.serialize()) elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status in [ 'pending', 'stopping' ] and data.get('status', self.context.status) in ['invalid', 'mistaken']: apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateDecision = get_now() self.context.acceptance = False elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status == 'pending' and data.get( 'status', self.context.status) == 'accepted': apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateAccepted = get_now() self.context.acceptance = True elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status in [ 'accepted', 'stopping' ] and data.get('status', self.context.status) in ['declined', 'satisfied']: apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateDecision = get_now() elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status in [ 'pending', 'accepted', 'stopping' ] and data.get('status', self.context.status) == 'stopped': apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateDecision = get_now() self.context.dateCanceled = self.context.dateCanceled or get_now() else: raise_operation_error(self.request, 'Can\'t update complaint') if self.context.tendererAction and not self.context.tendererActionDate: self.context.tendererActionDate = get_now() if self.context.status not in [ 'draft', 'claim', 'answered', 'pending', 'accepted', 'satisfied', 'stopping' ] and tender.status in ['active.qualification', 'active.awarded']: check_tender_status(self.request) if save_tender(self.request): self.LOGGER.info( 'Updated tender award complaint {}'.format(self.context.id), extra=context_unpack( self.request, {'MESSAGE_ID': 'tender_award_complaint_patch'})) return {'data': self.context.serialize("view")}
def date_signed_on_change_creation_for_very_old_contracts_data(self): # prepare old contract data contract = self.databases.contracts.get(self.contract["id"]) contract["dateSigned"] = None self.databases.contracts.save(contract) response = self.app.get("/contracts/{}?acc_token={}".format(self.contract["id"], self.contract_token)) self.assertEqual(response.status, "200 OK") self.assertNotIn("dateSigned", response.json["data"]) self.app.authorization = ("Basic", ("broker", "")) one_day_in_past = (get_now() - timedelta(days=1)).isoformat() response = self.app.post_json( "/contracts/{}/changes?acc_token={}".format(self.contract["id"], self.contract_token), { "data": { "rationale": "причина зміни укр", "rationale_en": "change cause en", "rationaleTypes": ["priceReduction"], "contractNumber": "№ 146", "dateSigned": one_day_in_past, } }, ) self.assertEqual(response.json["data"]["dateSigned"], one_day_in_past) change = response.json["data"] response = self.app.patch_json( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], change["id"], self.contract_token), {"data": {"status": "active"}}, ) self.assertEqual(response.json["data"]["status"], "active") # prepare old contract change data contract = self.databases.contracts.get(self.contract["id"]) last_change = contract["changes"][-1] last_change["dateSigned"] = None self.databases.contracts.save(contract) response = self.app.get( "/contracts/{}/changes/{}?acc_token={}".format(self.contract["id"], last_change["id"], self.contract_token) ) self.assertEqual(response.status, "200 OK") self.assertNotIn("dateSigned", response.json["data"]) response = self.app.post_json( "/contracts/{}/changes?acc_token={}".format(self.contract["id"], self.contract_token), { "data": { "rationale": "третя причина зміни укр", "rationale_en": "third change cause en", "rationaleTypes": ["priceReduction"], "contractNumber": "№ 148", "dateSigned": one_day_in_past, } }, status=403, ) self.assertEqual( "Change dateSigned ({}) can't be earlier than last active change dateSigned ({})".format( one_day_in_past, last_change["date"] ), response.json["errors"][0]["description"], ) valid_date = get_now().isoformat() response = self.app.post_json( "/contracts/{}/changes?acc_token={}".format(self.contract["id"], self.contract_token), { "data": { "rationale": "третя причина зміни укр", "rationale_en": "third change cause en", "rationaleTypes": ["priceReduction"], "contractNumber": "№ 148", "dateSigned": valid_date, } }, ) self.assertEqual(response.json["data"]["dateSigned"], valid_date)
u"region": u"м. Львів", u"locality": u"м. Львів" } } ], u"contractNumber": u"contract #13111", u"period": { u"startDate": u"2016-03-18T18:47:47.155143+02:00", u"endDate": u"2017-03-18T18:47:47.155143+02:00" }, u"value": { u"currency": u"UAH", u"amount": 238.0, u"valueAddedTaxIncluded": True }, u"dateSigned": get_now().isoformat(), u"awardID": u"8481d7eb01694c25b18658036c236c5d", u"id": uuid4().hex, u"contractID": u"UA-2016-03-18-000001-1", u"tender_id": uuid4().hex, u"tender_token": uuid4().hex, u"owner": u"broker" } test_contract_data_wo_items = deepcopy(test_contract_data) del test_contract_data_wo_items['items'] documents = [ {