def post(self): complaint = self.request.context tender = self.request.validated['tender'] award_id = self.request.validated['award_id'] data = self.request.validated['ownership_data'] if complaint.transfer_token == sha512(data['transfer']).hexdigest(): location = self.request.route_path('Tender Award Complaints', tender_id=tender.id, award_id=award_id, complaint_id=complaint.id) location = location[len(ROUTE_PREFIX):] # strips /api/<version> transfer = extract_transfer(self.request, transfer_id=data['id']) if transfer.get('usedFor') and transfer.get('usedFor') != location: self.request.errors.add('body', 'transfer', 'Transfer already used') self.request.errors.status = 403 return else: self.request.errors.add('body', 'transfer', 'Invalid transfer') self.request.errors.status = 403 return update_ownership(complaint, transfer) transfer.usedFor = location self.request.validated['transfer'] = transfer if save_transfer(self.request): self.LOGGER.info('Updated transfer relation {}'.format(transfer.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'transfer_relation_update'})) if save_tender(self.request): self.LOGGER.info('Updated award {} complaint {} ownership of tender {}'.format(complaint.id, award_id, tender.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'award_complaint_ownership_update'}, {'complaint_id': complaint.id, 'award_id': award_id, 'tender_id': tender.id})) return {'data': complaint.serialize('view')}
def check_initial_bids_count(request): tender = request.validated['tender'] if tender.lots: [setattr(i.auctionPeriod, 'startDate', None) for i in tender.lots if i.numberOfBids < MINIMAL_NUMBER_OF_BIDS and i.auctionPeriod and i.auctionPeriod.startDate] for i in tender.lots: # gather all bids by lot id bids = [bid for bid in tender.bids if i.id in [i_lot.relatedLot for i_lot in bid.lotValues if i_lot.status in ["active", "pending"]] and bid.status in ["active", "pending"]] if i.numberOfBids < MINIMAL_NUMBER_OF_BIDS or not validate_unique_bids(bids) and i.status == 'active': setattr(i, 'status', 'unsuccessful') for bid_index, bid in enumerate(tender.bids): for lot_index, lot_value in enumerate(bid.lotValues): if lot_value.relatedLot == i.id: setattr(tender.bids[bid_index].lotValues[lot_index], 'status', 'unsuccessful') if not set([i.status for i in tender.lots]).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 tender.numberOfBids < MINIMAL_NUMBER_OF_BIDS or not validate_unique_bids(tender.bids): LOGGER.info('Switched tender {} to {}'.format(tender.id, 'unsuccessful'), extra=context_unpack(request, {'MESSAGE_ID': 'switched_tender_unsuccessful'})) tender.status = 'unsuccessful'
def check_initial_bids_count(request): tender = request.validated['tender'] if tender.lots: [setattr(i.auctionPeriod, 'startDate', None) for i in tender.lots if i.numberOfBids < 2 and i.auctionPeriod and i.auctionPeriod.startDate] for i in tender.lots: if i.numberOfBids < 2 and i.status == 'active': setattr(i, 'status', 'unsuccessful') for bid_index, bid in enumerate(tender.bids): for lot_index, lot_value in enumerate(bid.lotValues): if lot_value.relatedLot == i.id: setattr(tender.bids[bid_index].lotValues[lot_index], 'status', 'unsuccessful') # [setattr(i, 'status', 'unsuccessful') for i in tender.lots if i.numberOfBids < 2 and i.status == 'active'] if not set([i.status for i in tender.lots]).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 tender.numberOfBids < 2: LOGGER.info('Switched tender {} to {}'.format(tender.id, 'unsuccessful'), extra=context_unpack(request, {'MESSAGE_ID': 'switched_tender_unsuccessful'})) if tender.auctionPeriod and tender.auctionPeriod.startDate: tender.auctionPeriod.startDate = None tender.status = 'unsuccessful'
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 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}) remove_draft_bids(request) 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.auction'), extra=context_unpack(request, {'MESSAGE_ID': 'switched_tender_active.auction'})) tender.status = 'active.auction' check_initial_bids_count(request) return elif not tender.lots and tender.status == 'active.awarded': standStillEnds = [ a.complaintPeriod.endDate.astimezone(TZ) for a in tender.awards if a.complaintPeriod.endDate ] if not standStillEnds: return standStillEnd = max(standStillEnds) if standStillEnd <= now: check_tender_status(request) elif tender.lots and tender.status in ['active.qualification', 'active.awarded']: if any([i['status'] in tender.block_complaint_status and i.relatedLot is None for i in tender.complaints]): return for lot in tender.lots: if lot['status'] != 'active': continue lot_awards = [i for i in tender.awards if i.lotID == lot.id] standStillEnds = [ a.complaintPeriod.endDate.astimezone(TZ) for a in lot_awards if a.complaintPeriod.endDate ] if not standStillEnds: continue standStillEnd = max(standStillEnds) if standStillEnd <= now: check_tender_status(request) return
def check_initial_bids_count(request): tender = request.validated['tender'] if tender.lots: [setattr(i.auctionPeriod, 'startDate', None) for i in tender.lots if i.numberOfBids < 2 and i.auctionPeriod and i.auctionPeriod.startDate] [setattr(i, 'status', 'unsuccessful') for i in tender.lots if i.numberOfBids < 2 and i.status == 'active'] if not set([i.status for i in tender.lots]).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 tender.numberOfBids < 2: LOGGER.info('Switched tender {} to {}'.format(tender.id, 'unsuccessful'), extra=context_unpack(request, {'MESSAGE_ID': 'switched_tender_unsuccessful'})) if tender.auctionPeriod and tender.auctionPeriod.startDate: tender.auctionPeriod.startDate = None tender.status = 'unsuccessful'
def collection_post(self): """Tender Bid Document Upload """ if self.request.validated["tender_status"] not in ["active.tendering", "active.qualification"]: self.request.errors.add( "body", "data", "Can't add document in current ({}) tender status".format(self.request.validated["tender_status"]), ) self.request.errors.status = 403 return if self.request.validated["tender_status"] == "active.qualification" and not [ i for i in self.request.validated["tender"].awards if i.status == "pending" and i.bid_id == self.request.validated["bid_id"] ]: self.request.errors.add("body", "data", "Can't add document because award of bid is not in pending state") self.request.errors.status = 403 return document = upload_file(self.request) self.context.documents.append(document) if save_tender(self.request): LOGGER.info( "Created tender bid document {}".format(document.id), extra=context_unpack( self.request, {"MESSAGE_ID": "tender_bid_document_create"}, {"document_id": document.id} ), ) self.request.response.status = 201 document_route = self.request.matched_route.name.replace("collection_", "") self.request.response.headers["Location"] = self.request.current_route_url( _route_name=document_route, document_id=document.id, _query={} ) return {"data": document.serialize("view")}
def patch(self): """Tender Bid Document Update""" if self.request.validated["tender_status"] not in ["active.tendering", "active.qualification"]: self.request.errors.add( "body", "data", "Can't update document in current ({}) tender status".format(self.request.validated["tender_status"]), ) self.request.errors.status = 403 return if self.request.validated["tender_status"] == "active.qualification" and not [ i for i in self.request.validated["tender"].awards if i.status == "pending" and i.bid_id == self.request.validated["bid_id"] ]: self.request.errors.add( "body", "data", "Can't update document because award of bid is not in pending state" ) self.request.errors.status = 403 return if apply_patch(self.request, src=self.request.context.serialize()): update_file_content_type(self.request) LOGGER.info( "Updated tender bid document {}".format(self.request.context.id), extra=context_unpack(self.request, {"MESSAGE_ID": "tender_bid_document_patch"}), ) return {"data": self.request.context.serialize("view")}
def patch(self): """Update of contract """ if self.request.validated['tender_status'] not in ['active', 'complete']: self.request.errors.add('body', 'data', 'Can\'t update contract in current ({}) tender status'.format(self.request.validated['tender_status'])) self.request.errors.status = 403 return data = self.request.validated['data'] if self.request.context.status == 'cancelled': self.request.errors.add('body', 'data', 'Can\'t update contract in current ({}) status'.format(self.request.context.status)) self.request.errors.status = 403 return if self.request.context.status != 'active' and 'status' in data and data['status'] == 'active': tender = self.request.validated['tender'] award = [a for a in tender.awards if a.id == self.request.context.awardID][0] stand_still_end = award.complaintPeriod.endDate if stand_still_end > get_now(): self.request.errors.add('body', 'data', 'Can\'t sign contract before stand-still period end ({})'.format(stand_still_end.isoformat())) self.request.errors.status = 403 return contract_status = self.request.context.status apply_patch(self.request, save=False, src=self.request.context.serialize()) if contract_status != self.request.context.status and contract_status != 'pending' and self.request.context.status != 'active': self.request.errors.add('body', 'data', 'Can\'t update contract status') self.request.errors.status = 403 return check_tender_status(self.request) if save_tender(self.request): LOGGER.info('Updated tender contract {}'.format(self.request.context.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_contract_patch'})) return {'data': self.request.context.serialize()}
def collection_post(self): """Post a cancellation """ tender = self.request.validated["tender"] if tender.status in ["complete", "cancelled", "unsuccessful"]: self.request.errors.add( "body", "data", "Can't add cancellation in current ({}) tender status".format(tender.status) ) self.request.errors.status = 403 return cancellation = self.request.validated["cancellation"] if cancellation.status == "active": tender.status = "cancelled" tender.cancellations.append(cancellation) if save_tender(self.request): self.LOGGER.info( "Created tender cancellation {}".format(cancellation.id), extra=context_unpack( self.request, {"MESSAGE_ID": "tender_cancellation_create"}, {"cancellation_id": cancellation.id} ), ) self.request.response.status = 201 self.request.response.headers["Location"] = self.request.route_url( "Tender Cancellations", tender_id=tender.id, cancellation_id=cancellation.id ) return {"data": cancellation.serialize("view")}
def collection_post(self): """Post a complaint """ auction = self.context if auction.status not in ['active.enquiries', 'active.tendering']: self.request.errors.add('body', 'data', 'Can\'t add complaint in current ({}) auction status'.format(auction.status)) self.request.errors.status = 403 return complaint = self.request.validated['complaint'] complaint.date = get_now() if complaint.status == 'claim': complaint.dateSubmitted = get_now() else: complaint.status = 'draft' complaint.complaintID = '{}.{}{}'.format(auction.auctionID, self.server_id, sum([len(i.complaints) for i in auction.awards], len(auction.complaints)) + 1) set_ownership(complaint, self.request) auction.complaints.append(complaint) if save_auction(self.request): self.LOGGER.info('Created auction complaint {}'.format(complaint.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'auction_complaint_create'}, {'complaint_id': complaint.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, complaint_id=complaint.id, _query={}) return { 'data': complaint.serialize(auction.status), 'access': { 'token': complaint.owner_token } }
def patch(self): """Post a complaint resolution """ tender = self.request.validated['tender'] if tender.status not in ['active.enquiries', 'active.tendering', 'active.auction', 'active.qualification', 'active.awarded']: self.request.errors.add('body', 'data', 'Can\'t update complaint in current ({}) tender status'.format(tender.status)) self.request.errors.status = 403 return if self.context.status not in ['draft', 'claim', 'answered', 'pending']: self.request.errors.add('body', 'data', 'Can\'t update complaint in current ({}) status'.format(self.context.status)) self.request.errors.status = 403 return data = self.request.validated['data'] # complaint_owner if self.request.authenticated_role == 'complaint_owner' and self.context.status in ['draft', 'claim', 'answered', 'pending'] 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 tender.status in ['active.enquiries', 'active.tendering'] 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 tender.status in ['active.enquiries', 'active.tendering'] and self.context.status == 'draft' and data.get('status', self.context.status) == 'claim': apply_patch(self.request, save=False, src=self.context.serialize()) 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()) elif self.request.authenticated_role == 'complaint_owner' and self.context.status == 'answered' and data.get('satisfied', self.context.satisfied) is True and data.get('status', self.context.status) == 'resolved': apply_patch(self.request, save=False, src=self.context.serialize()) elif self.request.authenticated_role == 'complaint_owner' and self.context.status == 'answered' and data.get('satisfied', self.context.satisfied) is False and data.get('status', self.context.status) == 'pending': apply_patch(self.request, save=False, src=self.context.serialize()) self.context.type = 'complaint' self.context.dateEscalated = get_now() # tender_owner elif self.request.authenticated_role == 'tender_owner' and self.context.status == 'claim' 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: self.request.errors.add('body', 'data', 'Can\'t update complaint: resolution too short') self.request.errors.status = 403 return 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 == 'pending': apply_patch(self.request, save=False, src=self.context.serialize()) # reviewers elif self.request.authenticated_role == 'reviewers' and self.context.status == 'pending' 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 == 'reviewers' and self.context.status == 'pending' and data.get('status', self.context.status) in ['resolved', 'invalid', 'declined']: apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateDecision = get_now() else: self.request.errors.add('body', 'data', 'Can\'t update complaint') self.request.errors.status = 403 return if self.context.tendererAction and not self.context.tendererActionDate: self.context.tendererActionDate = get_now() if self.context.status not in ['draft', 'claim', 'answered', 'pending'] and tender.status in ['active.qualification', 'active.awarded']: check_tender_status(self.request) if save_tender(self.request): self.LOGGER.info('Updated tender complaint {}'.format(self.context.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_complaint_patch'})) return {'data': self.context.serialize("view")}
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): """Auction Bid Document Upload """ if self.request.validated['auction_status'] not in ['active.tendering', 'active.qualification']: self.request.errors.add('body', 'data', 'Can\'t add document in current ({}) auction status'.format(self.request.validated['auction_status'])) self.request.errors.status = 403 return auction = self.request.validated['auction'] if self.request.validated['auction_status'] == 'active.tendering' and (auction.tenderPeriod.startDate and get_now() < auction.tenderPeriod.startDate or get_now() > auction.tenderPeriod.endDate): self.request.errors.add('body', 'data', 'Document can be added only during the tendering period: from ({}) to ({}).'.format(auction.auctionPeriod.startDate and auction.auctionPeriod.startDate.isoformat(), auction.auctionPeriod.endDate.isoformat())) self.request.errors.status = 403 return if self.request.validated['auction_status'] == 'active.qualification' and not [i for i in self.request.validated['auction'].awards if i.status == 'pending' and i.bid_id == self.request.validated['bid_id']]: self.request.errors.add('body', 'data', 'Can\'t add document because award of bid is not in pending state') self.request.errors.status = 403 return document = upload_file(self.request) self.context.documents.append(document) if self.request.validated['auction_status'] == 'active.tendering': self.request.validated['auction'].modified = False if save_auction(self.request): self.LOGGER.info('Created auction bid document {}'.format(document.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'auction_bid_document_create'}, {'document_id': document.id})) self.request.response.status = 201 document_route = self.request.matched_route.name.replace("collection_", "") self.request.response.headers['Location'] = self.request.current_route_url(_route_name=document_route, document_id=document.id, _query={}) return {'data': document.serialize("view")}
def delete(self): """Cancelling the proposal Example request for cancelling the proposal: .. sourcecode:: http DELETE /tenders/4879d3f8ee2443169b5fbbc9f89fa607/bids/71b6c23ed8944d688e92a31ec8c3f61a HTTP/1.1 Host: example.com Accept: application/json """ bid = self.request.context if self.request.validated['tender_status'] != 'active.tendering': self.request.errors.add('body', 'data', 'Can\'t delete bid in current ({}) tender status'.format(self.request.validated['tender_status'])) self.request.errors.status = 403 return tender = self.request.validated['tender'] if tender.tenderPeriod.startDate and get_now() < tender.tenderPeriod.startDate or get_now() > tender.tenderPeriod.endDate: self.request.errors.add('body', 'data', 'Bid can be deleted only during the tendering period: from ({}) to ({}).'.format(tender.tenderPeriod.startDate and tender.tenderPeriod.startDate.isoformat(), tender.tenderPeriod.endDate.isoformat())) self.request.errors.status = 403 return bid.status = 'deleted' if tender.lots: bid.lotValues = [] self.request.validated['tender'].modified = False if save_tender(self.request): res = bid.serialize("view") self.LOGGER.info('Deleted tender bid {}'.format(self.request.context.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_bid_delete'})) return {'data': res}
def collection_post(self): """Post a cancellation """ tender = self.request.validated['tender'] if tender.status in ['complete', 'cancelled', 'unsuccessful']: self.request.errors.add('body', 'data', 'Can\'t add cancellation in current ({}) tender status'.format(tender.status)) self.request.errors.status = 403 return cancellation_data = self.request.validated['data'] if any([i.status != 'active' for i in tender.lots if i.id == cancellation_data.get('relatedLot')]): self.request.errors.add('body', 'data', 'Can add cancellation only in active lot status') self.request.errors.status = 403 return cancellation = Cancellation(cancellation_data) cancellation.__parent__ = self.request.context if cancellation.relatedLot and cancellation.status == 'active': [setattr(i, 'status', 'cancelled') for i in tender.lots if i.id == cancellation.relatedLot] check_tender_status(self.request) elif cancellation.status == 'active': tender.status = 'cancelled' tender.cancellations.append(cancellation) if save_tender(self.request): LOGGER.info('Created tender cancellation {}'.format(cancellation.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_cancellation_create'}, {'cancellation_id': cancellation.id})) self.request.response.status = 201 self.request.response.headers['Location'] = self.request.route_url('Tender Cancellations', tender_id=tender.id, cancellation_id=cancellation.id) return {'data': cancellation.serialize("view")}
def collection_post(self): """Tender Award Document Upload """ if self.request.validated["tender_status"] != "active": self.request.errors.add( "body", "data", "Can't add document in current ({}) tender status".format(self.request.validated["tender_status"]), ) self.request.errors.status = 403 return document = upload_file(self.request) self.request.validated["award"].documents.append(document) if save_tender(self.request): LOGGER.info( "Created tender award document {}".format(document.id), extra=context_unpack( self.request, {"MESSAGE_ID": "tender_award_document_create"}, {"document_id": document.id} ), ) self.request.response.status = 201 document_route = self.request.matched_route.name.replace("collection_", "") self.request.response.headers["Location"] = self.request.current_route_url( _route_name=document_route, document_id=document.id, _query={} ) return {"data": document.serialize("view")}
def patch(self): """Update of proposal Example request to change bid proposal: .. sourcecode:: http PATCH /tenders/4879d3f8ee2443169b5fbbc9f89fa607/bids/71b6c23ed8944d688e92a31ec8c3f61a HTTP/1.1 Host: example.com Accept: application/json { "data": { "value": { "amount": 600 } } } And here is the response to be expected: .. sourcecode:: http HTTP/1.0 200 OK Content-Type: application/json { "data": { "value": { "amount": 600, "currency": "UAH", "valueAddedTaxIncluded": true } } } """ if self.request.validated['tender_status'] != 'active.tendering': self.request.errors.add('body', 'data', 'Can\'t update bid in current ({}) tender status'.format(self.request.validated['tender_status'])) self.request.errors.status = 403 return if self.request.authenticated_role != 'Administrator': bid_status_to = self.request.validated['data'].get("status", self.request.context.status) if bid_status_to != 'pending': self.request.errors.add('body', 'bid', 'Can\'t update bid to ({}) status'.format(bid_status_to)) self.request.errors.status = 403 return value = self.request.validated['data'].get("value") and self.request.validated['data']["value"].get("amount") if value and value != self.request.context.get("value", {}).get("amount"): self.request.validated['data']['date'] = get_now().isoformat() if self.request.context.lotValues: lotValues = dict([(i.relatedLot, i.value.amount) for i in self.request.context.lotValues]) for lotvalue in self.request.validated['data'].get("lotValues", []): if lotvalue['relatedLot'] in lotValues and lotvalue.get("value", {}).get("amount") != lotValues[lotvalue['relatedLot']]: lotvalue['date'] = get_now().isoformat() self.request.validated['tender'].modified = False if apply_patch(self.request, src=self.request.context.serialize()): self.LOGGER.info('Updated tender bid {}'.format(self.request.context.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_bid_patch'})) return {'data': self.request.context.serialize("view")}
def collection_post(self): """Post a complaint """ tender = self.context if tender.status not in ['active.enquiries', 'active.tendering']: self.request.errors.add('body', 'data', 'Can\'t add complaint in current ({}) tender status'.format(tender.status)) self.request.errors.status = 403 return complaint = self.request.validated['complaint'] if complaint.status == 'claim': complaint.dateSubmitted = get_now() else: complaint.status = 'draft' complaint.complaintID = '{}.{}{}'.format(tender.tenderID, self.server_id, sum([len(i.complaints) for i in tender.awards], len(tender.complaints)) + 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', tender_id=tender.id, complaint_id=complaint.id) return { 'data': complaint.serialize(tender.status), 'access': { 'token': complaint.owner_token } }
def patch(self): """Tender Bid Document Update""" if self.request.validated['tender_status'] not in ['active.tendering', 'active.qualification']: self.request.errors.add('body', 'data', 'Can\'t update document in current ({}) tender status'.format(self.request.validated['tender_status'])) self.request.errors.status = 403 return if self.request.validated['tender_status'] == 'active.qualification' and not [i for i in self.request.validated['tender'].awards if i.status == 'pending' and i.bid_id == self.request.validated['bid_id']]: self.request.errors.add('body', 'data', 'Can\'t update document because award of bid is not in pending state') self.request.errors.status = 403 return if self.request.validated['tender_status'] != 'active.tendering' and 'confidentiality' in self.request.validated['data']: if self.context.confidentiality != self.request.validated['data']['confidentiality']: self.request.errors.add('body', 'data', 'Can\'t update document confidentiality in current ({}) tender status'.format(self.request.validated['tender_status'])) self.request.errors.status = 403 return bid = getattr(self.context, "__parent__") if bid and bid.status in ['invalid', 'unsuccessful', 'deleted']: self.request.errors.add('body', 'data', 'Can\'t update document data for \'{}\' bid'.format(bid.status)) self.request.errors.status = 403 return if self.request.validated['tender_status'] == 'active.tendering': self.request.validated['tender'].modified = False if apply_patch(self.request, src=self.request.context.serialize()): update_file_content_type(self.request) self.LOGGER.info('Updated tender bid document {}'.format(self.request.context.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_bid_document_patch'})) return {'data': self.request.context.serialize("view")}
def collection_post(self): """Tender Contract Document Upload """ if self.request.validated['tender_status'] not in ['active.qualification', 'active.awarded']: self.request.errors.add('body', 'data', 'Can\'t add document in current ({}) tender status'.format(self.request.validated['tender_status'])) self.request.errors.status = 403 return tender = self.request.validated['tender'] contract = self.request.validated['contract'] if any([i.status != 'active' for i in tender.lots if i.id in [a.lotID for a in tender.awards if a.id == contract.awardID]]): self.request.errors.add('body', 'data', 'Can add document only in active lot status') self.request.errors.status = 403 return if contract.status not in ['pending', 'active']: self.request.errors.add('body', 'data', 'Can\'t add document in current contract status') self.request.errors.status = 403 return document = upload_file(self.request) self.context.documents.append(document) if save_tender(self.request): self.LOGGER.info('Created tender contract document {}'.format(document.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_contract_document_create'}, {'document_id': document.id})) self.request.response.status = 201 document_route = self.request.matched_route.name.replace("collection_", "") self.request.response.headers['Location'] = self.request.current_route_url(_route_name=document_route, document_id=document.id, _query={}) return {'data': document.serialize("view")}
def collection_post(self): """Post a complaint for award """ tender = self.request.validated['tender'] if tender.status != 'active': self.request.errors.add('body', 'data', 'Can\'t add complaint in current ({}) tender status'.format(tender.status)) self.request.errors.status = 403 return if self.context.complaintPeriod and \ (self.context.complaintPeriod.startDate and self.context.complaintPeriod.startDate > get_now() or self.context.complaintPeriod.endDate and self.context.complaintPeriod.endDate < get_now()): self.request.errors.add('body', 'data', 'Can add complaint only in complaintPeriod') self.request.errors.status = 403 return complaint = self.request.validated['complaint'] complaint.type = 'complaint' if complaint.status == 'pending': complaint.dateSubmitted = get_now() else: complaint.status = 'draft' complaint.complaintID = '{}.{}{}'.format(tender.tenderID, self.server_id, sum([len(i.complaints) for i in tender.awards], 1)) set_ownership(complaint, self.request) self.context.complaints.append(complaint) if save_tender(self.request): self.LOGGER.info('Created tender award complaint {}'.format(complaint.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_award_complaint_create'}, {'complaint_id': complaint.id})) self.request.response.status = 201 self.request.response.headers['Location'] = self.request.route_url('Tender negotiation Award Complaints', tender_id=tender.id, award_id=self.request.validated['award_id'], complaint_id=complaint['id']) return { 'data': complaint.serialize("view"), 'access': { 'token': complaint.owner_token } }
def patch(self): """Post a complaint resolution """ tender = self.request.validated['tender'] if tender.status not in ['active.enquiries', 'active.tendering', 'active.auction', 'active.qualification', 'active.awarded']: self.request.errors.add('body', 'data', 'Can\'t update complaint in current ({}) tender status'.format(tender.status)) self.request.errors.status = 403 return if self.request.context.status != 'pending': self.request.errors.add('body', 'data', 'Can\'t update complaint in current ({}) status'.format(self.request.context.status)) self.request.errors.status = 403 return if self.request.validated['data'].get('status', self.request.context.status) == 'cancelled': self.request.errors.add('body', 'data', 'Can\'t cancel complaint') self.request.errors.status = 403 return apply_patch(self.request, save=False, src=self.request.context.serialize()) if self.request.context.status == 'resolved' and tender.status != 'active.enquiries': for i in tender.complaints: if i.status == 'pending': i.status = 'cancelled' [setattr(i, 'status', 'cancelled') for i in tender.lots] tender.status = 'cancelled' elif self.request.context.status in ['declined', 'invalid'] and tender.status == 'active.awarded': check_tender_status(self.request) if save_tender(self.request): LOGGER.info('Updated tender complaint {}'.format(self.request.context.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_complaint_patch'})) return {'data': self.request.context.serialize("view")}
def put(self): """Auction Award Complaint Document Update""" if self.request.authenticated_role != self.context.author: self.request.errors.add('url', 'role', 'Can update document only author') self.request.errors.status = 403 return if self.request.validated['auction_status'] not in ['active.qualification', 'active.awarded']: self.request.errors.add('body', 'data', 'Can\'t update document in current ({}) auction status'.format(self.request.validated['auction_status'])) self.request.errors.status = 403 return if any([i.status != 'active' for i in self.request.validated['auction'].lots if i.id == self.request.validated['award'].lotID]): self.request.errors.add('body', 'data', 'Can update document only in active lot status') self.request.errors.status = 403 return if self.request.validated['complaint'].status not in STATUS4ROLE.get(self.request.authenticated_role, []): self.request.errors.add('body', 'data', 'Can\'t update document in current ({}) complaint status'.format(self.request.validated['complaint'].status)) self.request.errors.status = 403 return document = upload_file(self.request) document.author = self.request.authenticated_role self.request.validated['complaint'].documents.append(document) if save_auction(self.request): self.LOGGER.info('Updated auction award complaint document {}'.format(self.request.context.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'auction_award_complaint_document_put'})) return {'data': document.serialize("view")}
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 patch(self): """Contract Document Update""" if apply_patch(self.request, src=self.request.context.serialize()): update_file_content_type(self.request) self.LOGGER.info('Updated contract document {}'.format(self.request.context.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'contract_document_patch'})) return {'data': self.request.context.serialize("view")}
def patch(self): """Post a complaint resolution for award """ tender = self.request.validated['tender'] if tender.status != 'active': self.request.errors.add('body', 'data', 'Can\'t update complaint in current ({}) tender status'.format(tender.status)) self.request.errors.status = 403 return if self.context.status not in ['draft', 'claim', 'answered', 'pending', 'accepted', 'satisfied']: self.request.errors.add('body', 'data', 'Can\'t update complaint in current ({}) status'.format(self.context.status)) self.request.errors.status = 403 return 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', 'pending', 'accepted'] 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 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) == 'pending': apply_patch(self.request, save=False, src=self.context.serialize()) self.context.type = 'complaint' self.context.dateSubmitted = get_now() # 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 == '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 == '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 == 'pending' 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 == 'pending' and data.get('status', self.context.status) == 'invalid': 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 == 'accepted' 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 == 'accepted' 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() else: self.request.errors.add('body', 'data', 'Can\'t update complaint') self.request.errors.status = 403 return 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'] 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 put(self): """Tender Bid Document Update""" if self.request.validated['tender_status'] not in ['active.tendering', 'active.qualification']: self.request.errors.add('body', 'data', 'Can\'t update document in current ({}) tender status'.format(self.request.validated['tender_status'])) self.request.errors.status = 403 return tender = self.request.validated['tender'] if self.request.validated['tender_status'] == 'active.tendering' and (tender.tenderPeriod.startDate and get_now() < tender.tenderPeriod.startDate or get_now() > tender.tenderPeriod.endDate): self.request.errors.add('body', 'data', 'Document can be updated only during the tendering period: from ({}) to ({}).'.format(tender.tenderPeriod.startDate and tender.tenderPeriod.startDate.isoformat(), tender.tenderPeriod.endDate.isoformat())) self.request.errors.status = 403 return if self.request.validated['tender_status'] == 'active.qualification' and not [i for i in self.request.validated['tender'].awards if i.status == 'pending' and i.bid_id == self.request.validated['bid_id']]: self.request.errors.add('body', 'data', 'Can\'t update document because award of bid is not in pending state') self.request.errors.status = 403 return bid = getattr(self.context, "__parent__") if bid and bid.status in ['invalid', 'unsuccessful', 'deleted']: self.request.errors.add('body', 'data', 'Can\'t update document in \'{}\' bid'.format(bid.status)) self.request.errors.status = 403 return document = upload_file(self.request) getattr(self.request.validated['bid'], self.container).append(document) if self.request.validated['tender_status'] == 'active.tendering': self.request.validated['tender'].modified = False if save_tender(self.request): self.LOGGER.info('Updated tender bid document {}'.format(self.request.context.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_bid_document_put'})) return {'data': document.serialize("view")}
def collection_post(self): """Tender Bid Document Upload """ if self.request.validated['tender_status'] not in ['active.tendering', 'active.qualification']: self.request.errors.add('body', 'data', 'Can\'t add document in current ({}) tender status'.format(self.request.validated['tender_status'])) self.request.errors.status = 403 return if self.request.validated['tender_status'] == 'active.qualification' and not [i for i in self.request.validated['tender'].awards if i.status == 'pending' and i.bid_id == self.request.validated['bid_id']]: self.request.errors.add('body', 'data', 'Can\'t add document because award of bid is not in pending state') self.request.errors.status = 403 return if self.context.status in ['invalid', 'unsuccessful', 'deleted']: self.request.errors.add('body', 'data', 'Can\'t add document to \'{}\' bid'.format(self.context.status)) self.request.errors.status = 403 return document = upload_file(self.request) getattr(self.context, self.container).append(document) if self.request.validated['tender_status'] == 'active.tendering': self.request.validated['tender'].modified = False if save_tender(self.request): self.LOGGER.info('Created tender bid document {}'.format(document.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_bid_document_create'}, {'document_id': document.id})) self.request.response.status = 201 document_route = self.request.matched_route.name.replace("collection_", "") self.request.response.headers['Location'] = self.request.current_route_url(_route_name=document_route, document_id=document.id, _query={}) return {'data': document.serialize("view")}
def collection_post(self): """Auction Award Complaint Document Upload """ if self.request.validated['auction_status'] not in ['active.qualification', 'active.awarded']: self.request.errors.add('body', 'data', 'Can\'t add document in current ({}) auction status'.format(self.request.validated['auction_status'])) self.request.errors.status = 403 return if any([i.status != 'active' for i in self.request.validated['auction'].lots if i.id == self.request.validated['award'].lotID]): self.request.errors.add('body', 'data', 'Can add document only in active lot status') self.request.errors.status = 403 return if self.context.status not in STATUS4ROLE.get(self.request.authenticated_role, []): self.request.errors.add('body', 'data', 'Can\'t add document in current ({}) complaint status'.format(self.context.status)) self.request.errors.status = 403 return document = upload_file(self.request) document.author = self.request.authenticated_role self.context.documents.append(document) if save_auction(self.request): self.LOGGER.info('Created auction award complaint document {}'.format(document.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'auction_award_complaint_document_create'}, {'document_id': document.id})) self.request.response.status = 201 document_route = self.request.matched_route.name.replace("collection_", "") self.request.response.headers['Location'] = self.request.current_route_url(_route_name=document_route, document_id=document.id, _query={}) return {'data': document.serialize("view")}
def patch(self): """Patch the complaint """ tender = self.request.validated['tender'] data = self.request.validated['data'] is_qualificationPeriod = tender.qualificationPeriod.startDate < get_now( ) and (not tender.qualificationPeriod.endDate or tender.qualificationPeriod.endDate > 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_qualificationPeriod 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_qualificationPeriod and self.context.status == 'draft' and data.get( 'status', self.context.status) == 'claim': if self.request.validated[ 'qualification'].status == 'unsuccessful' and self.request.validated[ 'qualification'].bidID != self.context.bid_id: raise_operation_error( self.request, 'Can add claim only on unsuccessful qualification 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_qualificationPeriod and self.context.status == 'draft' and data.get( 'status', self.context.status) == 'pending': 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) == 'declined': apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateDecision = get_now() elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status == 'accepted' and data.get( 'status', self.context.status) == 'satisfied': apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateDecision = get_now() tender.status = 'active.pre-qualification' if tender.qualificationPeriod.endDate: tender.qualificationPeriod.endDate = None elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status in [ '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 save_tender(self.request): self.LOGGER.info( 'Updated tender qualification complaint {}'.format( self.context.id), extra=context_unpack( self.request, {'MESSAGE_ID': 'tender_qualification_complaint_patch'})) return {'data': self.context.serialize("view")}
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"] == "active.tendering"): if "tenderPeriod" in data and "endDate" in data["tenderPeriod"]: self.request.validated["tender"].tenderPeriod.import_data( data["tenderPeriod"]) validate_tender_period_extension_with_working_days( 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": # invalidate bids on tender change tender.invalidate_bids_data() 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 collection_post(self): """Tender Bid Document Upload """ if self.request.validated['tender_status'] not in [ 'active.tendering', 'active.qualification' ]: self.request.errors.add( 'body', 'data', 'Can\'t add document in current ({}) tender status'.format( self.request.validated['tender_status'])) self.request.errors.status = 403 return tender = self.request.validated['tender'] if self.request.validated['tender_status'] == 'active.tendering' and ( tender.tenderPeriod.startDate and get_now() < tender.tenderPeriod.startDate or get_now() > tender.tenderPeriod.endDate): self.request.errors.add( 'body', 'data', 'Document can be added only during the tendering period: from ({}) to ({}).' .format( tender.tenderPeriod.startDate and tender.tenderPeriod.startDate.isoformat(), tender.tenderPeriod.endDate.isoformat())) self.request.errors.status = 403 return if self.request.validated[ 'tender_status'] == 'active.qualification' and not [ i for i in self.request.validated['tender'].awards if i.status == 'pending' and i.bid_id == self.request.validated['bid_id'] ]: self.request.errors.add( 'body', 'data', 'Can\'t add document because award of bid is not in pending state' ) self.request.errors.status = 403 return if self.context.status in ['invalid', 'unsuccessful', 'deleted']: self.request.errors.add( 'body', 'data', 'Can\'t add document to \'{}\' bid'.format( self.context.status)) self.request.errors.status = 403 return document = upload_file(self.request) getattr(self.context, self.container).append(document) if self.request.validated['tender_status'] == 'active.tendering': self.request.validated['tender'].modified = False if save_tender(self.request): self.LOGGER.info( 'Created tender bid document {}'.format(document.id), extra=context_unpack( self.request, {'MESSAGE_ID': 'tender_bid_document_create'}, {'document_id': document.id})) self.request.response.status = 201 document_route = self.request.matched_route.name.replace( "collection_", "") self.request.response.headers[ 'Location'] = self.request.current_route_url( _route_name=document_route, document_id=document.id, _query={}) return {'data': document.serialize("view")}
def collection_post(self): """Post a complaint """ tender = self.request.validated['tender'] if tender.status not in ['active.pre-qualification.stand-still']: self.request.errors.add( 'body', 'data', 'Can\'t add complaint in current ({}) tender status'.format( tender.status)) self.request.errors.status = 403 return if any([ i.status != 'active' for i in tender.lots if i.id == self.context.lotID ]): self.request.errors.add( 'body', 'data', 'Can add complaint only in active lot status') self.request.errors.status = 403 return if tender.qualificationPeriod and \ (tender.qualificationPeriod.startDate and tender.qualificationPeriod.startDate > get_now() or tender.qualificationPeriod.endDate and tender.qualificationPeriod.endDate < get_now()): self.request.errors.add( 'body', 'data', 'Can add complaint only in qualificationPeriod') self.request.errors.status = 403 return complaint = self.request.validated['complaint'] complaint.relatedLot = self.context.lotID complaint.date = get_now() if complaint.status == 'claim': complaint.dateSubmitted = get_now() elif complaint.status == 'pending': complaint.type = 'complaint' complaint.dateSubmitted = get_now() else: complaint.status = 'draft' complaint.complaintID = '{}.{}{}'.format( tender.tenderID, self.server_id, self.complaints_len(tender) + 1) set_ownership(complaint, self.request) self.context.complaints.append(complaint) if save_tender(self.request): self.LOGGER.info( 'Created tender qualification complaint {}'.format( complaint.id), extra=context_unpack( self.request, {'MESSAGE_ID': 'tender_qualification_complaint_create'}, {'complaint_id': complaint.id})) self.request.response.status = 201 self.request.response.headers['Location'] = self.request.route_url( 'Tender EU Qualification Complaints', tender_id=tender.id, qualification_id=self.request.validated['qualification_id'], complaint_id=complaint['id']) return { 'data': complaint.serialize("view"), 'access': { 'token': complaint.owner_token } }
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 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 if self.request.authenticated_role != 'Administrator' and tender.status in [ 'complete', 'unsuccessful', 'cancelled' ]: self.request.errors.add( 'body', 'data', 'Can\'t update tender in current ({}) status'.format( tender.status)) self.request.errors.status = 403 return data = self.request.validated['data'] if self.request.authenticated_role == 'tender_owner' and 'status' in data and data[ 'status'] not in [ 'active.pre-qualification.stand-still', tender.status ]: self.request.errors.add('body', 'data', 'Can\'t update tender status') self.request.errors.status = 403 return 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']) if calculate_business_date( get_now(), TENDERING_EXTRA_PERIOD ) > self.request.validated['tender'].tenderPeriod.endDate: self.request.errors.add( 'body', 'data', 'tenderPeriod should be extended by {0.days} days'. format(TENDERING_EXTRA_PERIOD)) self.request.errors.status = 403 return self.request.validated['tender'].initialize() 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_business_date( get_now(), COMPLAINT_STAND_STILL) else: self.request.errors.add( 'body', 'data', 'Can\'t switch to \'active.pre-qualification.stand-still\' while not all bids are qualified' .format(TENDERING_EXTRA_PERIOD)) self.request.errors.status = 403 return 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 check_status(request): tender = request.validated["tender"] now = get_now() for complaint in tender.complaints: check_complaint_status(request, complaint, now) for award in tender.awards: if award.status == "active" and not any([i.awardID == award.id for i in tender.contracts]): add_contract(request, award, now) add_next_award(request) for complaint in award.complaints: check_complaint_status(request, complaint, now) if ( tender.status == "active.enquiries" and not tender.tenderPeriod.startDate and tender.enquiryPeriod.endDate.astimezone(TZ) <= now ): LOGGER.info( "Switched tender {} to {}".format(tender.id, "active.tendering"), extra=context_unpack(request, {"MESSAGE_ID": "switched_tender_active.tendering"}), ) tender.status = "active.tendering" return elif ( tender.status == "active.enquiries" and tender.tenderPeriod.startDate and tender.tenderPeriod.startDate.astimezone(TZ) <= now ): LOGGER.info( "Switched tender {} to {}".format(tender.id, "active.tendering"), extra=context_unpack(request, {"MESSAGE_ID": "switched_tender_active.tendering"}), ) tender.status = "active.tendering" return elif not tender.lots and tender.status == "active.tendering" and tender.tenderPeriod.endDate <= now: LOGGER.info( "Switched tender {} to {}".format(tender["id"], "active.auction"), extra=context_unpack(request, {"MESSAGE_ID": "switched_tender_active.auction"}), ) tender.status = "active.auction" remove_draft_bids(request) check_bids(request) if tender.numberOfBids < 2 and tender.auctionPeriod: tender.auctionPeriod.startDate = None return elif ( tender.lots and tender.status == "active.tendering" and tender.tenderPeriod.endDate <= now ): LOGGER.info( "Switched tender {} to {}".format(tender["id"], "active.auction"), extra=context_unpack(request, {"MESSAGE_ID": "switched_tender_active.auction"}), ) tender.status = "active.auction" remove_draft_bids(request) check_bids(request) [setattr(i.auctionPeriod, "startDate", None) for i in tender.lots if i.numberOfBids < 2 and i.auctionPeriod] return elif not tender.lots and tender.status == "active.awarded": standStillEnds = [a.complaintPeriod.endDate.astimezone(TZ) for a in tender.awards if a.complaintPeriod.endDate] if not standStillEnds: return standStillEnd = max(standStillEnds) if standStillEnd <= now: check_tender_status(request) elif tender.lots and tender.status in ["active.qualification", "active.awarded"]: if any([i["status"] in tender.block_complaint_status and i.relatedLot is None for i in tender.complaints]): return for lot in tender.lots: if lot["status"] != "active": continue lot_awards = [i for i in tender.awards if i.lotID == lot.id] standStillEnds = [a.complaintPeriod.endDate.astimezone(TZ) for a in lot_awards if a.complaintPeriod.endDate] if not standStillEnds: continue standStillEnd = max(standStillEnds) if standStillEnd <= now: check_tender_status(request) return
def patch(self): """Auction Edit (partial) For example here is how procuring entity can change number of items to be procured and total Value of a auction: .. sourcecode:: http PATCH /auctions/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", "auctionID": "UA-64e93250be76435397e8c992ed4214d1", "dateModified": "2014-10-27T08:12:34.956Z", "value": { "amount": 600 }, "itemsToBeProcured": [ { "quantity": 6 } ] } } """ auction = self.context if self.request.authenticated_role != 'Administrator' and auction.status in [ 'complete', 'unsuccessful', 'cancelled' ]: self.request.errors.add( 'body', 'data', 'Can\'t update auction in current ({}) status'.format( auction.status)) self.request.errors.status = 403 return if self.request.authenticated_role == 'chronograph': apply_patch(self.request, save=False, src=self.request.validated['auction_src']) check_status(self.request) save_auction(self.request) else: apply_patch(self.request, src=self.request.validated['auction_src']) self.LOGGER.info('Updated auction {}'.format(auction.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'auction_patch'})) return {'data': auction.serialize(auction.status)}
return dict([(i, j) for i, j in asset.serialize(asset.status).items() if i in fields]) def save_asset(request): asset = request.validated['asset'] if asset.mode == u'test': set_modetest_titles(asset) patch = get_revision_changes(asset.serialize("plain"), request.validated['asset_src']) if patch: revision = prepare_revision(asset, patch, request.authenticated_userid) asset.revisions.append(type(asset).revisions.model_class(revision)) old_dateModified = asset.dateModified if getattr(asset, 'modified', True): asset.dateModified = get_now() try: asset.store(request.registry.db) except ModelValidationError, e: for i in e.message: request.errors.add('body', i, e.message[i]) request.errors.status = 422 except ResourceConflict, e: # pragma: no cover request.errors.add('body', 'data', str(e)) request.errors.status = 409 except Exception, e: # pragma: no cover request.errors.add('body', 'data', str(e)) else: LOGGER.info('Saved asset {}: dateModified {} -> {}'.format(asset.id, old_dateModified and old_dateModified.isoformat(), asset.dateModified.isoformat()), extra=context_unpack(request, {'MESSAGE_ID': 'save_asset'}, {'RESULT': asset.rev})) return True
def check_status(request): auction = request.validated['auction'] now = get_now() for complaint in auction.complaints: check_complaint_status(request, complaint, now) for award in auction.awards: for complaint in award.complaints: check_complaint_status(request, complaint, now) if not auction.lots and auction.status == 'active.tendering' and auction.tenderPeriod.endDate <= now: LOGGER.info('Switched auction {} to {}'.format(auction['id'], 'active.auction'), extra=context_unpack( request, {'MESSAGE_ID': 'switched_auction_active.auction'})) auction.status = 'active.auction' remove_draft_bids(request) remove_invalid_bids(request) check_bids(request) return elif auction.lots and auction.status == 'active.tendering' and auction.tenderPeriod.endDate <= now: LOGGER.info('Switched auction {} to {}'.format(auction['id'], 'active.auction'), extra=context_unpack( request, {'MESSAGE_ID': 'switched_auction_active.auction'})) auction.status = 'active.auction' remove_draft_bids(request) remove_invalid_bids(request) check_bids(request) [ setattr(i.auctionPeriod, 'startDate', None) for i in auction.lots if i.numberOfBids < 2 and i.auctionPeriod ] return elif not auction.lots and auction.status == 'active.awarded': standStillEnds = [ a.complaintPeriod.endDate.astimezone(TZ) for a in auction.awards if a.complaintPeriod.endDate ] if not standStillEnds: return standStillEnd = max(standStillEnds) if standStillEnd <= now: check_auction_status(request) elif auction.lots and auction.status in [ 'active.qualification', 'active.awarded' ]: if any([ i['status'] in auction.block_complaint_status and i.relatedLot is None for i in auction.complaints ]): return for lot in auction.lots: if lot['status'] != 'active': continue lot_awards = [i for i in auction.awards if i.lotID == lot.id] standStillEnds = [ a.complaintPeriod.endDate.astimezone(TZ) for a in lot_awards if a.complaintPeriod.endDate ] if not standStillEnds: continue standStillEnd = max(standStillEnds) if standStillEnd <= now: check_auction_status(request) return
def check_status(request): tender = request.validated['tender'] now = get_now() active_lots = [lot.id for lot in tender.lots if lot.status == 'active'] if tender.lots else [None] configurator = request.content_configurator for award in tender.awards: if award.status == 'active' and not any( [i.awardID == award.id for i in tender.contracts]): tender.contracts.append( type(tender).contracts.model_class({ 'awardID': award.id, 'suppliers': award.suppliers, 'value': award.value, 'date': now, 'items': [i for i in tender.items if i.relatedLot == award.lotID], 'contractID': '{}-{}{}'.format(tender.tenderID, request.registry.server_id, len(tender.contracts) + 1) })) add_next_award( request, reverse=configurator.reverse_awarding_criteria, awarding_criteria_key=configurator.awarding_criteria_key) 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}) remove_draft_bids(request) 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 if q.lotID in active_lots ]): LOGGER.info('Switched tender {} to {}'.format(tender['id'], 'active.auction'), extra=context_unpack( request, {'MESSAGE_ID': 'switched_tender_active.auction'})) tender.status = 'active.auction' check_initial_bids_count(request) return elif not tender.lots and tender.status == 'active.awarded': standStillEnds = [ a.complaintPeriod.endDate.astimezone(TZ) for a in tender.awards if a.complaintPeriod.endDate ] if not standStillEnds: return standStillEnd = max(standStillEnds) if standStillEnd <= now: check_tender_status(request) elif tender.lots and tender.status in [ 'active.qualification', 'active.awarded' ]: if any([ i['status'] in tender.block_complaint_status and i.relatedLot is None for i in tender.complaints ]): return for lot in tender.lots: if lot['status'] != 'active': continue lot_awards = [i for i in tender.awards if i.lotID == lot.id] standStillEnds = [ a.complaintPeriod.endDate.astimezone(TZ) for a in lot_awards if a.complaintPeriod.endDate ] if not standStillEnds: continue standStillEnd = max(standStillEnds) if standStillEnd <= now: check_tender_status(request) return
def patch(self): """Post a complaint resolution """ tender = self.request.validated["tender"] data = self.request.validated["data"] # 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 tender.status == "active.tendering" 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 tender.status == "active.tendering" and self.context.status == "draft" and data.get("status", self.context.status) == "claim" ): validate_submit_claim_time(self.request) apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateSubmitted = get_now() elif ( self.request.authenticated_role == "complaint_owner" and tender.status == "active.tendering" and self.context.status in ["draft", "claim"] and data.get("status", self.context.status) == "pending" ): validate_submit_complaint_time(self.request) 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()) elif ( self.request.authenticated_role == "complaint_owner" and self.context.status == "answered" and data.get("satisfied", self.context.satisfied) is True and data.get("status", self.context.status) == "resolved" ): apply_patch(self.request, save=False, src=self.context.serialize()) elif ( self.request.authenticated_role == "complaint_owner" and self.context.status == "answered" and data.get("satisfied", self.context.satisfied) is False and data.get("status", self.context.status) == "pending" ): validate_submit_complaint_time(self.request) apply_patch(self.request, save=False, src=self.context.serialize()) self.context.type = "complaint" self.context.dateEscalated = get_now() # tender_owner elif ( self.request.authenticated_role == "tender_owner" and self.context.status == "claim" and data.get("status", self.context.status) == self.context.status ): validate_update_claim_time(self.request) apply_patch(self.request, save=False, src=self.context.serialize()) elif ( self.request.authenticated_role == "tender_owner" and self.context.status == "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" ): validate_update_claim_time(self.request) 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 in ["pending", "accepted"]: apply_patch(self.request, save=False, src=self.context.serialize()) 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 complaint {}".format(self.context.id), extra=context_unpack(self.request, {"MESSAGE_ID": "tender_complaint_patch"}), ) return {"data": self.context.serialize("view")}
def check_status(request): tender = request.validated['tender'] now = get_now() if tender.status == 'active.tendering' and tender.tenderPeriod.endDate <= now and \ not any([i.status in BLOCK_COMPLAINT_STATUS for i in tender.complaints]) and \ not any([i.id for i in tender.questions if not i.answer]): 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 BLOCK_COMPLAINT_STATUS for q in tender.qualifications for i in q.complaints ]): LOGGER.info('Switched tender {} to {}'.format(tender['id'], 'active.auction'), extra=context_unpack( request, {'MESSAGE_ID': 'switched_tender_active.auction'})) tender.status = 'active.auction' check_initial_bids_count(request) return elif not tender.lots and tender.status == 'active.awarded': standStillEnds = [ a.complaintPeriod.endDate.astimezone(TZ) for a in tender.awards if a.complaintPeriod.endDate ] if not standStillEnds: return standStillEnd = max(standStillEnds) if standStillEnd <= now: pending_complaints = any([ i['status'] in ['claim', 'answered', 'pending'] for i in tender.complaints ]) pending_awards_complaints = any([ i['status'] in ['claim', 'answered', 'pending'] for a in tender.awards for i in a.complaints ]) awarded = any([i['status'] == 'active' for i in tender.awards]) if not pending_complaints and not pending_awards_complaints and not awarded: LOGGER.info( 'Switched tender {} to {}'.format(tender.id, 'unsuccessful'), extra=context_unpack( request, {'MESSAGE_ID': 'switched_tender_unsuccessful'})) check_tender_status(request) return elif tender.lots and tender.status in [ 'active.qualification', 'active.awarded' ]: if any([ i['status'] in ['claim', 'answered', 'pending'] for i in tender.complaints ]): return lots_ends = [] for lot in tender.lots: if lot['status'] != 'active': continue lot_awards = [i for i in tender.awards if i.lotID == lot.id] standStillEnds = [ a.complaintPeriod.endDate.astimezone(TZ) for a in lot_awards if a.complaintPeriod.endDate ] if not standStillEnds: continue standStillEnd = max(standStillEnds) if standStillEnd <= now: pending_awards_complaints = any([ i['status'] in ['claim', 'answered', 'pending'] for a in lot_awards for i in a.complaints ]) awarded = any([i['status'] == 'active' for i in lot_awards]) if not pending_awards_complaints and not awarded: 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']})) check_tender_status(request)
def patch(self): """Update of award Example request to change the award: .. sourcecode:: http PATCH /tenders/4879d3f8ee2443169b5fbbc9f89fa607/awards/71b6c23ed8944d688e92a31ec8c3f61a HTTP/1.1 Host: example.com Accept: application/json { "data": { "value": { "amount": 600 } } } And here is the response to be expected: .. sourcecode:: http HTTP/1.0 200 OK Content-Type: application/json { "data": { "id": "4879d3f8ee2443169b5fbbc9f89fa607", "date": "2014-10-28T11:44:17.947Z", "status": "active", "suppliers": [ { "id": { "name": "Державне управління справами", "scheme": "https://ns.openprocurement.org/ua/edrpou", "uid": "00037256", "uri": "http://www.dus.gov.ua/" }, "address": { "countryName": "Україна", "postalCode": "01220", "region": "м. Київ", "locality": "м. Київ", "streetAddress": "вул. Банкова, 11, корпус 1" } } ], "value": { "amount": 600, "currency": "UAH", "valueAddedTaxIncluded": true } } } """ tender = self.request.validated["tender"] award = self.request.context award_status = award.status apply_patch(self.request, save=False, src=self.request.context.serialize()) configurator = self.request.content_configurator now = get_now() if award_status != award.status and award.status == "unsuccessful": if award.complaintPeriod: award.complaintPeriod.startDate = now else: award.complaintPeriod = {"startDate": now.isoformat()} if (tender.status == "active.qualification.stand-still" and award_status == "active" and award.status == "cancelled"): for aw in tender.awards: if aw.lotID == award.lotID: aw.status = "cancelled" add_next_awards( self.request, reverse=configurator.reverse_awarding_criteria, awarding_criteria_key=configurator.awarding_criteria_key, regenerate_all_awards=True, lot_id=award.lotID, ) self.context.dateDecision = now tender.status = "active.qualification" if tender.awardPeriod.endDate: tender.awardPeriod.endDate = None else: if award_status == "pending" and award.status == "unsuccessful": add_next_awards( self.request, reverse=configurator.reverse_awarding_criteria, awarding_criteria_key=configurator.awarding_criteria_key, ) elif award_status == "pending" and award.status == "active": pass elif award_status == "active" and award.status == "cancelled": add_next_awards( self.request, reverse=configurator.reverse_awarding_criteria, awarding_criteria_key=configurator.awarding_criteria_key, lot_id=award.lotID, ) elif award_status == "unsuccessful" and award.status == "cancelled": for aw in tender.awards: if aw.lotID == award.lotID: aw.status = "cancelled" add_next_awards( self.request, reverse=configurator.reverse_awarding_criteria, awarding_criteria_key=configurator.awarding_criteria_key, regenerate_all_awards=True, lot_id=award.lotID, ) elif self.request.authenticated_role != "Administrator" and not ( award_status == "pending" and award.status == "pending"): raise_operation_error( self.request, "Can't update award in current ({}) status".format( award_status)) if save_tender(self.request): self.LOGGER.info( "Updated tender award {}".format(self.request.context.id), extra=context_unpack(self.request, {"MESSAGE_ID": "tender_award_patch"}), ) return {"data": award.serialize("view")}
def check_tender_status(request): tender = request.validated["tender"] now = get_now() if tender.lots: if any([i.status in tender.block_complaint_status and i.relatedLot is None for i in tender.complaints]): return 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] pending_complaints = any( [i["status"] in tender.block_complaint_status and i.relatedLot == lot.id for i in tender.complaints] ) pending_awards_complaints = any( [i.status in tender.block_complaint_status for a in lot_awards for i in a.complaints] ) stand_still_end = max([a.complaintPeriod.endDate or now for a in lot_awards]) if pending_complaints or pending_awards_complaints or not stand_still_end <= now: continue elif 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: pending_complaints = any([i.status in tender.block_complaint_status for i in tender.complaints]) pending_awards_complaints = any( [i.status in tender.block_complaint_status for a in tender.awards for i in a.complaints] ) stand_still_ends = [a.complaintPeriod.endDate for a in tender.awards if a.complaintPeriod.endDate] stand_still_end = max(stand_still_ends) if stand_still_ends else now stand_still_time_expired = stand_still_end < now last_award_status = tender.awards[-1].status if tender.awards else "" if ( not pending_complaints and not pending_awards_complaints and stand_still_time_expired and 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 tender.contracts[-1].status == "active": tender.status = "complete" if tender.procurementMethodType == "belowThreshold": check_ignored_claim(tender)
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 tender.contracts[-1].status == "active": tender.status = "complete"
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 patch(self): """Update of contract """ if self.request.validated['auction_status'] not in [ 'active.qualification', 'active.awarded' ]: self.request.errors.add( 'body', 'data', 'Can\'t update contract in current ({}) auction status'.format( self.request.validated['auction_status'])) self.request.errors.status = 403 return auction = self.request.validated['auction'] if any([ i.status != 'active' for i in auction.lots if i.id in [ a.lotID for a in auction.awards if a.id == self.request.context.awardID ] ]): self.request.errors.add( 'body', 'data', 'Can update contract only in active lot status') self.request.errors.status = 403 return data = self.request.validated['data'] if data['value']: for ro_attr in ('valueAddedTaxIncluded', 'currency'): if data['value'][ro_attr] != getattr(self.context.value, ro_attr): self.request.errors.add( 'body', 'data', 'Can\'t update {} for contract value'.format(ro_attr)) self.request.errors.status = 403 return award = [ a for a in auction.awards if a.id == self.request.context.awardID ][0] if data['value']['amount'] < award.value.amount: self.request.errors.add( 'body', 'data', 'Value amount should be greater or equal to awarded amount ({})' .format(award.value.amount)) self.request.errors.status = 403 return if self.request.context.status != 'active' and 'status' in data and data[ 'status'] == 'active': award = [ a for a in auction.awards if a.id == self.request.context.awardID ][0] stand_still_end = award.complaintPeriod.endDate if stand_still_end > get_now(): self.request.errors.add( 'body', 'data', 'Can\'t sign contract before stand-still period end ({})'. format(stand_still_end.isoformat())) self.request.errors.status = 403 return pending_complaints = [ i for i in auction.complaints if i.status in ['claim', 'answered', 'pending'] and i.relatedLot in [None, award.lotID] ] pending_awards_complaints = [ i for a in auction.awards for i in a.complaints if i.status in ['claim', 'answered', 'pending'] and a.lotID == award.lotID ] if pending_complaints or pending_awards_complaints: self.request.errors.add( 'body', 'data', 'Can\'t sign contract before reviewing all complaints') self.request.errors.status = 403 return if not self.request.context.documents: self.request.errors.add( 'body', 'data', 'Cant\'t sign contract without document') self.request.errors.status = 403 return contract_status = self.request.context.status apply_patch(self.request, save=False, src=self.request.context.serialize()) if contract_status != self.request.context.status and ( contract_status != 'pending' or self.request.context.status != 'active'): self.request.errors.add('body', 'data', 'Can\'t update contract status') self.request.errors.status = 403 return if self.request.context.status == 'active' and not self.request.context.dateSigned: self.request.context.dateSigned = get_now() check_auction_status(self.request) if save_auction(self.request): self.LOGGER.info( 'Updated auction contract {}'.format(self.request.context.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'auction_contract_patch'})) return {'data': self.request.context.serialize()}
def get(self): """Auctions List Get Auctions List ---------------- Example request to get auctions list: .. sourcecode:: http GET /auctions HTTP/1.1 Host: example.com Accept: application/json This is what one should expect in response: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "data": [ { "id": "64e93250be76435397e8c992ed4214d1", "dateModified": "2014-10-27T08:06:58.158Z" } ] } """ # http://wiki.apache.org/couchdb/HTTP_view_API#Querying_Options params = {} pparams = {} fields = self.request.params.get('opt_fields', '') if fields: params['opt_fields'] = fields pparams['opt_fields'] = fields fields = fields.split(',') view_fields = fields + ['dateModified', 'id'] limit = self.request.params.get('limit', '') if limit: params['limit'] = limit pparams['limit'] = limit limit = int(limit) if limit.isdigit() and (100 if fields else 1000) >= int(limit) > 0 else 100 descending = bool(self.request.params.get('descending')) offset = self.request.params.get('offset', '') if descending: params['descending'] = 1 else: pparams['descending'] = 1 feed = self.request.params.get('feed', '') view_map = FEED.get(feed, VIEW_MAP) changes = view_map is CHANGES_VIEW_MAP if feed and feed in FEED: params['feed'] = feed pparams['feed'] = feed mode = self.request.params.get('mode', '') if mode and mode in view_map: params['mode'] = mode pparams['mode'] = mode view_limit = limit + 1 if offset else limit if changes: if offset: view_offset = decrypt(self.server.uuid, self.db.name, offset) if view_offset and view_offset.isdigit(): view_offset = int(view_offset) else: self.request.errors.add('params', 'offset', 'Offset expired/invalid') self.request.errors.status = 404 return if not offset: view_offset = 'now' if descending else 0 else: if offset: view_offset = offset else: view_offset = '9' if descending else '' list_view = view_map.get(mode, view_map[u'']) if self.update_after: view = partial(list_view, self.db, limit=view_limit, startkey=view_offset, descending=descending, stale='update_after') else: view = partial(list_view, self.db, limit=view_limit, startkey=view_offset, descending=descending) if fields: if not changes and set(fields).issubset(set(FIELDS)): results = [ (dict([(i, j) for i, j in x.value.items() + [('id', x.id), ('dateModified', x.key)] if i in view_fields]), x.key) for x in view() ] elif changes and set(fields).issubset(set(FIELDS)): results = [ (dict([(i, j) for i, j in x.value.items() + [('id', x.id)] if i in view_fields]), x.key) for x in view() ] elif fields: self.LOGGER.info('Used custom fields for auctions list: {}'.format(','.join(sorted(fields))), extra=context_unpack(self.request, {'MESSAGE_ID': 'auction_list_custom'})) results = [ (dict([(k, j) for k, j in i[u'doc'].items() if k in view_fields]), i.key) for i in view(include_docs=True) ] else: results = [ ({'id': i.id, 'dateModified': i.value['dateModified']} if changes else {'id': i.id, 'dateModified': i.key}, i.key) for i in view() ] if results: params['offset'], pparams['offset'] = results[-1][1], results[0][1] if offset and view_offset == results[0][1]: results = results[1:] elif offset and view_offset != results[0][1]: results = results[:limit] params['offset'], pparams['offset'] = results[-1][1], view_offset results = [i[0] for i in results] if changes: params['offset'] = encrypt(self.server.uuid, self.db.name, params['offset']) pparams['offset'] = encrypt(self.server.uuid, self.db.name, pparams['offset']) else: params['offset'] = offset pparams['offset'] = offset data = { 'data': results, 'next_page': { "offset": params['offset'], "path": self.request.route_path('Auctions', _query=params), "uri": self.request.route_url('Auctions', _query=params) } } if descending or offset: data['prev_page'] = { "offset": pparams['offset'], "path": self.request.route_path('Auctions', _query=pparams), "uri": self.request.route_url('Auctions', _query=pparams) } return data
def _patch_document_log(self): self.LOGGER.info( "Updated plan milestone document {}".format(self.request.context.id), extra=context_unpack(self.request, {"MESSAGE_ID": "plan_milestone_document_patch"}), )
try: _, rev = request.registry.db.save({ '_id': db_doc.id, '_rev': db_doc.rev, 'doc_type': resource }) except ResourceConflict, e: # pragma: no cover request.errors.add('body', 'data', str(e)) request.errors.status = 409 except Exception, e: # pragma: no cover request.errors.add('body', 'data', str(e)) else: LOGGER.info('Deleted {} {}: dateModified {} -> None'.format( resource, db_doc.id, db_doc.dateModified.isoformat()), extra=context_unpack(request, {'MESSAGE_ID': 'delete_resource'}, {'RESULT': rev})) return True def dump_resource(request): arch_pubkey = getattr(request.registry, 'arch_pubkey', None) res_secretkey = SecretKey() archive_box = Box(res_secretkey, arch_pubkey) res_pubkey = res_secretkey.pk del res_secretkey data = request.context.serialize() json_data = dumps(data) encrypted_data = archive_box.encrypt(json_data) return {'item': b64encode(encrypted_data), 'pubkey': b64encode(res_pubkey)}
def patch(self): """Tender Bid Document Update""" if self.request.validated['tender_status'] not in [ 'active.tendering', 'active.qualification' ]: self.request.errors.add( 'body', 'data', 'Can\'t update document in current ({}) tender status'.format( self.request.validated['tender_status'])) self.request.errors.status = 403 return tender = self.request.validated['tender'] if self.request.validated['tender_status'] == 'active.tendering' and ( tender.tenderPeriod.startDate and get_now() < tender.tenderPeriod.startDate or get_now() > tender.tenderPeriod.endDate): self.request.errors.add( 'body', 'data', 'Document can be updated only during the tendering period: from ({}) to ({}).' .format( tender.tenderPeriod.startDate and tender.tenderPeriod.startDate.isoformat(), tender.tenderPeriod.endDate.isoformat())) self.request.errors.status = 403 return if self.request.validated[ 'tender_status'] == 'active.qualification' and not [ i for i in self.request.validated['tender'].awards if i.status == 'pending' and i.bid_id == self.request.validated['bid_id'] ]: self.request.errors.add( 'body', 'data', 'Can\'t update document because award of bid is not in pending state' ) self.request.errors.status = 403 return if self.request.validated['tender_status'] != 'active.tendering' and 'confidentiality' in self.request.validated['data'] \ and self.context.confidentiality != self.request.validated['data']['confidentiality']: self.request.errors.add( 'body', 'data', 'Can\'t update document confidentiality in current ({0}) tender status' .format(self.request.validated['tender_status'])) self.request.errors.status = 403 return bid = getattr(self.context, "__parent__") if bid and bid.status in ['invalid', 'unsuccessful', 'deleted']: self.request.errors.add( 'body', 'data', 'Can\'t update document data for \'{}\' bid'.format( bid.status)) self.request.errors.status = 403 return if self.request.validated['tender_status'] == 'active.tendering': self.request.validated['tender'].modified = False if apply_patch(self.request, src=self.request.context.serialize()): update_file_content_type(self.request) self.LOGGER.info('Updated tender bid document {}'.format( self.request.context.id), extra=context_unpack( self.request, {'MESSAGE_ID': 'tender_bid_document_patch'})) return {'data': self.request.context.serialize("view")}
def collection_post(self): """Registration of new bid proposal Creating new Bid proposal ------------------------- Example request to create bid proposal: .. sourcecode:: http POST /auctions/4879d3f8ee2443169b5fbbc9f89fa607/bids HTTP/1.1 Host: example.com Accept: application/json { "data": { "tenderers": [ { "id": { "name": "Державне управління справами", "scheme": "https://ns.openprocurement.org/ua/edrpou", "uid": "00037256", "uri": "http://www.dus.gov.ua/" }, "address": { "countryName": "Україна", "postalCode": "01220", "region": "м. Київ", "locality": "м. Київ", "streetAddress": "вул. Банкова, 11, корпус 1" } } ], "value": { "amount": 489, "currency": "UAH", "valueAddedTaxIncluded": true } } } This is what one should expect in response: .. sourcecode:: http HTTP/1.1 201 Created Content-Type: application/json { "data": { "id": "4879d3f8ee2443169b5fbbc9f89fa607", "status": "registration", "date": "2014-10-28T11:44:17.947Z", "tenderers": [ { "id": { "name": "Державне управління справами", "scheme": "https://ns.openprocurement.org/ua/edrpou", "uid": "00037256", "uri": "http://www.dus.gov.ua/" }, "address": { "countryName": "Україна", "postalCode": "01220", "region": "м. Київ", "locality": "м. Київ", "streetAddress": "вул. Банкова, 11, корпус 1" } } ], "value": { "amount": 489, "currency": "UAH", "valueAddedTaxIncluded": true } } } """ # See https://github.com/open-contracting/standard/issues/78#issuecomment-59830415 # for more info upon schema auction = self.request.validated['auction'] if self.request.validated['auction_status'] != 'active.tendering': self.request.errors.add( 'body', 'data', 'Can\'t add bid in current ({}) auction status'.format( self.request.validated['auction_status'])) self.request.errors.status = 403 return if auction.tenderPeriod.startDate and get_now( ) < auction.tenderPeriod.startDate or get_now( ) > auction.tenderPeriod.endDate: self.request.errors.add( 'body', 'data', 'Bid can be added only during the tendering period: from ({}) to ({}).' .format( auction.tenderPeriod.startDate and auction.tenderPeriod.startDate.isoformat(), auction.tenderPeriod.endDate.isoformat())) self.request.errors.status = 403 return bid = self.request.validated['bid'] set_ownership(bid, self.request) auction.bids.append(bid) auction.modified = False if save_auction(self.request): self.LOGGER.info( 'Created auction bid {}'.format(bid.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'auction_bid_create'}, {'bid_id': bid.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, bid_id=bid.id, _query={}) return { 'data': bid.serialize('view'), 'access': { 'token': bid.owner_token } }
def patch(self): """Post a complaint resolution """ tender = self.request.validated['tender'] if tender.status != 'active.tendering': self.request.errors.add( 'body', 'data', 'Can\'t update complaint in current ({}) tender status'.format( tender.status)) self.request.errors.status = 403 return if self.context.status not in [ 'draft', 'claim', 'answered', 'pending', 'accepted', 'satisfied', 'stopping' ]: self.request.errors.add( 'body', 'data', 'Can\'t update complaint in current ({}) status'.format( self.context.status)) self.request.errors.status = 403 return data = self.request.validated['data'] # 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 tender.status == 'active.tendering' 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 tender.status == 'active.tendering' and self.context.status == 'draft' and data.get( 'status', self.context.status) == 'claim': if get_now() > calculate_business_date(tender.tenderPeriod.endDate, -CLAIM_SUBMIT_TIME, tender): self.request.errors.add( 'body', 'data', 'Can submit claim not later than {0.days} days before tenderPeriod end' .format(CLAIM_SUBMIT_TIME)) self.request.errors.status = 403 return apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateSubmitted = get_now() elif self.request.authenticated_role == 'complaint_owner' and tender.status == 'active.tendering' and self.context.status in [ 'draft', 'claim' ] and data.get('status', self.context.status) == 'pending': if get_now() > tender.complaintPeriod.endDate: self.request.errors.add( 'body', 'data', 'Can submit complaint not later than {0.days} days before tenderPeriod end' .format(COMPLAINT_SUBMIT_TIME)) self.request.errors.status = 403 return 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()) elif self.request.authenticated_role == 'complaint_owner' and self.context.status == 'answered' and data.get( 'satisfied', self.context.satisfied) is True and data.get( 'status', self.context.status) == 'resolved': apply_patch(self.request, save=False, src=self.context.serialize()) elif self.request.authenticated_role == 'complaint_owner' and self.context.status == 'answered' and data.get( 'satisfied', self.context.satisfied) is False and data.get( 'status', self.context.status) == 'pending': if get_now() > tender.complaintPeriod.endDate: self.request.errors.add( 'body', 'data', 'Can submit complaint not later than {0.days} days before tenderPeriod end' .format(COMPLAINT_SUBMIT_TIME)) self.request.errors.status = 403 return apply_patch(self.request, save=False, src=self.context.serialize()) self.context.type = 'complaint' self.context.dateEscalated = get_now() # tender_owner elif self.request.authenticated_role == 'tender_owner' and self.context.status == 'claim' and data.get( 'status', self.context.status) == self.context.status: now = get_now() if now > tender.enquiryPeriod.clarificationsUntil: self.request.errors.add( 'body', 'data', 'Can update claim only before enquiryPeriod.clarificationsUntil' ) self.request.errors.status = 403 return apply_patch(self.request, save=False, src=self.context.serialize()) elif self.request.authenticated_role == 'tender_owner' and self.context.status == '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': now = get_now() if now > tender.enquiryPeriod.clarificationsUntil: self.request.errors.add( 'body', 'data', 'Can update claim only before enquiryPeriod.clarificationsUntil' ) self.request.errors.status = 403 return if len(data.get('resolution', self.context.resolution)) < 20: self.request.errors.add( 'body', 'data', 'Can\'t update complaint: resolution too short') self.request.errors.status = 403 return 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 in [ 'pending', 'accepted' ]: apply_patch(self.request, save=False, src=self.context.serialize()) 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 == 'accepted' 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 [ '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: self.request.errors.add('body', 'data', 'Can\'t update complaint') self.request.errors.status = 403 return 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 complaint {}'.format(self.context.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_complaint_patch'})) return {'data': self.context.serialize("view")}
def post(self): """This API request is targeted to creating new Tenders by procuring organizations. Creating new Tender ------------------- Example request to create tender: .. sourcecode:: http POST /tenders HTTP/1.1 Host: example.com Accept: application/json { "data": { "procuringEntity": { "id": { "name": "Державне управління справами", "scheme": "https://ns.openprocurement.org/ua/edrpou", "uid": "00037256", "uri": "http://www.dus.gov.ua/" }, "address": { "countryName": "Україна", "postalCode": "01220", "region": "м. Київ", "locality": "м. Київ", "streetAddress": "вул. Банкова, 11, корпус 1" } }, "value": { "amount": 500, "currency": "UAH", "valueAddedTaxIncluded": true }, "itemsToBeProcured": [ { "description": "футляри до державних нагород", "primaryClassification": { "scheme": "CPV", "id": "44617100-9", "description": "Cartons" }, "additionalClassification": [ { "scheme": "ДКПП", "id": "17.21.1", "description": "папір і картон гофровані, паперова й картонна тара" } ], "unitOfMeasure": "item", "quantity": 5 } ], "enquiryPeriod": { "endDate": "2014-10-31T00:00:00" }, "tenderPeriod": { "startDate": "2014-11-03T00:00:00", "endDate": "2014-11-06T10:00:00" }, "awardPeriod": { "endDate": "2014-11-13T00:00:00" }, "deliveryDate": { "endDate": "2014-11-20T00:00:00" }, "minimalStep": { "amount": 35, "currency": "UAH" } } } This is what one should expect in response: .. sourcecode:: http HTTP/1.1 201 Created Location: http://localhost/api/0.1/tenders/64e93250be76435397e8c992ed4214d1 Content-Type: application/json { "data": { "id": "64e93250be76435397e8c992ed4214d1", "tenderID": "UA-64e93250be76435397e8c992ed4214d1", "dateModified": "2014-10-27T08:06:58.158Z", "procuringEntity": { "id": { "name": "Державне управління справами", "scheme": "https://ns.openprocurement.org/ua/edrpou", "uid": "00037256", "uri": "http://www.dus.gov.ua/" }, "address": { "countryName": "Україна", "postalCode": "01220", "region": "м. Київ", "locality": "м. Київ", "streetAddress": "вул. Банкова, 11, корпус 1" } }, "value": { "amount": 500, "currency": "UAH", "valueAddedTaxIncluded": true }, "itemsToBeProcured": [ { "description": "футляри до державних нагород", "primaryClassification": { "scheme": "CPV", "id": "44617100-9", "description": "Cartons" }, "additionalClassification": [ { "scheme": "ДКПП", "id": "17.21.1", "description": "папір і картон гофровані, паперова й картонна тара" } ], "unitOfMeasure": "item", "quantity": 5 } ], "enquiryPeriod": { "endDate": "2014-10-31T00:00:00" }, "tenderPeriod": { "startDate": "2014-11-03T00:00:00", "endDate": "2014-11-06T10:00:00" }, "awardPeriod": { "endDate": "2014-11-13T00:00:00" }, "deliveryDate": { "endDate": "2014-11-20T00:00:00" }, "minimalStep": { "amount": 35, "currency": "UAH" } } } """ tender_id = generate_id() tender = self.request.validated["tender"] tender.id = tender_id if not tender.get("tenderID"): tender.tenderID = generate_tender_id(get_now(), self.db, self.server_id) self.request.registry.notify(TenderInitializeEvent(tender)) if self.request.json["data"].get("status") == "draft": tender.status = "draft" access = set_ownership(tender, self.request) self.request.validated["tender"] = tender self.request.validated["tender_src"] = {} if save_tender(self.request): self.LOGGER.info( "Created tender {} ({})".format(tender_id, tender.tenderID), extra=context_unpack( self.request, {"MESSAGE_ID": "tender_create"}, { "tender_id": tender_id, "tenderID": tender.tenderID, "tender_mode": tender.mode }, ), ) self.request.response.status = 201 self.request.response.headers["Location"] = self.request.route_url( "{}:Tender".format(tender.procurementMethodType), tender_id=tender_id) return {"data": tender.serialize(tender.status), "access": access}
:return: True if Ok """ transfer = request.validated['transfer'] transfer.date = get_now() try: transfer.store(request.registry.db) except ModelValidationError, e: # pragma: no cover for i in e.message: request.errors.add('body', i, e.message[i]) request.errors.status = 422 except Exception, e: # pragma: no cover request.errors.add('body', 'data', str(e)) else: LOGGER.info('Saved transfer {}: at {}'.format(transfer.id, get_now().isoformat()), extra=context_unpack(request, {'MESSAGE_ID': 'save_transfer'})) return True def set_ownership(item, request, access_token=None, transfer_token=None): item.owner = request.authenticated_userid item.access_token = sha512(access_token).hexdigest() item.transfer_token = sha512(transfer_token).hexdigest() def update_ownership(tender, transfer): tender.owner = transfer.owner tender.owner_token = transfer.access_token tender.transfer_token = transfer.transfer_token
def patch(self): """Post a complaint resolution """ auction = self.request.validated['auction'] if auction.status not in [ 'active.tendering', 'active.auction', 'active.qualification', 'active.awarded' ]: self.request.errors.add( 'body', 'data', 'Can\'t update complaint in current ({}) auction status'. format(auction.status)) self.request.errors.status = 403 return if self.context.status not in [ 'draft', 'claim', 'answered', 'pending' ]: self.request.errors.add( 'body', 'data', 'Can\'t update complaint in current ({}) status'.format( self.context.status)) self.request.errors.status = 403 return data = self.request.validated['data'] # complaint_owner if self.request.authenticated_role == 'complaint_owner' and self.context.status in [ 'draft', 'claim', 'answered', 'pending' ] 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 auction.status in [ 'active.tendering' ] 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 auction.status in [ 'active.tendering' ] and self.context.status == 'draft' and data.get( 'status', self.context.status) == 'claim': apply_patch(self.request, save=False, src=self.context.serialize()) 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()) elif self.request.authenticated_role == 'complaint_owner' and self.context.status == 'answered' and data.get( 'satisfied', self.context.satisfied) is True and data.get( 'status', self.context.status) == 'resolved': apply_patch(self.request, save=False, src=self.context.serialize()) elif self.request.authenticated_role == 'complaint_owner' and self.context.status == 'answered' and data.get( 'satisfied', self.context.satisfied) is False and data.get( 'status', self.context.status) == 'pending': apply_patch(self.request, save=False, src=self.context.serialize()) self.context.type = 'complaint' self.context.dateEscalated = get_now() elif self.request.authenticated_role == 'auction_owner' and self.context.status == 'claim' 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 == 'auction_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: self.request.errors.add( 'body', 'data', 'Can\'t update complaint: resolution too short') self.request.errors.status = 403 return apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateAnswered = get_now() elif self.request.authenticated_role == 'auction_owner' and self.context.status == 'pending': apply_patch(self.request, save=False, src=self.context.serialize()) # reviewers elif self.request.authenticated_role == 'reviewers' and self.context.status == 'pending' 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 == 'reviewers' and self.context.status == 'pending' and data.get( 'status', self.context.status) in ['resolved', 'invalid', 'declined']: apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateDecision = get_now() else: self.request.errors.add('body', 'data', 'Can\'t update complaint') self.request.errors.status = 403 return if self.context.tendererAction and not self.context.tendererActionDate: self.context.tendererActionDate = get_now() if self.context.status not in [ 'draft', 'claim', 'answered', 'pending' ] and auction.status in ['active.qualification', 'active.awarded']: check_auction_status(self.request) if save_auction(self.request): self.LOGGER.info( 'Updated auction complaint {}'.format(self.context.id), extra=context_unpack( self.request, {'MESSAGE_ID': 'auction_complaint_patch'})) return {'data': self.context.serialize("view")}
def patch(self): """Update of proposal Example request to change bid proposal: .. sourcecode:: http PATCH /auctions/4879d3f8ee2443169b5fbbc9f89fa607/bids/71b6c23ed8944d688e92a31ec8c3f61a HTTP/1.1 Host: example.com Accept: application/json { "data": { "value": { "amount": 600 } } } And here is the response to be expected: .. sourcecode:: http HTTP/1.0 200 OK Content-Type: application/json { "data": { "value": { "amount": 600, "currency": "UAH", "valueAddedTaxIncluded": true } } } """ if self.request.authenticated_role != 'Administrator' and self.request.validated[ 'auction_status'] != 'active.tendering': self.request.errors.add( 'body', 'data', 'Can\'t update bid in current ({}) auction status'.format( self.request.validated['auction_status'])) self.request.errors.status = 403 return auction = self.request.validated['auction'] if self.request.authenticated_role != 'Administrator' and ( auction.tenderPeriod.startDate and get_now() < auction.tenderPeriod.startDate or get_now() > auction.tenderPeriod.endDate): self.request.errors.add( 'body', 'data', 'Bid can be updated only during the tendering period: from ({}) to ({}).' .format( auction.tenderPeriod.startDate and auction.tenderPeriod.startDate.isoformat(), auction.tenderPeriod.endDate.isoformat())) self.request.errors.status = 403 return if self.request.authenticated_role != 'Administrator': bid_status_to = self.request.validated['data'].get("status") if bid_status_to != self.request.context.status and bid_status_to != "active": self.request.errors.add( 'body', 'bid', 'Can\'t update bid to ({}) status'.format(bid_status_to)) self.request.errors.status = 403 return value = self.request.validated['data'].get( "value") and self.request.validated['data']["value"].get("amount") if value and value != self.request.context.get("value", {}).get("amount"): self.request.validated['data']['date'] = get_now().isoformat() if self.request.context.lotValues: lotValues = dict([(i.relatedLot, i.value.amount) for i in self.request.context.lotValues]) for lotvalue in self.request.validated['data'].get( "lotValues", []): if lotvalue['relatedLot'] in lotValues and lotvalue.get( "value", {}).get("amount") != lotValues[lotvalue['relatedLot']]: lotvalue['date'] = get_now().isoformat() self.request.validated['auction'].modified = False if apply_patch(self.request, src=self.request.context.serialize()): self.LOGGER.info( 'Updated auction bid {}'.format(self.request.context.id), extra=context_unpack(self.request, {'MESSAGE_ID': 'auction_bid_patch'})) return {'data': self.request.context.serialize("view")}
def patch(self): """Post a complaint resolution for award """ tender = self.request.validated['tender'] if tender.status not in ['active.qualification', 'active.awarded']: self.request.errors.add( 'body', 'data', 'Can\'t update complaint in current ({}) tender status'.format( tender.status)) self.request.errors.status = 403 return if any([ i.status != 'active' for i in tender.lots if i.id == self.request.validated['award'].lotID ]): self.request.errors.add( 'body', 'data', 'Can update complaint only in active lot status') self.request.errors.status = 403 return if self.context.status not in [ 'draft', 'claim', 'answered', 'pending' ]: self.request.errors.add( 'body', 'data', 'Can\'t update complaint in current ({}) status'.format( self.context.status)) self.request.errors.status = 403 return 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', 'pending' ] 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 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': apply_patch(self.request, save=False, src=self.context.serialize()) 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()) elif self.request.authenticated_role == 'complaint_owner' and self.context.status == 'answered' and data.get( 'satisfied', self.context.satisfied) is True and data.get( 'status', self.context.status) == 'resolved': apply_patch(self.request, save=False, src=self.context.serialize()) elif self.request.authenticated_role == 'complaint_owner' and self.context.status == 'answered' and data.get( 'satisfied', self.context.satisfied) is False and data.get( 'status', self.context.status) == 'pending': apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateEscalated = get_now() # tender_owner elif self.request.authenticated_role == 'tender_owner' and self.context.status == 'claim' 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 len( data.get('resolution', self.context.resolution or "")) >= 20 and data.get( 'resolutionType', self.context.resolutionType) and data.get( 'status', self.context.status) == 'answered': 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 == 'pending': apply_patch(self.request, save=False, src=self.context.serialize()) # reviewers elif self.request.authenticated_role == 'reviewers' and self.context.status == 'pending' 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 == 'reviewers' and self.context.status == 'pending' and data.get( 'status', self.context.status) in ['resolved', 'invalid', 'declined']: apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateDecision = get_now() else: self.request.errors.add('body', 'data', 'Can\'t update complaint') self.request.errors.status = 403 return if self.context.tendererAction and not self.context.tendererActionDate: self.context.tendererActionDate = get_now() if self.context.status not in [ 'draft', 'claim', 'answered', 'pending' ] and tender.status in ['active.qualification', 'active.awarded']: check_tender_status(self.request) if save_tender(self.request): 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 post(self): """This API request is targeted to creating new Auctions by procuring organizations. Creating new Auction ------------------- Example request to create auction: .. sourcecode:: http POST /auctions HTTP/1.1 Host: example.com Accept: application/json { "data": { "procuringEntity": { "id": { "name": "Державне управління справами", "scheme": "https://ns.openprocurement.org/ua/edrpou", "uid": "00037256", "uri": "http://www.dus.gov.ua/" }, "address": { "countryName": "Україна", "postalCode": "01220", "region": "м. Київ", "locality": "м. Київ", "streetAddress": "вул. Банкова, 11, корпус 1" } }, "value": { "amount": 500, "currency": "UAH", "valueAddedTaxIncluded": true }, "itemsToBeProcured": [ { "description": "футляри до державних нагород", "primaryClassification": { "scheme": "CAV", "id": "44617100-9", "description": "Cartons" }, "additionalClassification": [ { "scheme": "ДКПП", "id": "17.21.1", "description": "папір і картон гофровані, паперова й картонна тара" } ], "unitOfMeasure": "item", "quantity": 5 } ], "enquiryPeriod": { "endDate": "2014-10-31T00:00:00" }, "tenderPeriod": { "startDate": "2014-11-03T00:00:00", "endDate": "2014-11-06T10:00:00" }, "awardPeriod": { "endDate": "2014-11-13T00:00:00" }, "deliveryDate": { "endDate": "2014-11-20T00:00:00" }, "minimalStep": { "amount": 35, "currency": "UAH" } } } This is what one should expect in response: .. sourcecode:: http HTTP/1.1 201 Created Location: http://localhost/api/0.1/auctions/64e93250be76435397e8c992ed4214d1 Content-Type: application/json { "data": { "id": "64e93250be76435397e8c992ed4214d1", "auctionID": "UA-64e93250be76435397e8c992ed4214d1", "dateModified": "2014-10-27T08:06:58.158Z", "procuringEntity": { "id": { "name": "Державне управління справами", "scheme": "https://ns.openprocurement.org/ua/edrpou", "uid": "00037256", "uri": "http://www.dus.gov.ua/" }, "address": { "countryName": "Україна", "postalCode": "01220", "region": "м. Київ", "locality": "м. Київ", "streetAddress": "вул. Банкова, 11, корпус 1" } }, "value": { "amount": 500, "currency": "UAH", "valueAddedTaxIncluded": true }, "itemsToBeProcured": [ { "description": "футляри до державних нагород", "primaryClassification": { "scheme": "CAV", "id": "44617100-9", "description": "Cartons" }, "additionalClassification": [ { "scheme": "ДКПП", "id": "17.21.1", "description": "папір і картон гофровані, паперова й картонна тара" } ], "unitOfMeasure": "item", "quantity": 5 } ], "enquiryPeriod": { "endDate": "2014-10-31T00:00:00" }, "tenderPeriod": { "startDate": "2014-11-03T00:00:00", "endDate": "2014-11-06T10:00:00" }, "awardPeriod": { "endDate": "2014-11-13T00:00:00" }, "deliveryDate": { "endDate": "2014-11-20T00:00:00" }, "minimalStep": { "amount": 35, "currency": "UAH" } } } """ self.request.registry.getAdapter(self.request.validated['auction'], IAuctionManager).create_auction( self.request) auction_id = generate_id() auction = self.request.validated['auction'] auction.id = auction_id auction.auctionID = generate_auction_id(get_now(), self.db, self.server_id) if hasattr(auction, "initialize"): auction.initialize() status = self.request.json_body['data'].get('status') if status and status in ['draft', 'pending.verification']: auction.status = status acc = set_ownership(auction, self.request) self.request.validated['auction'] = auction self.request.validated['auction_src'] = {} if save_auction(self.request): self.LOGGER.info( 'Created auction {} ({})'.format(auction_id, auction.auctionID), extra=context_unpack(self.request, {'MESSAGE_ID': 'auction_create'}, { 'auction_id': auction_id, 'auctionID': auction.auctionID })) self.request.response.status = 201 auction_route_name = get_auction_route_name(self.request, auction) self.request.response.headers['Location'] = self.request.route_url( route_name=auction_route_name, auction_id=auction_id) return {'data': auction.serialize(auction.status), 'access': acc}