def cancel_lot(self, cancellation=None):
     if not cancellation:
         cancellation = self.context
     tender = self.request.validated['tender']
     [
         setattr(i, 'status', 'cancelled') for i in tender.lots
         if i.id == cancellation.relatedLot
     ]
     statuses = set([lot.status for lot in tender.lots])
     if statuses == set(['cancelled']):
         self.cancel_tender()
     elif not statuses.difference(set(['unsuccessful', 'cancelled'])):
         tender.status = 'unsuccessful'
     elif not statuses.difference(
             set(['complete', 'unsuccessful', 'cancelled'])):
         tender.status = 'complete'
     if tender.status == 'active.auction' and all([
             i.auctionPeriod and i.auctionPeriod.endDate
             for i in self.request.validated['tender'].lots
             if i.status == 'active'
     ]):
         configurator = self.request.content_configurator
         add_next_award(
             self.request,
             reverse=configurator.reverse_awarding_criteria,
             awarding_criteria_key=configurator.awarding_criteria_key)
Ejemplo n.º 2
0
 def add_next_award_method(request):
     configurator = request.content_configurator
     add_next_award(
         request,
         reverse=configurator.reverse_awarding_criteria,
         awarding_criteria_key=configurator.awarding_criteria_key,
     )
Ejemplo n.º 3
0
 def post(self):
     """Report auction results for lot.
     """
     apply_patch(self.request,
                 save=False,
                 src=self.request.validated["tender_src"])
     if all([
             i.auctionPeriod and i.auctionPeriod.endDate
             for i in self.request.validated["tender"].lots
             if i.status == "active"
     ]):
         configurator = self.request.content_configurator
         add_next_award(
             self.request,
             reverse=configurator.reverse_awarding_criteria,
             awarding_criteria_key=configurator.awarding_criteria_key,
         )
     if save_tender(self.request):
         self.LOGGER.info("Report auction results",
                          extra=context_unpack(
                              self.request,
                              {"MESSAGE_ID": "tender_lot_auction_post"}))
         return {
             "data":
             self.request.validated["tender"].serialize(
                 self.request.validated["tender"].status)
         }
 def cancel_lot(self, cancellation=None):
     if not cancellation:
         cancellation = self.context
     tender = self.request.validated['tender']
     [
         setattr(i, 'status', 'cancelled') for i in tender.lots
         if i.id == cancellation.relatedLot
     ]
     cancelled_lots = [i.id for i in tender.lots if i.status == 'cancelled']
     cancelled_items = [
         i.id for i in tender.items if i.relatedLot in cancelled_lots
     ]
     cancelled_features = [
         i.code for i in (tender.features or [])
         if i.featureOf == 'lot' and i.relatedItem in cancelled_lots
         or i.featureOf == 'item' and i.relatedItem in cancelled_items
     ]
     if tender.status in [
             'active.tendering', 'active.pre-qualification',
             'active.pre-qualification.stand-still', 'active.auction'
     ]:
         for bid in tender.bids:
             if tender.status == "active.tendering":
                 bid.documents = [
                     i for i in bid.documents if i.documentOf != 'lot'
                     or i.relatedItem not in cancelled_lots
                 ]
             bid.financialDocuments = [
                 i for i in bid.financialDocuments if i.documentOf != 'lot'
                 or i.relatedItem not in cancelled_lots
             ]
             bid.parameters = [
                 i for i in bid.parameters
                 if i.code not in cancelled_features
             ]
             bid.lotValues = [
                 i for i in bid.lotValues
                 if i.relatedLot not in cancelled_lots
             ]
             if not bid.lotValues:
                 bid.status = 'invalid' if tender.status == 'active.tendering' and bid.status in [
                     'pending', 'active'
                 ] else 'invalid.pre-qualification'
     for qualification in tender.qualifications:
         if qualification.lotID in cancelled_lots:
             qualification.status = 'cancelled'
     statuses = set([lot.status for lot in tender.lots])
     if statuses == set(['cancelled']):
         self.cancel_tender()
     elif not statuses.difference(set(['unsuccessful', 'cancelled'])):
         tender.status = 'unsuccessful'
     elif not statuses.difference(
             set(['complete', 'unsuccessful', 'cancelled'])):
         tender.status = 'complete'
     if tender.status == 'active.auction' and all([
             i.auctionPeriod and i.auctionPeriod.endDate
             for i in self.request.validated['tender'].lots
             if i.status == 'active'
     ]):
         add_next_award(self.request)
