Exemple #1
0
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"