def test_calculate_business_date(self):
        date_obj = datetime(2017,10,7)
        delta_obj = timedelta(days=7)

        # Test with accelerator = 1440
        context = {
            "procurementMethodDetails": "quick, accelerator=1440",
            "procurementMethodType": "negotiation"
        }
        business_date = calculate_business_date(
            date_obj, delta_obj, context=context, working_days=True)
        self.assertEqual(business_date, datetime(2017, 10, 7, 0, 7))

        # Test without context and working_days
        business_date = calculate_business_date(date_obj, delta_obj)
        self.assertEqual(business_date, datetime(2017, 10, 14))

        # Test with working_days and timedelta_obj > timedelta()
        business_date = calculate_business_date(
            date_obj, delta_obj, working_days=True)
        self.assertEqual(business_date, datetime(2017, 10, 19))

        # Test with working days and timedelta_obj < timedelta()
        business_date = calculate_business_date(
            date_obj, timedelta(0), working_days=True
        )
        self.assertEqual(business_date, datetime(2017, 10, 7))

        # Another test with working days and timedelta > timedelta()
        date_obj = datetime(2017, 10, 15)
        delta_obj = timedelta(1)
        business_date = calculate_business_date(
            date_obj, delta_obj, working_days=True
        )
        self.assertEqual(business_date, datetime(2017, 10, 18))
예제 #2
0
 def check_auction_time(self):
     if self.auctionPeriod and self.auctionPeriod.startDate and self.auctionPeriod.shouldStartAfter \
             and self.auctionPeriod.startDate > calculate_business_date(parse_date(self.auctionPeriod.shouldStartAfter), AUCTION_PERIOD_TIME, self, True):
         self.auctionPeriod.startDate = None
     for lot in self.lots:
         if lot.auctionPeriod and lot.auctionPeriod.startDate and lot.auctionPeriod.shouldStartAfter \
                 and lot.auctionPeriod.startDate > calculate_business_date(parse_date(lot.auctionPeriod.shouldStartAfter), AUCTION_PERIOD_TIME, self, True):
             lot.auctionPeriod.startDate = None
def check_complaint_status(request, complaint, now=None):
    if not now:
        now = get_now()
    if complaint.status == 'claim' and calculate_business_date(complaint.dateSubmitted, COMPLAINT_STAND_STILL_TIME, request.tender) < now:
        complaint.status = 'pending'
        complaint.type = 'complaint'
        complaint.dateEscalated = now
    elif complaint.status == 'answered' and calculate_business_date(complaint.dateAnswered, COMPLAINT_STAND_STILL_TIME, request.tender) < now:
        complaint.status = complaint.resolutionType
 def tender_enquiryPeriod(self):
     endDate = calculate_business_date(self.tenderPeriod.endDate,
                                       -ENQUIRY_PERIOD_TIME, self)
     return EnquiryPeriod(
         dict(startDate=self.tenderPeriod.startDate,
              endDate=endDate,
              invalidationDate=self.enquiryPeriod
              and self.enquiryPeriod.invalidationDate,
              clarificationsUntil=calculate_business_date(
                  endDate, ENQUIRY_STAND_STILL_TIME, self, True)))
 def __call__(self, cls, data, period):
     if not data['_rev'] and calculate_business_date(
             get_now(), -timedelta(minutes=10)) >= period.startDate:
         raise ValidationError(
             u"tenderPeriod.startDate should be in greater than current date"
         )
     if period and calculate_business_date(
             period.startDate, TENDERING_DURATION, data) > period.endDate:
         raise ValidationError(
             u"tenderPeriod should be greater than {} days".format(
                 TENDERING_DAYS))
 def validate_tenderPeriod(self, data, period):
     # if data['_rev'] is None when tender was created just now
     if not data['_rev'] and calculate_business_date(
             get_now(), -timedelta(minutes=10)) >= period.startDate:
         raise ValidationError(
             u"tenderPeriod.startDate should be in greater than current date"
         )
     if period and calculate_business_date(period.startDate, TENDER_PERIOD,
                                           data) > period.endDate:
         raise ValidationError(
             u"tenderPeriod should be greater than 15 days")
 def invalidate_bids_data(self):
     if self.auctionPeriod and self.auctionPeriod.startDate and self.auctionPeriod.shouldStartAfter \
             and self.auctionPeriod.startDate > calculate_business_date(parse_date(self.auctionPeriod.shouldStartAfter), AUCTION_PERIOD_TIME, self, True):
         self.auctionPeriod.startDate = None
     for lot in self.lots:
         if lot.auctionPeriod and lot.auctionPeriod.startDate and lot.auctionPeriod.shouldStartAfter \
                 and lot.auctionPeriod.startDate > calculate_business_date(parse_date(lot.auctionPeriod.shouldStartAfter), AUCTION_PERIOD_TIME, self, True):
             lot.auctionPeriod.startDate = None
     self.enquiryPeriod.invalidationDate = get_now()
     for bid in self.bids:
         if bid.status not in ["deleted", "draft"]:
             bid.status = "invalid"
 def __call__(self, obj, *args, **kwargs):
     configurator = getAdapter(obj, IContentConfigurator)
     enquiryPeriod_class = obj._fields['enquiryPeriod']
     endDate = calculate_business_date(obj.tenderPeriod.endDate,
                                       -configurator.questions_stand_still,
                                       obj)
     return enquiryPeriod_class(
         dict(startDate=obj.tenderPeriod.startDate,
              endDate=endDate,
              invalidationDate=obj.enquiryPeriod
              and obj.enquiryPeriod.invalidationDate,
              clarificationsUntil=calculate_business_date(
                  endDate, configurator.enquiry_stand_still, obj, True)))