Ejemplo n.º 5
0
def check_bids(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 == 0 and i.status == 'active'
        ]
        numberOfBids_in_active_lots = [
            i.numberOfBids for i in tender.lots if i.status == 'active'
        ]
        if numberOfBids_in_active_lots and max(
                numberOfBids_in_active_lots) < 2:
            add_next_award(request)
        if not set([i.status for i in tender.lots]).difference(
                set(['unsuccessful', 'cancelled'])):
            tender.status = 'unsuccessful'
    else:
        if tender.numberOfBids < 2 and tender.auctionPeriod and tender.auctionPeriod.startDate:
            tender.auctionPeriod.startDate = None
        if tender.numberOfBids == 0:
            tender.status = 'unsuccessful'
        if tender.numberOfBids == 1:
            add_next_award(request)
Ejemplo n.º 6
0
def check_bids(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 == 0 and i.status == "active"
        ]
        numberOfBids_in_active_lots = [
            i.numberOfBids for i in tender.lots if i.status == "active"
        ]
        if numberOfBids_in_active_lots and max(
                numberOfBids_in_active_lots) < 2:
            add_next_award(request)
        if not set([i.status for i in tender.lots]).difference(
                set(["unsuccessful", "cancelled"])):
            tender.status = "unsuccessful"
    else:
        if tender.numberOfBids < 2 and tender.auctionPeriod and tender.auctionPeriod.startDate:
            tender.auctionPeriod.startDate = None
        if tender.numberOfBids == 0:
            tender.status = "unsuccessful"
        if tender.numberOfBids == 1:
            add_next_award(request)
 def post(self):
     """Report auction results for lot.
     """
     apply_patch(self.request, save=False, src=self.request.validated['tender_src'])
     if all([i.auctionPeriod and i.auctionPeriod.endDate for i in self.request.validated['tender'].lots if i.numberOfBids > 1 and i.status == 'active']):
         add_next_award(self.request)
     if save_tender(self.request):
         self.LOGGER.info('Report auction results', extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_lot_auction_post'}))
         return {'data': self.request.validated['tender'].serialize(self.request.validated['tender'].status)}
 def post(self):
     """Report auction results for lot.
     """
     apply_patch(self.request, save=False, src=self.request.validated['tender_src'])
     if all([i.auctionPeriod and i.auctionPeriod.endDate for i in self.request.validated['tender'].lots if i.status == 'active']):
         configurator = self.request.content_configurator
         add_next_award(self.request, reverse=configurator.reverse_awarding_criteria, awarding_criteria_key=configurator.awarding_criteria_key)
     if save_tender(self.request):
         self.LOGGER.info('Report auction results', extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_lot_auction_post'}))
         return {'data': self.request.validated['tender'].serialize(self.request.validated['tender'].status)}
 def post(self):
     """Report auction results for lot.
     """
     apply_patch(self.request, save=False, src=self.request.validated["tender_src"])
     if all(
         [
             i.auctionPeriod and i.auctionPeriod.endDate
             for i in self.request.validated["tender"].lots
             if i.status == "active"
         ]
     ):
         add_next_award(self.request)
     if save_tender(self.request):
         self.LOGGER.info(
             "Report auction results", extra=context_unpack(self.request, {"MESSAGE_ID": "tender_lot_auction_post"})
         )
         return {"data": self.request.validated["tender"].serialize(self.request.validated["tender"].status)}
def check_bids(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 == 0 and i.status == 'active']
        numberOfBids_in_active_lots = [i.numberOfBids for i in tender.lots if i.status == 'active']
        if numberOfBids_in_active_lots and max(numberOfBids_in_active_lots) < 2:
            add_next_award(request)
        if not set([i.status for i in tender.lots]).difference(set(['unsuccessful', 'cancelled'])):
            tender.status = 'unsuccessful'
    else:
        if tender.numberOfBids < 2 and tender.auctionPeriod and tender.auctionPeriod.startDate:
            tender.auctionPeriod.startDate = None
        if tender.numberOfBids == 0:
            tender.status = 'unsuccessful'
        if tender.numberOfBids == 1:
            add_next_award(request)
 def cancel_lot(self, cancellation=None):
     if not cancellation:
         cancellation = self.context
     tender = self.request.validated['tender']
     [setattr(i, 'status', 'cancelled') for i in tender.lots if i.id == cancellation.relatedLot]
     statuses = set([lot.status for lot in tender.lots])
     if statuses == set(['cancelled']):
         self.cancel_tender()
     elif not statuses.difference(set(['unsuccessful', 'cancelled'])):
         tender.status = 'unsuccessful'
     elif not statuses.difference(set(['complete', 'unsuccessful', 'cancelled'])):
         tender.status = 'complete'
     if tender.status == 'active.auction' and all([
         i.auctionPeriod and i.auctionPeriod.endDate
         for i in self.request.validated['tender'].lots
         if i.status == 'active'
     ]):
         add_next_award(self.request)
Ejemplo n.º 12
0
Archivo: auction.py Proyecto: lttga/op2
 def post(self):
     """Report auction results for lot.
     """
     apply_patch(self.request,
                 save=False,
                 src=self.request.validated["tender_src"])
     if all([
             i.auctionPeriod and i.auctionPeriod.endDate
             for i in self.request.validated["tender"].lots
             if i.numberOfBids > 1 and i.status == "active"
     ]):
         add_next_award(self.request)
     if save_tender(self.request):
         self.LOGGER.info("Report auction results",
                          extra=context_unpack(
                              self.request,
                              {"MESSAGE_ID": "tender_lot_auction_post"}))
         return {
             "data":
             self.request.validated["tender"].serialize(
                 self.request.validated["tender"].status)
         }
Ejemplo n.º 13
0
 def post(self):
     """Report auction results for lot.
     """
     apply_patch(self.request,
                 save=False,
                 src=self.request.validated['tender_src'])
     if all([
             i.auctionPeriod and i.auctionPeriod.endDate
             for i in self.request.validated['tender'].lots
             if i.numberOfBids > 1 and i.status == 'active'
     ]):
         add_next_award(self.request)
     if save_tender(self.request):
         self.LOGGER.info('Report auction results',
                          extra=context_unpack(
                              self.request,
                              {'MESSAGE_ID': 'tender_lot_auction_post'}))
         return {
             'data':
             self.request.validated['tender'].serialize(
                 self.request.validated['tender'].status)
         }
 def cancel_lot(self, cancellation=None):
     if not cancellation:
         cancellation = self.context
     tender = self.request.validated['tender']
     [setattr(i, 'status', 'cancelled') for i in tender.lots if i.id == cancellation.relatedLot]
     cancelled_lots = [i.id for i in tender.lots if i.status == 'cancelled']
     cancelled_items = [i.id for i in tender.items if i.relatedLot in cancelled_lots]
     cancelled_features = [
         i.code
         for i in (tender.features or [])
         if i.featureOf == 'lot' and i.relatedItem in cancelled_lots or i.featureOf == 'item' and i.relatedItem in cancelled_items
     ]
     if tender.status in ['active.tendering', 'active.pre-qualification', 'active.pre-qualification.stand-still', 'active.auction']:
         for bid in tender.bids:
             if tender.status == "active.tendering":
                 bid.documents = [i for i in bid.documents if i.documentOf != 'lot' or i.relatedItem not in cancelled_lots]
             bid.financialDocuments = [i for i in bid.financialDocuments if i.documentOf != 'lot' or i.relatedItem not in cancelled_lots]
             bid.eligibilityDocuments = [i for i in bid.eligibilityDocuments if i.documentOf != 'lot' or i.relatedItem not in cancelled_lots]
             bid.qualificationDocuments = [i for i in bid.qualificationDocuments if i.documentOf != 'lot' or i.relatedItem not in cancelled_lots]
             bid.parameters = [i for i in bid.parameters if i.code not in cancelled_features]
             bid.lotValues = [i for i in bid.lotValues if i.relatedLot not in cancelled_lots]
             if not bid.lotValues:
                 bid.status = 'invalid' if tender.status == 'active.tendering' and bid.status in ['pending', 'active'] else 'invalid.pre-qualification'
     for qualification in tender.qualifications:
         if qualification.lotID in cancelled_lots:
             qualification.status = 'cancelled'
     statuses = set([lot.status for lot in tender.lots])
     if statuses == set(['cancelled']):
         self.cancel_tender()
     elif not statuses.difference(set(['unsuccessful', 'cancelled'])):
         tender.status = 'unsuccessful'
     elif not statuses.difference(set(['complete', 'unsuccessful', 'cancelled'])):
         tender.status = 'complete'
     if tender.status == 'active.auction' and all([
         i.auctionPeriod and i.auctionPeriod.endDate
         for i in self.request.validated['tender'].lots
         if i.status == 'active'
     ]):
         add_next_award(self.request)
Ejemplo n.º 15
0
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]
    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)

    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
