def check_award_status(request): tender = request.validated["tender"] now = get_now() awards = tender.awards is_cancelled = [ award for award in tender.awards if award.status == 'cancelled' ] for award in awards: if (award.status == 'pending' and calculate_tender_business_date( award.date, QUALIFICATION_DURATION, tender) <= now): award.status = 'unsuccessful' if is_cancelled: tender.status = 'unsuccessful' LOGGER.info( "Switched tender {} to {}".format(tender["id"], tender.status), extra=context_unpack(request, { "MESSAGE_ID": "switched_tender_{}".format(tender.status) }), ) else: add_next_award(request) 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)
def check_status(request): tender = request.validated["tender"] now = get_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) after_enquiryPeriod_endDate = ( not tender.tenderPeriod.startDate and tender.enquiryPeriod.endDate.astimezone(TZ) <= now) after_tenderPeriod_startDate = tender.tenderPeriod.startDate and tender.tenderPeriod.startDate.astimezone( TZ) <= now if tender.status == "active.enquiries" and (after_enquiryPeriod_endDate or after_tenderPeriod_startDate): 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
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()) now = get_now() if award_status != award.status and award.status in ["active", "unsuccessful"]: if award.complaintPeriod: award.complaintPeriod.startDate = now else: award.complaintPeriod = {"startDate": now.isoformat()} if award_status == "pending" and award.status == "active": award.complaintPeriod.endDate = calculate_tender_business_date(now, STAND_STILL_TIME, tender, True) add_contract(self.request, award, now) add_next_award(self.request) elif award_status == "active" and award.status == "cancelled": if award.complaintPeriod.endDate > now: award.complaintPeriod.endDate = now for j in award.complaints: if j.status not in ["invalid", "resolved", "declined"]: j.status = "cancelled" j.cancellationReason = "cancelled" j.dateCanceled = now for i in tender.contracts: if i.awardID == award.id: i.status = "cancelled" add_next_award(self.request) elif award_status == "pending" and award.status == "unsuccessful": award.complaintPeriod.endDate = calculate_tender_business_date(get_now(), STAND_STILL_TIME, tender, True) add_next_award(self.request) elif ( award_status == "unsuccessful" and award.status == "cancelled" and any([i.status in ["claim", "answered", "pending", "resolved"] for i in award.complaints]) ): if tender.status == "active.awarded": tender.status = "active.qualification" tender.awardPeriod.endDate = None award.complaintPeriod.endDate = now cancelled_awards = [] for i in tender.awards[tender.awards.index(award):]: if i.lotID != award.lotID: continue if i.complaintPeriod and (not i.complaintPeriod.endDate or i.complaintPeriod.endDate > now): i.complaintPeriod.endDate = now i.status = "cancelled" for j in i.complaints: if j.status not in ["invalid", "resolved", "declined"]: j.status = "cancelled" j.cancellationReason = "cancelled" j.dateCanceled = now cancelled_awards.append(i.id) for i in tender.contracts: if i.awardID in cancelled_awards: i.status = "cancelled" add_next_award(self.request) 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_status(request): tender = request.validated["tender"] now = get_now() check_complaint_statuses_at_complaint_period_end(tender, now) check_cancellation_status(request) if cancellation_block_tender(tender): return 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) if (not tender.lots and 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.auction"), extra=context_unpack( request, {"MESSAGE_ID": "switched_tender_active.auction"}), ) tender.status = "active.auction" 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 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.auction"), extra=context_unpack( request, {"MESSAGE_ID": "switched_tender_active.auction"}), ) tender.status = "active.auction" 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 and a.complaintPeriod.endDate ] if not standStillEnds: return standStillEnd = max(standStillEnds) if standStillEnd <= now: 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 ]) 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 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 and a.complaintPeriod.endDate ] if not standStillEnds: continue standStillEnd = max(standStillEnds) if standStillEnd <= now: 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 ]) awarded = any([i["status"] == "active" for i in lot_awards]) if not pending_complaints and 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()) now = get_now() if award.status == "active" and not award.qualified: raise_operation_error(self.request, "Can't update award to active status with not qualified") if ( award.lotID and [aw.lotID for aw in tender.awards if aw.status in ["pending", "active"]].count(award.lotID) > 1 ): self.request.errors.add("body", "lotID", "Another award is already using this lotID.") self.request.errors.status = 403 raise error_handler(self.request.errors) if award_status == "pending" and award.status == "active": award.complaintPeriod = {"startDate": now.isoformat(), "endDate": calculate_complaint_business_date(now, self.stand_still_delta, tender) } add_contract(self.request, award, now) elif ( award_status == "active" and award.status == "cancelled" and any([i.status == "satisfied" for i in award.complaints]) ): cancelled_awards = [] for i in tender.awards: if i.lotID != award.lotID: continue if not i.complaintPeriod.endDate or i.complaintPeriod.endDate > now: i.complaintPeriod.endDate = now i.status = "cancelled" cancelled_awards.append(i.id) for i in tender.contracts: if i.awardID in cancelled_awards: i.status = "cancelled" elif award_status == "active" and award.status == "cancelled": if award.complaintPeriod.endDate > now: award.complaintPeriod.endDate = now for i in tender.contracts: if i.awardID == award.id: i.status = "cancelled" elif award_status == "pending" and award.status == "unsuccessful": award.complaintPeriod = {"startDate": now.isoformat(), "endDate": now} elif ( award_status == "unsuccessful" and award.status == "cancelled" and any([i.status == "satisfied" for i in award.complaints]) ): cancelled_awards = [] for i in tender.awards: if i.lotID != award.lotID: continue if not i.complaintPeriod.endDate or i.complaintPeriod.endDate > now: i.complaintPeriod.endDate = now i.status = "cancelled" cancelled_awards.append(i.id) for i in tender.contracts: if i.awardID in cancelled_awards: i.status = "cancelled" elif award_status != award.status: raise_operation_error(self.request, "Can't update award in current ({}) status".format(award_status)) elif self.request.authenticated_role != "Administrator" 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"}, {"TENDER_REV": tender.rev}), ) return {"data": award.serialize("view")}
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()) if award_status == "pending" and award.status == "active": add_contract(self.request, award) # add_next_award(self.request) elif award_status == "active" and award.status == "cancelled": for i in tender.contracts: if i.awardID == award.id: i.status = "cancelled" # add_next_award(self.request) elif award_status == "pending" and award.status == "unsuccessful": pass # add_next_award(self.request) elif award_status != award.status: raise_operation_error(self.request, "Can't update award in current ({}) status".format(award_status)) elif 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"}, {"TENDER_REV": tender.rev}), ) return {"data": award.serialize("view")}
def check_status(request): tender = request.validated["tender"] now = get_now() configurator = request.content_configurator check_complaint_statuses_at_complaint_period_end(tender, now) check_cancellation_status(request, CancelTenderLot) active_lots = [lot.id for lot in tender.lots if lot.status == "active"] if tender.lots else [None] 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, reverse=configurator.reverse_awarding_criteria, awarding_criteria_key=configurator.awarding_criteria_key, ) if block_tender(request): return 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) 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) elif not tender.lots and tender.status == "active.awarded": standStillEnds = [ a.complaintPeriod.endDate.astimezone(TZ) for a in tender.awards if a.complaintPeriod and a.complaintPeriod.endDate ] if standStillEnds: standStillEnd = max(standStillEnds) if standStillEnd <= now: check_tender_status(request) elif tender.lots and tender.status in [ "active.qualification", "active.awarded" ]: 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 and a.complaintPeriod.endDate ] if not standStillEnds: continue standStillEnd = max(standStillEnds) if standStillEnd <= now: check_tender_status(request) break
def check_status(request): tender = request.validated["tender"] now = get_now() 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]): add_contract(request, award, now) add_next_award( request, reverse=configurator.reverse_awarding_criteria, awarding_criteria_key=configurator.awarding_criteria_key, ) if ( not tender.lots and 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.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 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.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