예제 #9
0
def tender_init_handler(event):
    """ initialization handler for openuadefence tenders """
    tender = event.tender
    endDate = calculate_business_date(tender.tenderPeriod.endDate,
                                      -ENQUIRY_PERIOD_TIME, tender, True)
    tender.enquiryPeriod = EnquiryPeriod(dict(startDate=tender.tenderPeriod.startDate,
                                         endDate=endDate,
                                         invalidationDate=tender.enquiryPeriod and tender.enquiryPeriod.invalidationDate,
                                         clarificationsUntil=calculate_business_date(endDate, ENQUIRY_STAND_STILL_TIME, tender, True)))
    now = get_now()
    tender.date = now
    if tender.lots:
        for lot in tender.lots:
            lot.date = now
예제 #10
0
def tender_init_handler_2(event):
    """ initialization handler for tenders """
    #   import pdb; pdb.set_trace()
    tender = event.tender
    tender.tenderPeriod.endDate = calculate_business_date(
        tender.tenderPeriod.startDate, TENDERING_DURATION_UA, tender)
    tender_init_handler_ua(event)
 def validate_update_tender(self):
     """ TODO move validators
     This class is inherited in openua package, but validate_update_tender function has different validators.
     For now, we have no way to use different validators on methods according to procedure type.
     """
     if self.request.authenticated_role != 'auction' and self.request.validated['tender_status'] not in ['active.tendering', STAGE2_STATUS] or \
        self.request.authenticated_role == 'auction' and self.request.validated['tender_status'] not in ['active.auction', 'active.qualification']:
         raise_operation_error(
             self.request,
             'Can\'t {} document in current ({}) tender status'.format(
                 OPERATIONS.get(self.request.method),
                 self.request.validated['tender_status']))
     if self.request.validated[
             'tender_status'] == 'active.tendering' and calculate_business_date(
                 get_now(), TENDERING_EXTRA_PERIOD,
                 self.request.validated['tender']
             ) > self.request.validated['tender'].tenderPeriod.endDate:
         raise_operation_error(
             self.request,
             'tenderPeriod should be extended by {0.days} days'.format(
                 TENDERING_EXTRA_PERIOD))
     if self.request.method in ['PUT', 'PATCH']:
         validate_tender_document_update_not_by_author_or_tender_owner(
             self.request)
     return True
예제 #12
0
 def shouldStartAfter(self):
     if self.endDate:
         return
     tender = get_tender(self)
     lot = self.__parent__
     if tender.status not in [
             'active.tendering', 'active.pre-qualification.stand-still',
             'active.auction'
     ] or lot.status != 'active':
         return
     start_after = None
     if tender.status == 'active.tendering' and tender.tenderPeriod.endDate:
         start_after = calculate_business_date(tender.tenderPeriod.endDate,
                                               TENDERING_AUCTION, tender)
     elif self.startDate and get_now() > calc_auction_end_time(
             lot.numberOfBids, self.startDate):
         start_after = calc_auction_end_time(lot.numberOfBids,
                                             self.startDate)
     elif tender.qualificationPeriod and tender.qualificationPeriod.endDate:
         decision_dates = [
             datetime.combine(
                 complaint.dateDecision.date() + timedelta(days=3),
                 time(0, tzinfo=complaint.dateDecision.tzinfo))
             for qualification in tender.qualifications
             for complaint in qualification.complaints
             if complaint.dateDecision
         ]
         decision_dates.append(tender.qualificationPeriod.endDate)
         start_after = max(decision_dates)
     if start_after:
         return rounding_shouldStartAfter(start_after, tender).isoformat()
 def wrapper():
     return PeriodStartEndRequired({
         "startDate":
         get_now(),
         "endDate":
         calculate_business_date(get_now(), tendering_duration)
     })
def validate_tender_period_extension(request):
    extra_period = request.content_configurator.tendering_period_extra
    tender = request.validated['tender']
    if calculate_business_date(get_now(), extra_period,
                               tender) > tender.tenderPeriod.endDate:
        raise_operation_error(
            request, 'tenderPeriod should be extended by {0.days} days'.format(
                extra_period))
예제 #15
0
 def validate_update_tender(self):
     """ TODO move validators
     This class is inherited from openua package, but validate_update_tender function has different validators (check using working days).
     For now, we have no way to use different validators on methods according to procedure type.
     """
     if self.request.validated['tender_status'] == 'active.tendering' and calculate_business_date(get_now(), TENDERING_EXTRA_PERIOD, self.request.validated['tender'], True) > self.request.validated['tender'].tenderPeriod.endDate:
         raise_operation_error(self.request, 'tenderPeriod should be extended by {0.days} working days'.format(TENDERING_EXTRA_PERIOD))
     return True
 def __call__(self, obj, *args, **kwargs):
     complaintPeriod_class = obj._fields['tenderPeriod']
     normalized_end = calculate_normalized_date(obj.tenderPeriod.endDate,
                                                obj)
     return complaintPeriod_class(
         dict(startDate=obj.tenderPeriod.startDate,
              endDate=calculate_business_date(normalized_end,
                                              -COMPLAINT_SUBMIT_TIME, obj)))
 def complaintPeriod(self):
     normalized_end = calculate_normalized_date(self.tenderPeriod.endDate,
                                                self)
     return Period(
         dict(startDate=self.tenderPeriod.startDate,
              endDate=calculate_business_date(normalized_end,
                                              -COMPLAINT_SUBMIT_TIME,
                                              self)))