Ejemplo n.º 16
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]):
            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)
    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.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.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']
        if tender.status not in ['active.qualification', 'active.awarded']:
            self.request.errors.add(
                'body', 'data',
                'Can\'t update award in current ({}) tender status'.format(
                    tender.status))
            self.request.errors.status = 403
            return
        award = self.request.context
        if any(
            [i.status != 'active' for i in tender.lots
             if i.id == award.lotID]):
            self.request.errors.add(
                'body', 'data', 'Can update award only in active lot status')
            self.request.errors.status = 403
            return
        award_status = award.status
        apply_patch(self.request,
                    save=False,
                    src=self.request.context.serialize())
        if award_status == 'pending' and award.status == 'active':
            award.complaintPeriod.endDate = calculate_business_date(
                get_now(), STAND_STILL_TIME)
            tender.contracts.append(
                type(tender).contracts.model_class({
                    'awardID':
                    award.id,
                    'suppliers':
                    award.suppliers,
                    'value':
                    award.value,
                    'items':
                    [i for i in tender.items if i.relatedLot == award.lotID],
                    'contractID':
                    '{}-{}{}'.format(tender.tenderID, self.server_id,
                                     len(tender.contracts) + 1)
                }))
            add_next_award(self.request)
        elif award_status == 'active' and award.status == 'cancelled':
            award.complaintPeriod.endDate = get_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_business_date(
                get_now(), STAND_STILL_TIME)
            add_next_award(self.request)
        elif award_status == 'unsuccessful' and award.status == 'cancelled' and any(
            [i.status == 'satisfied' for i in award.complaints]):
            if tender.status == 'active.awarded':
                tender.status = 'active.qualification'
                tender.awardPeriod.endDate = None
            now = get_now()
            award.complaintPeriod.endDate = now
            cancelled_awards = []
            for i in tender.awards:
                if i.lotID != award.lotID:
                    continue
                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'
            add_next_award(self.request)
        else:
            self.request.errors.add(
                'body', 'data',
                'Can\'t update award in current ({}) status'.format(
                    award_status))
            self.request.errors.status = 403
            return
        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")}
