Beispiel #1
0
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_contracts(request, award, now)
            add_next_award(request)
Beispiel #2
0
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_contracts(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
Beispiel #3
0
    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_contracts(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":
            cancelled_awards_same_bid = [
                a for a in tender.awards
                if a.bid_id == award.bid_id and a.status == "cancelled"
            ]
            if tender.status == "active.qualification" and not cancelled_awards_same_bid:
                raise_operation_error(
                    self.request,
                    "Can't update award status to {}, if tender status is {} and there is no "
                    "cancelled award with the same bid_id".format(
                        award.status, tender.status),
                )
            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))
        check_tender_status(self.request)
        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 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)
        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_contracts(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 i.complaintPeriod and (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 i.complaintPeriod and (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")}
Beispiel #5
0
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_contracts(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":
        first_revision_date = get_first_revision_date(tender)
        new_defence_complaints = NEW_DEFENSE_COMPLAINTS_FROM < first_revision_date < NEW_DEFENSE_COMPLAINTS_TO
        standStillEnds = [
            a.complaintPeriod.endDate.astimezone(TZ) for a in tender.awards
            if (a.complaintPeriod and a.complaintPeriod.endDate and (
                a.status != "cancelled" if new_defence_complaints else True))
        ]
        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
        first_revision_date = get_first_revision_date(tender)
        new_defence_complaints = NEW_DEFENSE_COMPLAINTS_FROM < first_revision_date < NEW_DEFENSE_COMPLAINTS_TO
        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 and (
                    a.status != "cancelled" if new_defence_complaints else True
                ))
            ]
            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 != 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_contracts(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")}
Beispiel #7
0
def check_status(request):
    tender = request.validated["tender"]

    now = get_now()

    check_complaint_statuses_at_complaint_period_end(tender, now)
    check_cancellation_status(request, cancel_class=CancelTenderLot)

    for award in tender.awards:
        if award.status == "active" and not any(
            [i.awardID == award.id for i in tender.contracts]):
            add_contracts(request, award, now)
            add_next_award(request)

    if cancellation_block_tender(tender):
        return

    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
    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
        ]

    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()

    check_complaint_statuses_at_complaint_period_end(tender, now)
    check_cancellation_status(request, CancelTenderLot)

    for award in tender.awards:
        if award.status == "active" and not any(
            [i.awardID == award.id for i in tender.contracts]):
            add_contracts(request, award, now)
            add_next_award(request)

    if cancellation_block_tender(tender):
        return

    active_lots = [lot.id for lot in tender.lots
                   if lot.status == "active"] if tender.lots else [None]

    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