예제 #18
0
def tender_init_handler(event):
    """ initialization handler for closeFrameworkAgreementUA tenders """
    tender = event.tender
    config = getAdapter(tender, IContentConfigurator)
    endDate = calculate_business_date(tender.tenderPeriod.endDate,
                                      -config.questions_stand_still, tender)
    tender.enquiryPeriod = EnquiryPeriod(
        dict(startDate=tender.tenderPeriod.startDate,
             endDate=endDate,
             invalidationDate=tender.enquiryPeriod
             and tender.enquiryPeriod.invalidationDate,
             clarificationsUntil=calculate_business_date(
                 endDate, config.enquiry_stand_still, tender, True)))
    now = get_now()
    tender.date = now
    if tender.lots:
        for lot in tender.lots:
            lot.date = now
예제 #19
0
def validate_submit_claim_time(request):
    tender = request.context
    claim_submit_time = request.content_configurator.tender_claim_submit_time
    if get_now() > calculate_business_date(tender.tenderPeriod.endDate,
                                           -claim_submit_time, tender, True):
        raise_operation_error(
            request,
            'Can submit claim not later than {0.days} days before tenderPeriod end'
            .format(claim_submit_time))
예제 #20
0
 def validate_update_tender(self):
     tender = self.request.validated['tender']
     if calculate_business_date(get_now(), TENDERING_EXTRA_PERIOD, tender,
                                True) > tender.tenderPeriod.endDate:
         raise_operation_error(
             self.request,
             'tenderPeriod should be extended by {0.days} working days'.
             format(TENDERING_EXTRA_PERIOD))
     return True
def tender_init_handler(event):
    """ initialization handler for esco tenders """
    tender = event.tender
    endDate = calculate_business_date(tender.tenderPeriod.endDate,
                                      -QUESTIONS_STAND_STILL, tender)
    tender.enquiryPeriod = EnquiryPeriod(
        dict(startDate=tender.tenderPeriod.startDate,
             endDate=endDate,
             invalidationDate=tender.enquiryPeriod
             and tender.enquiryPeriod.invalidationDate,
             clarificationsUntil=calculate_business_date(
                 endDate, ENQUIRY_STAND_STILL_TIME, tender, True)))
    now = get_now()
    tender.date = now
    if tender.lots:
        for lot in tender.lots:
            lot.date = now

    check_submission_method_details(tender)
def validate_patch_tender_tenderPeriod(request):
    source = request.validated['data']
    tender = request.validated['tender_src']
    startDate = tender['tenderPeriod'].get('startDate')
    endDate = source['tenderPeriod'].get('endDate')

    if (startDate and endDate) and \
            calculate_business_date(parse_date(startDate), request.content_configurator.tender_period, tender) > parse_date(endDate):
        raise_operation_error(request,
                              'tenderPeriod should last at least 3 days')
def validate_tender_period_extension(request):
    extra_period = request.content_configurator.tendering_period_extra
    tender = request.validated['tender']
    if calculate_business_date(get_now(), extra_period,
                               tender) > tender.tenderPeriod.endDate:
        request.errors.add(
            'body', 'data',
            'tenderPeriod should be extended by {0.days} days'.format(
                extra_period))
        request.errors.status = 403
        raise error_handler(request.errors)
 def validate_update_tender(self):
      """ TODO move validators
      This class is inherited in openua package, but validate_update_tender function has different validators.
      For now, we have no way to use different validators on methods according to procedure type.
      """
      if self.request.authenticated_role != 'auction' and self.request.validated['tender_status'] not in ['active.tendering', STAGE2_STATUS] or \
         self.request.authenticated_role == 'auction' and self.request.validated['tender_status'] not in ['active.auction', 'active.qualification']:
          raise_operation_error(self.request, 'Can\'t {} document in current ({}) tender status'.format(OPERATIONS.get(self.request.method), self.request.validated['tender_status']))
      if self.request.validated['tender_status'] == 'active.tendering' and calculate_business_date(get_now(), TENDERING_EXTRA_PERIOD, self.request.validated['tender']) > self.request.validated['tender'].tenderPeriod.endDate:
          raise_operation_error(self.request, 'tenderPeriod should be extended by {0.days} days'.format(TENDERING_EXTRA_PERIOD))
      if self.request.method in ['PUT', 'PATCH']:
          validate_tender_document_update_not_by_author_or_tender_owner(self.request)
      return True
예제 #25
0
 def shouldStartAfter(self):
     if self.endDate:
         return
     tender = self.__parent__
     if tender.lots or tender.status not in [
             'active.tendering', 'active.pre-qualification.stand-still',
             'active.auction'
     ]:
         return
     start_after = None
     if tender.status == 'active.tendering' and tender.tenderPeriod.endDate:
         start_after = calculate_business_date(tender.tenderPeriod.endDate,
                                               TENDERING_AUCTION, tender)
     elif self.startDate and get_now() > calc_auction_end_time(
             tender.numberOfBids, self.startDate):
         start_after = calc_auction_end_time(tender.numberOfBids,
                                             self.startDate)
     elif tender.qualificationPeriod and tender.qualificationPeriod.endDate:
         start_after = tender.qualificationPeriod.endDate
     if start_after:
         return rounding_shouldStartAfter(start_after, tender).isoformat()