Ejemplo n.º 18
0
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
Ejemplo n.º 19
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']
        if tender.status not in ['active.qualification', 'active.awarded']:
            self.request.errors.add('body', 'data', 'Can\'t update award in current ({}) tender status'.format(tender.status))
            self.request.errors.status = 403
            return
        award = self.request.context
        if any([i.status != 'active' for i in tender.lots if i.id == award.lotID]):
            self.request.errors.add('body', 'data', 'Can update award only in active lot status')
            self.request.errors.status = 403
            return
        award_status = award.status
        apply_patch(self.request, save=False, src=self.request.context.serialize())
        if award_status == 'pending' and award.status == 'active':
            award.complaintPeriod.endDate = calculate_business_date(get_now(), STAND_STILL_TIME)
            tender.contracts.append(type(tender).contracts.model_class({
                'awardID': award.id,
                'suppliers': award.suppliers,
                'value': award.value,
                'items': [i for i in tender.items if i.relatedLot == award.lotID ],
                'contractID': '{}-{}{}'.format(tender.tenderID, self.server_id, len(tender.contracts) +1) }))
            add_next_award(self.request)
        elif award_status == 'active' and award.status == 'cancelled':
            award.complaintPeriod.endDate = get_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_business_date(get_now(), STAND_STILL_TIME)
            add_next_award(self.request)
        elif award_status == 'unsuccessful' and award.status == 'cancelled' and any([i.status == 'satisfied' for i in award.complaints]):
            if tender.status == 'active.awarded':
                tender.status = 'active.qualification'
                tender.awardPeriod.endDate = None
            now = get_now()
            award.complaintPeriod.endDate = now
            cancelled_awards = []
            for i in tender.awards:
                if i.lotID != award.lotID:
                    continue
                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'
            add_next_award(self.request)
        else:
            self.request.errors.add('body', 'data', 'Can\'t update award in current ({}) status'.format(award_status))
            self.request.errors.status = 403
            return
        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()
    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)
    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.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.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)
