def add_next_awards(request, reverse=False, awarding_criteria_key="amount", regenerate_all_awards=False, lot_id=None): """Adding next award. :param request: The pyramid request object. :param reverse: Is used for sorting bids to generate award. By default (reverse = False) awards are generated from lower to higher by value.amount When reverse is set to True awards are generated from higher to lower by value.amount """ tender = request.validated["tender"] max_awards = tender["maxAwardsCount"] now = get_now() if not tender.awardPeriod: tender.awardPeriod = type(tender).awardPeriod({}) if not tender.awardPeriod.startDate: tender.awardPeriod.startDate = now if tender.lots: statuses = set() for lot in tender.lots: if lot.status != "active": continue lot_awards = [ award for award in tender.awards if award.lotID == lot.id ] lot_awards_statuses = { award["status"] for award in tender.awards if award.lotID == lot.id } if lot_awards_statuses and lot_awards_statuses.issubset( {"pending", "active"}): statuses.union(lot_awards_statuses) continue all_bids = prepare_bids_for_awarding(tender, tender.bids, lot_id=lot.id) if not all_bids: lot.status = "unsuccessful" statuses.add("unsuccessful") continue selected_bids = exclude_unsuccessful_awarded_bids(tender, all_bids, lot_id=lot.id) if not regenerate_all_awards and lot.id == lot_id: # this block seems is supposed to cause the function append only one award # for a bid of the first (the only?) cancelled award cancelled_award_bid_ids = [ award.bid_id for award in lot_awards if award.status == "cancelled" and request.context.id == award.id ] if cancelled_award_bid_ids: selected_bids = [ bid for bid in all_bids if bid["id"] == cancelled_award_bid_ids[0] ] if max_awards: # limit awards selected_bids = selected_bids[:max_awards] active_award_bid_ids = { a.bid_id for a in lot_awards if a.status in ("active", "pending") } selected_bids = list( filter(lambda b: b["id"] not in active_award_bid_ids, selected_bids)) if selected_bids: for bid in selected_bids: tender.append_award(bid, all_bids, lot_id=lot.id) statuses.add("pending") else: statuses.add("unsuccessful") if (statuses.difference({"unsuccessful", "active"}) and any([i for i in tender.lots])): # logic for auction to switch status tender.awardPeriod.endDate = None tender.status = "active.qualification" else: # pragma: no cover if not tender.awards or request.context.status in ("cancelled", "unsuccessful"): codes = [i.code for i in tender.features or []] active_bids = [{ "id": bid.id, "value": bid.value.serialize(), "tenderers": bid.tenderers, "parameters": [i for i in bid.parameters if i.code in codes], "date": bid.date, } for bid in tender.bids if bid.status == "active"] cancelled_awards = None if not regenerate_all_awards: cancelled_awards = [ award.bid_id for award in tender.awards if award.status == "cancelled" and request.context.id == award.id ] unsuccessful_awards = [ i.bid_id for i in tender.awards if i.status == "unsuccessful" ] bids = chef(active_bids, tender.features or [], unsuccessful_awards, reverse, awarding_criteria_key) bids = [bid for bid in bids if bid["id"] == cancelled_awards[0] ] if cancelled_awards else bids bids = bids[:max_awards] if max_awards else bids active_awards = [ a.bid_id for a in tender.awards if a.status in ("active", "pending") ] bids = [bid for bid in bids if bid["id"] not in active_awards] if bids: for bid in bids: award = tender.__class__.awards.model_class({ "bid_id": bid["id"], "status": "pending", "date": get_now(), "value": bid["value"], "suppliers": bid["tenderers"], }) award.__parent__ = tender tender.awards.append(award) if tender.awards[ -1].status == "pending": # logic for auction to switch status tender.awardPeriod.endDate = None tender.status = "active.qualification"
def add_next_award(request, reverse=False, awarding_criteria_key="amount"): """Adding next award. reverse and awarding_criteria_key are deprecated, since we can get them from request :param request: The pyramid request object. :param reverse: Is used for sorting bids to generate award. By default (reverse = False) awards are generated from lower to higher by value.amount When reverse is set to True awards are generated from higher to lower by value.amount """ tender = request.validated["tender"] now = get_now() if not tender.awardPeriod: tender.awardPeriod = type(tender).awardPeriod({}) if not tender.awardPeriod.startDate: tender.awardPeriod.startDate = now if tender.lots: statuses = set() for lot in tender.lots: if lot.status != "active": continue lot_awards = [i for i in tender.awards if i.lotID == lot.id] if lot_awards and lot_awards[-1].status in ["pending", "active"]: statuses.add(lot_awards[-1].status if lot_awards else "unsuccessful") continue all_bids = prepare_bids_for_awarding(tender, tender.bids, lot_id=lot.id) if all_bids: bids = exclude_unsuccessful_awarded_bids(tender, all_bids, lot_id=lot.id) if bids: tender.append_award(bids[0], all_bids, lot_id=lot.id) request.response.headers["Location"] = request.route_url( "{}:Tender Awards".format(tender.procurementMethodType), tender_id=tender.id, award_id=tender.awards[-1]["id"] ) statuses.add("pending") else: statuses.add("unsuccessful") else: lot.status = "unsuccessful" statuses.add("unsuccessful") if statuses.difference(set(["unsuccessful", "active"])): tender.awardPeriod.endDate = None tender.status = "active.qualification" else: tender.awardPeriod.endDate = now tender.status = "active.awarded" else: if not tender.awards or tender.awards[-1].status not in ["pending", "active"]: all_bids = prepare_bids_for_awarding(tender, tender.bids, lot_id=None) bids = exclude_unsuccessful_awarded_bids(tender, all_bids, lot_id=None) if bids: tender.append_award(bids[0], all_bids) request.response.headers["Location"] = request.route_url( "{}:Tender Awards".format(tender.procurementMethodType), tender_id=tender.id, award_id=tender.awards[-1]["id"] ) if tender.awards[-1].status == "pending": tender.awardPeriod.endDate = None tender.status = "active.qualification" else: tender.awardPeriod.endDate = now tender.status = "active.awarded"
def add_next_award(request): """Adding next award. :param request: The pyramid request object. """ tender = request.validated["tender"] now = get_now() if not tender.awardPeriod: tender.awardPeriod = type(tender).awardPeriod({}) if not tender.awardPeriod.startDate: tender.awardPeriod.startDate = now if tender.lots: statuses = set() for lot in tender.lots: if lot.status != "active": continue lot_awards = [i for i in tender.awards if i.lotID == lot.id] if lot_awards and lot_awards[-1].status in ["pending", "active"]: statuses.add(lot_awards[-1].status) continue all_bids = prepare_bids_for_awarding(tender, tender.bids, lot_id=lot.id) if all_bids: bids = exclude_unsuccessful_awarded_bids(tender, all_bids, lot_id=lot.id) if bids: tender.append_award(bids[0], all_bids, lot_id=lot.id) request.response.headers["Location"] = request.route_url( "{}:Tender Awards".format( tender.procurementMethodType), tender_id=tender.id, award_id=tender.awards[-1]["id"]) statuses.add("pending") else: statuses.add("unsuccessful") else: lot.status = "unsuccessful" statuses.add("unsuccessful") if statuses.difference(set(["unsuccessful", "active"])): tender.awardPeriod.endDate = None tender.status = "active.qualification" else: tender.awardPeriod.endDate = now tender.status = "active.awarded" else: if not tender.awards or tender.awards[-1].status not in [ "pending", "active" ]: all_bids = prepare_bids_for_awarding(tender, tender.bids, lot_id=None) bids = exclude_unsuccessful_awarded_bids(tender, all_bids, lot_id=None) if bids: tender.append_award(bids[0], all_bids) request.response.headers["Location"] = request.route_url( "{}:Tender Awards".format(tender.procurementMethodType), tender_id=tender.id, award_id=tender.awards[-1]["id"]) if tender.awards[-1].status == "pending": tender.awardPeriod.endDate = None tender.status = "active.qualification" else: tender.awardPeriod.endDate = now tender.status = "active.awarded"
def add_next_award(request, reverse=False, awarding_criteria_key="amount"): """Adding next award. reverse and awarding_criteria_key are deprecated, since we can get them from request :param request: The pyramid request object. :param reverse: Is used for sorting bids to generate award. By default (reverse = False) awards are generated from lower to higher by value.amount When reverse is set to True awards are generated from higher to lower by value.amount """ tender = request.validated["tender"] now = get_now() if not tender.awardPeriod: tender.awardPeriod = type(tender).awardPeriod({}) if not tender.awardPeriod.startDate: tender.awardPeriod.startDate = now if tender.lots: statuses = set() for lot in tender.lots: if lot.status != "active": continue lot_awards = [i for i in tender.awards if i.lotID == lot.id] if lot_awards and lot_awards[-1].status in ["pending", "active"]: statuses.add( lot_awards[-1].status if lot_awards else "unsuccessful") continue all_bids = prepare_bids_for_awarding(tender, tender.bids, lot_id=lot.id) if all_bids: bids = exclude_unsuccessful_awarded_bids(tender, all_bids, lot_id=lot.id) if bids: tender.append_award(bids[0], all_bids, lot_id=lot.id) request.response.headers["Location"] = request.route_url( "{}:Tender Awards".format( tender.procurementMethodType), tender_id=tender.id, award_id=tender.awards[-1]["id"]) statuses.add("pending") else: statuses.add("unsuccessful") else: lot.status = "unsuccessful" statuses.add("unsuccessful") if statuses.difference(set(["unsuccessful", "active"])): tender.awardPeriod.endDate = None tender.status = "active.qualification" else: tender.awardPeriod.endDate = now tender.status = "active.awarded" first_revision_date = get_first_revision_date(tender) new_defence_complaints = NEW_DEFENSE_COMPLAINTS_FROM < first_revision_date < NEW_DEFENSE_COMPLAINTS_TO if new_defence_complaints and statuses == set(["unsuccessful"]): for lot in tender.lots: if lot.status != "active": continue pending_complaints = any([ i["status"] in tender.block_complaint_status and i.relatedLot == lot.id for i in tender.complaints ]) lot_awards = [ i for i in tender.awards if i.lotID == lot.id ] if not lot_awards: continue awards_no_complaint_periods = all([ not a.complaintPeriod for a in lot_awards if a["status"] == "unsuccessful" ]) if (not pending_complaints and awards_no_complaint_periods): 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" lot_statuses = set([lot.status for lot in tender.lots]) if not lot_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" else: if not tender.awards or tender.awards[-1].status not in [ "pending", "active" ]: all_bids = prepare_bids_for_awarding(tender, tender.bids, lot_id=None) bids = exclude_unsuccessful_awarded_bids(tender, all_bids, lot_id=None) if bids: tender.append_award(bids[0], all_bids) request.response.headers["Location"] = request.route_url( "{}:Tender Awards".format(tender.procurementMethodType), tender_id=tender.id, award_id=tender.awards[-1]["id"]) if tender.awards[-1].status == "pending": tender.awardPeriod.endDate = None tender.status = "active.qualification" else: tender.awardPeriod.endDate = now tender.status = "active.awarded" first_revision_date = get_first_revision_date(tender) new_defence_complaints = NEW_DEFENSE_COMPLAINTS_FROM < first_revision_date < NEW_DEFENSE_COMPLAINTS_TO if new_defence_complaints: pending_complaints = any([ i["status"] in tender.block_complaint_status for i in tender.complaints ]) last_award_unsuccessful = tender.awards[ -1].status == "unsuccessful" awards_no_complaint_periods = all([ not a.complaintPeriod for a in tender.awards if a["status"] == "unsuccessful" ]) if (not pending_complaints and last_award_unsuccessful and awards_no_complaint_periods): LOGGER.info( "Switched tender {} to {}".format( tender.id, "unsuccessful"), extra=context_unpack( request, {"MESSAGE_ID": "switched_tender_unsuccessful"}), ) tender.status = "unsuccessful"