def check_period_and_items(request, tender):
    agreement_items = tender.agreements[0].items if tender.agreements[
        0].items else []
    agreement_items_ids = {
        calculate_item_identification_tuple(agreement_item)
        for agreement_item in agreement_items
    }
    tender_items_ids = {
        calculate_item_identification_tuple(tender_item)
        for tender_item in tender.items
    }

    if not tender_items_ids.issubset(agreement_items_ids):
        drop_draft_to_unsuccessful(request, tender, AGREEMENT_ITEMS)
        return

    if get_now() > calculate_business_date(
            tender.agreements[0].period.endDate,
            -request.content_configurator.agreement_expired_until, tender):
        drop_draft_to_unsuccessful(request, tender, AGREEMENT_EXPIRED)
    elif tender.agreements[0].period.startDate > tender.date:
        drop_draft_to_unsuccessful(request, tender, AGREEMENT_START_DATE)
예제 #27
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':
            award.complaintPeriod.endDate = calculate_business_date(
                get_now(), STAND_STILL_TIME, tender, True)
            tender.contracts.append(
                type(tender).contracts.model_class({
                    'awardID':
                    award.id,
                    'suppliers':
                    award.suppliers,
                    'value':
                    award.value,
                    'date':
                    get_now(),
                    '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':
            now = get_now()
            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_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
            now = get_now()
            award.complaintPeriod.endDate = now
            cancelled_awards = []
            for i in tender.awards[tender.awards.index(award):]:
                if i.lotID != award.lotID:
                    continue
                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'):
            self.request.errors.add(
                'body', 'data',
                'Can\'t update award in current ({}) status'.format(
                    award_status))
            self.request.errors.status = 403
            raise error_handler(self.request.errors)
        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")}
예제 #28
0
def check_tender_status_on_active_qualification_stand_still(request):

    tender = request.validated['tender']
    config = getAdapter(tender, IContentConfigurator)
    now = get_now()
    active_lots = [lot.id for lot in tender.lots
                   if lot.status == 'active'] if tender.lots else [None]
    if not (tender.awardPeriod and tender.awardPeriod.endDate <= now
            and not any([
                i.status in tender.block_complaint_status
                for a in tender.awards
                for i in a.complaints if a.lotID in active_lots
            ])):
        return
    statuses = set()
    if tender.lots:
        for lot in tender.lots:
            if lot.status != 'active':
                statuses.add(lot.status)
                continue
            active_lot_awards = [
                i for i in tender.awards
                if i.lotID == lot.id and i.status == 'active'
            ]
            if len(active_lot_awards) < config.min_bids_number:
                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'
                statuses.add(lot.status)
                continue
            statuses.add(lot.status)
    else:
        active_awards = [i for i in tender.awards if i.status == 'active']
        if len(active_awards) <= config.min_bids_count:
            statuses.add('unsuccessful')
        else:
            statuses.add('active.awarded')

    if statuses == set(['cancelled']):
        LOGGER.info('Switched tender {} to {}'.format(tender.id, 'cancelled'),
                    extra=context_unpack(
                        request, {'MESSAGE_ID': 'switched_tender_cancelled'}))
        tender.status = 'cancelled'
    elif not 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:
        LOGGER.info('Switched tender {} to {}'.format(tender.id,
                                                      'active.awarded'),
                    extra=context_unpack(
                        request,
                        {'MESSAGE_ID': 'switched_tender_active_awarded'}))
        tender.status = 'active.awarded'
        tender.contractPeriod = {'startDate': now}
        tender.contractPeriod['clarificationsUntil'] = calculate_business_date(
            now, config.clarifications_until_period, tender, False)
        lots = [l for l in tender.get('lots', []) if l.status == 'active']
        if lots:
            for lot in lots:
                agreement_data = generate_agreement_data(request, tender, lot)
                agreement = type(tender).agreements.model_class(agreement_data)
                agreement.__parent__ = tender
                tender.agreements.append(agreement)
        else:
            agreement_data = generate_agreement_data(request, tender)
            agreement = type(tender).agreements.model_class(agreement_data)
            agreement.__parent__ = tender
            tender.agreements.append(agreement)
    def patch(self):
        """Tender Edit (partial)

        For example here is how procuring entity can change number of items to be procured and total Value of a tender:

        .. sourcecode:: http

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

            {
                "data": {
                    "value": {
                        "amount": 600
                    },
                    "itemsToBeProcured": [
                        {
                            "quantity": 6
                        }
                    ]
                }
            }

        And here is the response to be expected:

        .. sourcecode:: http

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

            {
                "data": {
                    "id": "4879d3f8ee2443169b5fbbc9f89fa607",
                    "tenderID": "UA-64e93250be76435397e8c992ed4214d1",
                    "dateModified": "2014-10-27T08:12:34.956Z",
                    "value": {
                        "amount": 600
                    },
                    "itemsToBeProcured": [
                        {
                            "quantity": 6
                        }
                    ]
                }
            }

        """
        tender = self.context
        data = self.request.validated['data']
        if self.request.authenticated_role == 'tender_owner' and self.request.validated['tender_status'] == 'active.tendering':
            if 'tenderPeriod' in data and 'endDate' in data['tenderPeriod']:
                self.request.validated['tender'].tenderPeriod.import_data(data['tenderPeriod'])
                validate_tender_period_extension(self.request)
                self.request.registry.notify(TenderInitializeEvent(self.request.validated['tender']))
                self.request.validated['data']["enquiryPeriod"] = self.request.validated['tender'].enquiryPeriod.serialize()

        apply_patch(self.request, save=False, src=self.request.validated['tender_src'])
        if self.request.authenticated_role == 'chronograph':
            check_status(self.request)
        elif self.request.authenticated_role == 'tender_owner' and tender.status == 'active.tendering':
            tender.invalidate_bids_data()
        elif self.request.authenticated_role == 'tender_owner' and self.request.validated['tender_status'] == 'active.pre-qualification' and tender.status == "active.pre-qualification.stand-still":
            active_lots = [lot.id for lot in tender.lots if lot.status == 'active'] if tender.lots else [None]
            if any([i['status'] in self.request.validated['tender'].block_complaint_status for q in self.request.validated['tender']['qualifications'] for i in q['complaints'] if q['lotID'] in active_lots]):
                raise_operation_error(self.request, 'Can\'t switch to \'active.pre-qualification.stand-still\' before resolve all complaints')
            if all_bids_are_reviewed(self.request):
                normalized_date = calculate_normalized_date(get_now(), tender, True)
                tender.qualificationPeriod.endDate = calculate_business_date(normalized_date, COMPLAINT_STAND_STILL, self.request.validated['tender'])
                tender.check_auction_time()
            else:
                raise_operation_error(self.request, 'Can\'t switch to \'active.pre-qualification.stand-still\' while not all bids are qualified')

        save_tender(self.request)
        self.LOGGER.info('Updated tender {}'.format(tender.id),
                    extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_patch'}))
        return {'data': tender.serialize(tender.status)}
예제 #30
0
 def next_check(self):
     now = get_now()
     checks = []
     if self.status == 'active.enquiries' and self.tenderPeriod.startDate:
         checks.append(self.tenderPeriod.startDate.astimezone(TZ))
     elif self.status == 'active.enquiries' and self.enquiryPeriod.endDate:
         checks.append(self.enquiryPeriod.endDate.astimezone(TZ))
     elif self.status == 'active.tendering' and self.tenderPeriod.endDate:
         checks.append(self.tenderPeriod.endDate.astimezone(TZ))
     elif not self.lots and self.status == 'active.auction' and self.auctionPeriod and self.auctionPeriod.startDate and not self.auctionPeriod.endDate:
         if now < self.auctionPeriod.startDate:
             checks.append(self.auctionPeriod.startDate.astimezone(TZ))
         elif now < calc_auction_end_time(
                 self.numberOfBids,
                 self.auctionPeriod.startDate).astimezone(TZ):
             checks.append(
                 calc_auction_end_time(
                     self.numberOfBids,
                     self.auctionPeriod.startDate).astimezone(TZ))
     elif self.lots and self.status == 'active.auction':
         for lot in self.lots:
             if lot.status != 'active' or not lot.auctionPeriod or not lot.auctionPeriod.startDate or lot.auctionPeriod.endDate:
                 continue
             if now < lot.auctionPeriod.startDate:
                 checks.append(lot.auctionPeriod.startDate.astimezone(TZ))
             elif now < calc_auction_end_time(
                     lot.numberOfBids,
                     lot.auctionPeriod.startDate).astimezone(TZ):
                 checks.append(
                     calc_auction_end_time(
                         lot.numberOfBids,
                         lot.auctionPeriod.startDate).astimezone(TZ))
     elif not self.lots and self.status == 'active.awarded' and not any(
         [i.status in self.block_complaint_status
          for i in self.complaints]) and not any([
              i.status in self.block_complaint_status for a in self.awards
              for i in a.complaints
          ]):
         standStillEnds = [
             a.complaintPeriod.endDate.astimezone(TZ) for a in self.awards
             if a.complaintPeriod.endDate
         ]
         last_award_status = self.awards[-1].status if self.awards else ''
         if standStillEnds and last_award_status == 'unsuccessful':
             checks.append(max(standStillEnds))
     elif self.lots and self.status in [
             'active.qualification', 'active.awarded'
     ] and not any([
             i.status in self.block_complaint_status
             and i.relatedLot is None for i in self.complaints
     ]):
         for lot in self.lots:
             if lot['status'] != 'active':
                 continue
             lot_awards = [i for i in self.awards if i.lotID == lot.id]
             pending_complaints = any([
                 i['status'] in self.block_complaint_status
                 and i.relatedLot == lot.id for i in self.complaints
             ])
             pending_awards_complaints = any([
                 i.status in self.block_complaint_status for a in lot_awards
                 for i in a.complaints
             ])
             standStillEnds = [
                 a.complaintPeriod.endDate.astimezone(TZ)
                 for a in lot_awards if a.complaintPeriod.endDate
             ]
             last_award_status = lot_awards[-1].status if lot_awards else ''
             if not pending_complaints and not pending_awards_complaints and standStillEnds and last_award_status == 'unsuccessful':
                 checks.append(max(standStillEnds))
     if self.status.startswith('active'):
         from openprocurement.tender.core.utils import calculate_business_date
         for complaint in self.complaints:
             if complaint.status == 'answered' and complaint.dateAnswered:
                 checks.append(
                     calculate_business_date(complaint.dateAnswered,
                                             COMPLAINT_STAND_STILL_TIME,
                                             self))
             elif complaint.status == 'pending':
                 checks.append(self.dateModified)
         for award in self.awards:
             if award.status == 'active' and not any(
                 [i.awardID == award.id for i in self.contracts]):
                 checks.append(award.date)
             for complaint in award.complaints:
                 if complaint.status == 'answered' and complaint.dateAnswered:
                     checks.append(
                         calculate_business_date(
                             complaint.dateAnswered,
                             COMPLAINT_STAND_STILL_TIME, self))
                 elif complaint.status == 'pending':
                     checks.append(self.dateModified)
     return min(checks).isoformat() if checks else None
def tender_init_handler_2(event):
    """ initialization handler for tenders """
 #   import pdb; pdb.set_trace()
    tender = event.tender
    tender.tenderPeriod.endDate = calculate_business_date(tender.tenderPeriod.startDate, TENDERING_DURATION_UA, tender)
    tender_init_handler_ua(event)
예제 #32
0
 def patch(self):
     """Post a complaint resolution
     """
     tender = self.request.validated['tender']
     data = self.request.validated['data']
     # complaint_owner
     if self.request.authenticated_role == 'complaint_owner' and self.context.status in [
             'draft', 'claim', 'answered'
     ] and data.get('status', self.context.status) == 'cancelled':
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.dateCanceled = get_now()
     elif self.request.authenticated_role == 'complaint_owner' and self.context.status in [
             'pending', 'accepted'
     ] and data.get('status', self.context.status) == 'stopping':
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.dateCanceled = get_now()
     elif self.request.authenticated_role == 'complaint_owner' and tender.status == 'active.tendering' and self.context.status == 'draft' and data.get(
             'status', self.context.status) == self.context.status:
         apply_patch(self.request, save=False, src=self.context.serialize())
     elif self.request.authenticated_role == 'complaint_owner' and tender.status == 'active.tendering' and self.context.status == 'draft' and data.get(
             'status', self.context.status) == 'claim':
         if get_now() > calculate_business_date(tender.tenderPeriod.endDate,
                                                -CLAIM_SUBMIT_TIME, tender):
             raise_operation_error(
                 self.request,
                 'Can submit claim not later than {0.days} days before tenderPeriod end'
                 .format(CLAIM_SUBMIT_TIME))
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.dateSubmitted = get_now()
     elif self.request.authenticated_role == 'complaint_owner' and tender.status == 'active.tendering' and self.context.status in [
             'draft', 'claim'
     ] and data.get('status', self.context.status) == 'pending':
         if get_now() > tender.complaintPeriod.endDate:
             raise_operation_error(
                 self.request,
                 'Can submit complaint not later than {0.days} days before tenderPeriod end'
                 .format(COMPLAINT_SUBMIT_TIME))
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.type = 'complaint'
         self.context.dateSubmitted = get_now()
     elif self.request.authenticated_role == 'complaint_owner' and self.context.status == 'answered' and data.get(
             'status', self.context.status) == self.context.status:
         apply_patch(self.request, save=False, src=self.context.serialize())
     elif self.request.authenticated_role == 'complaint_owner' and self.context.status == 'answered' and data.get(
             'satisfied', self.context.satisfied) is True and data.get(
                 'status', self.context.status) == 'resolved':
         apply_patch(self.request, save=False, src=self.context.serialize())
     elif self.request.authenticated_role == 'complaint_owner' and self.context.status == 'answered' and data.get(
             'satisfied', self.context.satisfied) is False and data.get(
                 'status', self.context.status) == 'pending':
         if get_now() > tender.complaintPeriod.endDate:
             raise_operation_error(
                 self.request,
                 'Can submit complaint not later than {0.days} days before tenderPeriod end'
                 .format(COMPLAINT_SUBMIT_TIME))
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.type = 'complaint'
         self.context.dateEscalated = get_now()
     # tender_owner
     elif self.request.authenticated_role == 'tender_owner' and self.context.status == 'claim' and data.get(
             'status', self.context.status) == self.context.status:
         now = get_now()
         if now > tender.enquiryPeriod.clarificationsUntil:
             raise_operation_error(
                 self.request,
                 'Can update claim only before enquiryPeriod.clarificationsUntil'
             )
         apply_patch(self.request, save=False, src=self.context.serialize())
     elif self.request.authenticated_role == 'tender_owner' and self.context.status == 'satisfied' and data.get(
             'status', self.context.status) == self.context.status:
         apply_patch(self.request, save=False, src=self.context.serialize())
     elif self.request.authenticated_role == 'tender_owner' and self.context.status == 'claim' and data.get(
             'resolution', self.context.resolution) and data.get(
                 'resolutionType',
                 self.context.resolutionType) and data.get(
                     'status', self.context.status) == 'answered':
         now = get_now()
         if now > tender.enquiryPeriod.clarificationsUntil:
             raise_operation_error(
                 self.request,
                 'Can update claim only before enquiryPeriod.clarificationsUntil'
             )
         if len(data.get('resolution', self.context.resolution)) < 20:
             raise_operation_error(
                 self.request,
                 'Can\'t update complaint: resolution too short')
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.dateAnswered = get_now()
     elif self.request.authenticated_role == 'tender_owner' and self.context.status in [
             'pending', 'accepted'
     ]:
         apply_patch(self.request, save=False, src=self.context.serialize())
     elif self.request.authenticated_role == 'tender_owner' and self.context.status == 'satisfied' and data.get(
             'tendererAction', self.context.tendererAction) and data.get(
                 'status', self.context.status) == 'resolved':
         apply_patch(self.request, save=False, src=self.context.serialize())
     # aboveThresholdReviewers
     elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status in [
             'pending', 'accepted', 'stopping'
     ] and data.get('status', self.context.status) == self.context.status:
         apply_patch(self.request, save=False, src=self.context.serialize())
     elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status in [
             'pending', 'stopping'
     ] and data.get('status',
                    self.context.status) in ['invalid', 'mistaken']:
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.dateDecision = get_now()
         self.context.acceptance = False
     elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status == 'pending' and data.get(
             'status', self.context.status) == 'accepted':
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.dateAccepted = get_now()
         self.context.acceptance = True
     elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status in [
             'accepted', 'stopping'
     ] and data.get('status',
                    self.context.status) in ['declined', 'satisfied']:
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.dateDecision = get_now()
     elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status in [
             'pending', 'accepted', 'stopping'
     ] and data.get('status', self.context.status) == 'stopped':
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.dateDecision = get_now()
         self.context.dateCanceled = self.context.dateCanceled or get_now()
     else:
         raise_operation_error(self.request, 'Can\'t update complaint')
     if self.context.tendererAction and not self.context.tendererActionDate:
         self.context.tendererActionDate = get_now()
     if self.context.status not in [
             'draft', 'claim', 'answered', 'pending', 'accepted',
             'satisfied', 'stopping'
     ] and tender.status in ['active.qualification', 'active.awarded']:
         check_tender_status(self.request)
     if save_tender(self.request):
         self.LOGGER.info(
             'Updated tender complaint {}'.format(self.context.id),
             extra=context_unpack(self.request,
                                  {'MESSAGE_ID': 'tender_complaint_patch'}))
         return {'data': self.context.serialize("view")}
 def patch(self):
     """Post a complaint resolution
     """
     tender = self.request.validated['tender']
     data = self.request.validated['data']
     # complaint_owner
     if self.request.authenticated_role == 'complaint_owner' and self.context.status in ['draft', 'claim', 'answered'] and data.get('status', self.context.status) == 'cancelled':
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.dateCanceled = get_now()
     elif self.request.authenticated_role == 'complaint_owner' and self.context.status in ['pending', 'accepted'] and data.get('status', self.context.status) == 'stopping':
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.dateCanceled = get_now()
     elif self.request.authenticated_role == 'complaint_owner' and tender.status == 'active.tendering' and self.context.status == 'draft' and data.get('status', self.context.status) == self.context.status:
         apply_patch(self.request, save=False, src=self.context.serialize())
     elif self.request.authenticated_role == 'complaint_owner' and tender.status == 'active.tendering' and self.context.status == 'draft' and data.get('status', self.context.status) == 'claim':
         if get_now() > calculate_business_date(tender.tenderPeriod.endDate, -CLAIM_SUBMIT_TIME, tender, True):
             raise_operation_error(self.request, 'Can submit claim not later than {0.days} days before tenderPeriod end'.format(CLAIM_SUBMIT_TIME))
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.dateSubmitted = get_now()
     elif self.request.authenticated_role == 'complaint_owner' and tender.status == 'active.tendering' and self.context.status in ['draft', 'claim'] and data.get('status', self.context.status) == 'pending':
         if get_now() > tender.complaintPeriod.endDate:
             raise_operation_error(self.request, 'Can submit complaint not later than {0.days} days before tenderPeriod end'.format(COMPLAINT_SUBMIT_TIME))
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.type = 'complaint'
         self.context.dateSubmitted = get_now()
     elif self.request.authenticated_role == 'complaint_owner' and self.context.status == 'answered' and data.get('status', self.context.status) == self.context.status:
         apply_patch(self.request, save=False, src=self.context.serialize())
     elif self.request.authenticated_role == 'complaint_owner' and self.context.status == 'answered' and data.get('satisfied', self.context.satisfied) is True and data.get('status', self.context.status) == 'resolved':
         apply_patch(self.request, save=False, src=self.context.serialize())
     elif self.request.authenticated_role == 'complaint_owner' and self.context.status == 'answered' and data.get('satisfied', self.context.satisfied) is False and data.get('status', self.context.status) == 'pending':
         if get_now() > tender.complaintPeriod.endDate:
             raise_operation_error(self.request, 'Can submit complaint not later than {0.days} days before tenderPeriod end'.format(COMPLAINT_SUBMIT_TIME))
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.type = 'complaint'
         self.context.dateEscalated = get_now()
     # tender_owner
     elif self.request.authenticated_role == 'tender_owner' and self.context.status == 'claim' and data.get('status', self.context.status) == self.context.status:
         now = get_now()
         if now > tender.enquiryPeriod.clarificationsUntil:
             raise_operation_error(self.request, 'Can update claim only before enquiryPeriod.clarificationsUntil')
         apply_patch(self.request, save=False, src=self.context.serialize())
     elif self.request.authenticated_role == 'tender_owner' and self.context.status == 'satisfied' and data.get('status', self.context.status) == self.context.status:
         apply_patch(self.request, save=False, src=self.context.serialize())
     elif self.request.authenticated_role == 'tender_owner' and self.context.status == 'claim' and data.get('resolution', self.context.resolution) and data.get('resolutionType', self.context.resolutionType) and data.get('status', self.context.status) == 'answered':
         now = get_now()
         if now > tender.enquiryPeriod.clarificationsUntil:
             raise_operation_error(self.request, 'Can update claim only before enquiryPeriod.clarificationsUntil')
         if len(data.get('resolution', self.context.resolution)) < 20:
             raise_operation_error(self.request, 'Can\'t update complaint: resolution too short')
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.dateAnswered = get_now()
     elif self.request.authenticated_role == 'tender_owner' and self.context.status in ['pending', 'accepted']:
         apply_patch(self.request, save=False, src=self.context.serialize())
     elif self.request.authenticated_role == 'tender_owner' and self.context.status == 'satisfied' and data.get('tendererAction', self.context.tendererAction) and data.get('status', self.context.status) == 'resolved':
         apply_patch(self.request, save=False, src=self.context.serialize())
     # aboveThresholdReviewers
     elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status in ['pending', 'accepted', 'stopping'] and data.get('status', self.context.status) == self.context.status:
         apply_patch(self.request, save=False, src=self.context.serialize())
     elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status in ['pending', 'stopping'] and data.get('status', self.context.status) in ['invalid', 'mistaken']:
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.dateDecision = get_now()
         self.context.acceptance = False
     elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status == 'pending' and data.get('status', self.context.status) == 'accepted':
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.dateAccepted = get_now()
         self.context.acceptance = True
     elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status in ['accepted', 'stopping'] and data.get('status', self.context.status) in ['declined', 'satisfied']:
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.dateDecision = get_now()
     elif self.request.authenticated_role == 'aboveThresholdReviewers' and self.context.status in ['pending', 'accepted', 'stopping'] and data.get('status', self.context.status) == 'stopped':
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.dateDecision = get_now()
         self.context.dateCanceled = self.context.dateCanceled or get_now()
     else:
         raise_operation_error(self.request, 'Can\'t update complaint')
     if self.context.tendererAction and not self.context.tendererActionDate:
         self.context.tendererActionDate = get_now()
     if self.context.status not in ['draft', 'claim', 'answered', 'pending', 'accepted', 'stopping'] and tender.status in ['active.qualification', 'active.awarded']:
         check_tender_status(self.request)
     if save_tender(self.request):
         self.LOGGER.info('Updated tender complaint {}'.format(self.context.id),
                     extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_complaint_patch'}))
         return {'data': self.context.serialize("view")}
def validate_tender_period_extension_with_working_days(request):
    tender = request.context
    extra_period = request.content_configurator.tendering_period_extra
    if calculate_business_date(get_now(), extra_period, tender, True) > request.validated['tender'].tenderPeriod.endDate:
        raise_operation_error(request,'tenderPeriod should be extended by {0.days} working days'.format(extra_period))
 def validate_update_tender(self):
     tender = self.request.validated['tender']
     if calculate_business_date(get_now(), TENDERING_EXTRA_PERIOD, tender, True) > tender.tenderPeriod.endDate:
         raise_operation_error(self.request, 'tenderPeriod should be extended by {0.days} working days'.format(TENDERING_EXTRA_PERIOD))
     return True
 def wrapper():
     return PeriodStartEndRequired({"startDate": get_now(),
                                    "endDate": calculate_business_date(get_now(), tendering_duration)})
def validate_submit_claim_time(request):
    tender = request.context
    claim_submit_time = request.content_configurator.tender_claim_submit_time
    if get_now() > calculate_business_date(tender.tenderPeriod.endDate, -claim_submit_time, tender, True):
        raise_operation_error(request,'Can submit claim not later than {0.days} days before tenderPeriod end'.format(claim_submit_time))
    def patch(self):
        """Tender Edit (partial)

        For example here is how procuring entity can change number of items to be procured and total Value of a tender:

        .. sourcecode:: http

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

            {
                "data": {
                    "value": {
                        "amount": 600
                    },
                    "itemsToBeProcured": [
                        {
                            "quantity": 6
                        }
                    ]
                }
            }

        And here is the response to be expected:

        .. sourcecode:: http

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

            {
                "data": {
                    "id": "4879d3f8ee2443169b5fbbc9f89fa607",
                    "tenderID": "UA-64e93250be76435397e8c992ed4214d1",
                    "dateModified": "2014-10-27T08:12:34.956Z",
                    "value": {
                        "amount": 600
                    },
                    "itemsToBeProcured": [
                        {
                            "quantity": 6
                        }
                    ]
                }
            }

        """
        tender = self.context
        data = self.request.validated['data']
        if self.request.authenticated_role == 'tender_owner' and \
                self.request.validated['tender_status'] in ['active.tendering', STAGE2_STATUS]:
            if 'tenderPeriod' in data and 'endDate' in data['tenderPeriod']:
                self.request.validated['tender'].tenderPeriod.import_data(data['tenderPeriod'])
                validate_tender_period_extension(self.request)
                self.request.registry.notify(TenderInitializeEvent(self.request.validated['tender']))
                self.request.validated['data']["enquiryPeriod"] = self.request.validated['tender'].enquiryPeriod.serialize()

        apply_patch(self.request, save=False, src=self.request.validated['tender_src'])
        if self.request.authenticated_role == 'chronograph':
            check_status_eu(self.request)
        elif self.request.authenticated_role == 'tender_owner' and tender.status == 'active.tendering':
            tender.invalidate_bids_data()
        elif self.request.authenticated_role == 'tender_owner' and \
                self.request.validated['tender_status'] == 'active.pre-qualification' and \
                tender.status == "active.pre-qualification.stand-still":
            if all_bids_are_reviewed(self.request):
                tender.qualificationPeriod.endDate = calculate_business_date(get_now(), COMPLAINT_STAND_STILL,
                                                                             self.request.validated['tender'])
                tender.check_auction_time()
            else:
                raise_operation_error(self.request, 'Can\'t switch to \'active.pre-qualification.stand-still\' while not all bids are qualified')

        save_tender(self.request)
        self.LOGGER.info('Updated tender {}'.format(tender.id),
                         extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_patch'}))
        return {'data': tender.serialize(tender.status)}