Ejemplo n.º 21
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_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)
Ejemplo n.º 22
0
    def collection_post(self):
        """Report auction results.

        Report auction results
        ----------------------

        Example request to report auction results:

        .. sourcecode:: http

            POST /tenders/4879d3f8ee2443169b5fbbc9f89fa607/auction HTTP/1.1
            Host: example.com
            Accept: application/json

            {
                "data": {
                    "dateModified": "2014-10-27T08:06:58.158Z",
                    "bids": [
                        {
                            "value": {
                                "amount": 400,
                                "currency": "UAH"
                            }
                        },
                        {
                            "value": {
                                "amount": 385,
                                "currency": "UAH"
                            }
                        }
                    ]
                }
            }

        This is what one should expect in response:

        .. sourcecode:: http

            HTTP/1.1 200 OK
            Content-Type: application/json

            {
                "data": {
                    "dateModified": "2014-10-27T08:06:58.158Z",
                    "bids": [
                        {
                            "value": {
                                "amount": 400,
                                "currency": "UAH",
                                "valueAddedTaxIncluded": true
                            }
                        },
                        {
                            "value": {
                                "amount": 385,
                                "currency": "UAH",
                                "valueAddedTaxIncluded": true
                            }
                        }
                    ],
                    "minimalStep":{
                        "amount": 35,
                        "currency": "UAH"
                    },
                    "tenderPeriod":{
                        "startDate": "2014-11-04T08:00:00"
                    }
                }
            }

        """
        apply_patch(self.request,
                    save=False,
                    src=self.request.validated['tender_src'])
        if all([
                i.auctionPeriod and i.auctionPeriod.endDate
                for i in self.request.validated['tender'].lots
                if i.status == 'active'
        ]):
            add_next_award(self.request,
                           reverse=self.request.content_configurator.
                           reverse_awarding_criteria)
        if save_tender(self.request):
            self.LOGGER.info(
                'Report auction results',
                extra=context_unpack(self.request,
                                     {'MESSAGE_ID': 'tender_auction_post'}))
            return {
                'data':
                self.request.validated['tender'].serialize(
                    self.request.validated['tender'].status)
            }
Ejemplo n.º 23
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())
        configurator = self.request.content_configurator

        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_complaint_business_date(
                now, STAND_STILL_TIME, tender)
            add_contract(self.request, award, now)
            add_next_award(
                self.request,
                reverse=configurator.reverse_awarding_criteria,
                awarding_criteria_key=configurator.awarding_criteria_key,
            )
        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"
            add_next_award(
                self.request,
                reverse=configurator.reverse_awarding_criteria,
                awarding_criteria_key=configurator.awarding_criteria_key,
            )
        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"
            add_next_award(
                self.request,
                reverse=configurator.reverse_awarding_criteria,
                awarding_criteria_key=configurator.awarding_criteria_key,
            )
        elif award_status == "pending" and award.status == "unsuccessful":
            award.complaintPeriod.endDate = calculate_complaint_business_date(
                get_now(), STAND_STILL_TIME, tender)
            add_next_award(
                self.request,
                reverse=configurator.reverse_awarding_criteria,
                awarding_criteria_key=configurator.awarding_criteria_key,
            )
        elif (award_status == "unsuccessful" and award.status == "cancelled"
              and any([i.status == "satisfied" for i in award.complaints])):
            if tender.status == "active.awarded":
                tender.status = "active.qualification"
                tender.awardPeriod.endDate = None
            if award.complaintPeriod.endDate > now:
                award.complaintPeriod.endDate = now
            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"
            add_next_award(
                self.request,
                reverse=configurator.reverse_awarding_criteria,
                awarding_criteria_key=configurator.awarding_criteria_key,
            )
        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")}
Ejemplo n.º 24
0
    def collection_post(self):
        """Report auction results.

        Report auction results
        ----------------------

        Example request to report auction results:

        .. sourcecode:: http

            POST /tenders/4879d3f8ee2443169b5fbbc9f89fa607/auction HTTP/1.1
            Host: example.com
            Accept: application/json

            {
                "data": {
                    "dateModified": "2014-10-27T08:06:58.158Z",
                    "bids": [
                        {
                            "value": {
                                "amount": 400,
                                "currency": "UAH"
                            }
                        },
                        {
                            "value": {
                                "amount": 385,
                                "currency": "UAH"
                            }
                        }
                    ]
                }
            }

        This is what one should expect in response:

        .. sourcecode:: http

            HTTP/1.1 200 OK
            Content-Type: application/json

            {
                "data": {
                    "dateModified": "2014-10-27T08:06:58.158Z",
                    "bids": [
                        {
                            "value": {
                                "amount": 400,
                                "currency": "UAH",
                                "valueAddedTaxIncluded": true
                            }
                        },
                        {
                            "value": {
                                "amount": 385,
                                "currency": "UAH",
                                "valueAddedTaxIncluded": true
                            }
                        }
                    ],
                    "minimalStep":{
                        "amount": 35,
                        "currency": "UAH"
                    },
                    "tenderPeriod":{
                        "startDate": "2014-11-04T08:00:00"
                    }
                }
            }

        """
        apply_patch(self.request, save=False, src=self.request.validated["tender_src"])
        if all(
            [
                i.auctionPeriod and i.auctionPeriod.endDate
                for i in self.request.validated["tender"].lots
                if i.status == "active"
            ]
        ):
            add_next_award(self.request)
        if save_tender(self.request):
            self.LOGGER.info(
                "Report auction results", extra=context_unpack(self.request, {"MESSAGE_ID": "tender_auction_post"})
            )
            return {"data": self.request.validated["tender"].serialize(self.request.validated["tender"].status)}