def initialize(self): if not self.enquiryPeriod: self.enquiryPeriod = type(self).enquiryPeriod.model_class() if not self.tenderPeriod: self.tenderPeriod = type(self).tenderPeriod.model_class() now = get_now() self.tenderPeriod.startDate = self.enquiryPeriod.startDate = now pause_between_periods = self.auctionPeriod.startDate - ( self.auctionPeriod.startDate.replace( hour=20, minute=0, second=0, microsecond=0) - timedelta(days=1)) self.enquiryPeriod.endDate = calculate_business_date( self.auctionPeriod.startDate, -pause_between_periods, self).astimezone(TZ) time_before_tendering_end = (self.auctionPeriod.startDate.replace( hour=9, minute=30, second=0, microsecond=0) + DUTCH_PERIOD) - self.enquiryPeriod.endDate self.tenderPeriod.endDate = calculate_business_date( self.enquiryPeriod.endDate, time_before_tendering_end, self) if SANDBOX_MODE and self.submissionMethodDetails and 'quick' in self.submissionMethodDetails: self.tenderPeriod.endDate = (self.enquiryPeriod.endDate + QUICK_DUTCH_PERIOD).astimezone(TZ) self.auctionPeriod.startDate = None self.auctionPeriod.endDate = None self.date = now self.documents.append( type(self).documents.model_class(DGF_PLATFORM_LEGAL_DETAILS))
def switch_to_next_award(request): auction = request.validated['auction'] now = get_now() waiting_awards = [ i for i in auction.awards if i['status'] == 'pending.waiting' ] if waiting_awards: award = waiting_awards[0] award.status = 'pending.verification' award.verificationPeriod = {'startDate': now} award.verificationPeriod.endDate = calculate_business_date( now, VERIFY_AUCTION_PROTOCOL_TIME, auction, True) award.paymentPeriod = {'startDate': now} award.paymentPeriod.endDate = calculate_business_date( now, AWARD_PAYMENT_TIME, auction, True) award.signingPeriod = {'startDate': now} award.signingPeriod.endDate = calculate_business_date( now, CONTRACT_SIGNING_TIME, auction, True) award.complaintPeriod.endDate = award.signingPeriod.endDate = calculate_business_date( now, CONTRACT_SIGNING_TIME, auction, True) request.response.headers['Location'] = request.route_url( '{}:Auction Awards'.format(auction.procurementMethodType), auction_id=auction.id, award_id=award['id']) elif all([ award.status in ['cancelled', 'unsuccessful'] for award in auction.awards ]): auction.awardPeriod.endDate = now auction.status = 'unsuccessful'
def calculate_enddate(auction, period, duration): period.endDate = calculate_business_date(period.startDate, duration, auction, True) round_to_18_hour_delta = period.endDate.replace( hour=18, minute=0, second=0) - period.endDate period.endDate = calculate_business_date(period.endDate, round_to_18_hour_delta, auction, False)
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.auction) < 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.auction) < now: complaint.status = complaint.resolutionType
def award_paymentPeriod(self): period = self.paymentPeriod if not period: return if not period.endDate: auction = get_auction(self) period.endDate = calculate_business_date(period.startDate, AWARD_PAYMENT_TIME, auction, True) round_to_18_hour_delta = period.endDate.replace( hour=18, minute=0, second=0) - period.endDate period.endDate = calculate_business_date(period.endDate, round_to_18_hour_delta, auction, False) return period.to_primitive()
def award_signingPeriod(self): period = self.signingPeriod if not period: return if not period.endDate: auction = get_auction(self) period.endDate = calculate_business_date(period.startDate, CONTRACT_SIGNING_TIME, auction, True) round_to_18_hour_delta = period.endDate.replace( hour=18, minute=0, second=0) - period.endDate period.endDate = calculate_business_date(period.endDate, round_to_18_hour_delta, auction, False) return period.to_primitive()
def award_verificationPeriod(self): period = self.verificationPeriod if not period: return if not period.endDate: auction = get_auction(self) period.endDate = calculate_business_date( period.startDate, VERIFY_AUCTION_PROTOCOL_TIME, auction, True) round_to_18_hour_delta = period.endDate.replace( hour=18, minute=0, second=0) - period.endDate period.endDate = calculate_business_date(period.endDate, round_to_18_hour_delta, auction, False) return period.to_primitive()
def test_apply_long(self): contract, prolongation = self.fixture_created() pre_prolongation_contract_signingPeriod = contract.signingPeriod target_signingPeriod_endDate = calculate_business_date( contract.signingPeriod.startDate, PROLONGATION_LONG_PERIOD, contract.__parent__, working_days=True, specific_hour=CONTRACT_SIGNING_PERIOD_END_DATE_HOUR) previous_short_prolongation = self.fixture_created()[0] previous_short_prolongation.status = 'applied' contract.prolongations.append(previous_short_prolongation) managed_prolongation = ProlongationManager(prolongation) managed_prolongation.apply() self.assertEqual(previous_short_prolongation.status, 'applied') self.assertEqual(prolongation.status, 'applied') # check update of startDate self.assertEqual(contract.signingPeriod.startDate, pre_prolongation_contract_signingPeriod.startDate, 'startDate of Contract is incorrect') # check update of endDate self.assertEqual( contract.signingPeriod.endDate, target_signingPeriod_endDate, 'endDate of Contract is incorrect', )
def test_patch_contract_status_active_payment(self): contract = create_contract(self) response = patch_contract( self, contract, {'data': { 'status': 'active.payment' }}, ) self.assertEqual(response.status, '200 OK') response_data = response.json['data'] self.assertEqual(response_data['status'], 'active.payment') self.assertTrue(isinstance(response_data.get('milestones'), list), "Milestones weren't created") financial_milestone = response_data['milestones'][0] contract = Contract(contract_create_data) target_dueDate = calculate_business_date( contract.dateSigned, MILESTONE_FINANCING_DUEDATE_OFFSET, context=None, working_days=False, specific_hour=18, result_is_working_day=True) self.assertEqual( financial_milestone['dueDate'], target_dueDate.isoformat(), "dueDate of financial milestone wasn't calculated right")
def test_patch_contract_status_active_payment(self): contract = create_contract(self) contract_id = contract.data.id response = self.app.patch_json( ENDPOINTS['contracts'].format(contract_id=contract_id) + "?acc_token={}".format(contract.access.token), {'data': { 'status': 'active.payment' }}, ) self.assertEqual(response.status, '200 OK') response_data = response.json['data'] self.assertEqual(response_data['status'], 'active.payment') self.assertTrue(isinstance(response_data.get('milestones'), list), "Milestones weren't created") financial_milestone = response_data['milestones'][0] contract = Contract(contract_create_data) target_dueDate = calculate_business_date( contract.dateSigned, MILESTONE_FINANCING_DUEDATE_OFFSET, context=None, working_days=True, specific_hour=18) self.assertEqual( financial_milestone['dueDate'], target_dueDate.isoformat(), "dueDate of financial milestone wasn't calculated right")
def wrapper(): return PeriodStartEndRequired({ "startDate": get_now(), "endDate": calculate_business_date(get_now(), tendering_duration) })
def validate_tenderPeriod(self, data, period): """Auction start date must be not closer than MINIMAL_EXPOSITION_PERIOD days and not a holiday""" if not (period and period.startDate and period.endDate): return if get_auction_creation_date(data) < MINIMAL_EXPOSITION_REQUIRED_FROM: return if calculate_business_date(period.startDate, MINIMAL_EXPOSITION_PERIOD, data) > period.endDate: raise ValidationError(u"tenderPeriod should be greater than 6 days")
def generate_rectificationPeriod(auction): now = get_now() if not auction.rectificationPeriod: period = type(auction).rectificationPeriod.model_class() period.startDate = period.startDate or now if not period.endDate: calculated_endDate = calculate_business_date(auction.tenderPeriod.endDate, -MINIMAL_PERIOD_FROM_RECTIFICATION_END, auction) period.endDate = calculated_endDate if calculated_endDate > now else now period.invalidationDate = None return period
def set_dueDate(self, milestone, contract): """Sets dueDate of the Milestone Also takes into account milestone's type, so this method can be used on any milestone of Ceasefire contracting. :param milestone: milestone to work with :param contract: contract, related to milestone :type milestone: openprocurement.contracting.ceasefire.models.Milestone :type start_date: openprocurement.contracting.ceasefire.models.Contract :return: dueDate of milestone :rtype: datetime.datetime """ if milestone.type_ == 'financing': milestone.dueDate = calculate_business_date( contract.dateSigned, MILESTONE_FINANCING_DUEDATE_OFFSET, context=contract, working_days=False, specific_hour=18, result_is_working_day=True) elif milestone.type_ == 'approval': financing_milestone = search_list_with_dicts( contract.milestones, 'type_', 'financing') milestone.dueDate = calculate_business_date( financing_milestone.dateMet, MILESTONE_APPROVAL_DUEDATE_OFFSET, context=contract, working_days=False, specific_hour=18, result_is_working_day=True) elif milestone.type_ == 'reporting' and milestone.dueDate is None: approval_milestone = search_list_with_dicts( contract.milestones, 'type_', 'approval') milestone.dueDate = datetime.combine( date( approval_milestone.dateMet.year + MILESTONE_REPORTING_DUEDATE_OFFSET_YEARS, approval_milestone.dateMet.month, approval_milestone.dateMet.day), approval_milestone.dateMet.time())
def award_signingPeriod(self): period = self.signingPeriod if not period: return if not period.endDate: auction = get_auction(self) period.endDate = calculate_business_date( period.startDate, CONTRACT_SIGNING_TIME, auction, True, AWARDING_PERIODS_END_DATE_HOUR) return period.to_primitive()
def award_verificationPeriod(self): period = self.verificationPeriod if not period: return if not period.endDate: auction = get_auction(self) period.endDate = calculate_business_date( period.startDate, VERIFY_AUCTION_PROTOCOL_TIME, auction, True, AWARDING_PERIODS_END_DATE_HOUR) return period.to_primitive()
def tender_Period(self): if self.tenderPeriod and self.auctionPeriod.startDate: end_date = calculate_business_date(self.auctionPeriod.startDate, DUTCH_PERIOD, self) if SANDBOX_MODE and self.submissionMethodDetails and 'quick' in self.submissionMethodDetails: end_date = self.auctionPeriod.startDate + QUICK_DUTCH_PERIOD if self.auctionPeriod.endDate and self.auctionPeriod.endDate <= self.tenderPeriod.endDate: end_date = self.auctionPeriod.endDate.astimezone(TZ) self.tenderPeriod.endDate = end_date return self.tenderPeriod
def apply_prolongation_short(test_case): pre_prolongation_contract = get_related_contract(test_case) add_document_to_prolongation( test_case, test_case.auction_id, test_case.contract_id, test_case.prolongation_id, ) patch_data = { 'data': { 'status': 'applied', } } patch_prolongation_response = test_case.app.patch_json( PATHS['prolongation'].format( auction_id=test_case.auction_id, contract_id=test_case.contract_id, prolongation_id=test_case.prolongation_id, token=test_case.auction_token ), patch_data ) test_case.assertEqual( patch_prolongation_response.status, '200 OK' ) retrieved_prolongation = Prolongation( patch_prolongation_response.json['data'] ) retrieved_prolongation.validate() test_case.assertEqual( retrieved_prolongation.status, 'applied' ) add_document_to_prolongation( test_case, test_case.auction_id, test_case.contract_id, test_case.prolongation_id, ) post_prolongation_contract = get_related_contract(test_case) auction = get_auction(test_case) contract_signing_period_end_date = calculate_business_date( pre_prolongation_contract.signingPeriod.startDate, PROLONGATION_SHORT_PERIOD, context=auction, working_days=True, specific_hour=CONTRACT_SIGNING_PERIOD_END_DATE_HOUR ) test_case.assertEqual( post_prolongation_contract.signingPeriod.endDate, contract_signing_period_end_date )
def award_signingPeriod(self): period = self.signingPeriod if not period: return if not period.endDate: auction = get_auction(self) period.endDate = calculate_business_date( start=period.startDate, context=auction, **self.SIGNING_PERIOD_PARAMS) return period.to_primitive()
def award_verificationPeriod(self): period = self.verificationPeriod if not period: return if not period.endDate: auction = get_auction(self) period.endDate = calculate_business_date( start=period.startDate, context=auction, **self.VERIFICATION_PERIOD_PARAMS) return period.to_primitive()
def _start_awarding(self): """ Function create NUMBER_OF_BIDS_TO_BE_QUALIFIED awards objects First award always in pending.verification status others in pending.waiting status In case that only one bid was applied, award object in pending.admission status will be created for that bid """ auction = self.context auction.status = 'active.qualification' now = get_now() auction.awardPeriod = type(auction).awardPeriod({'startDate': now}) awarding_type = self.awarding_type valid_bids = [bid for bid in auction.bids if self.is_bid_valid(bid)] award_status = 'pending.admission' if len( valid_bids ) == 1 and self.pending_admission_for_one_bid else 'pending' bids = chef(valid_bids, auction.features or [], [], True) bids_to_qualify = self.get_bids_to_qualify(bids) for bid, status in izip_longest(bids[:bids_to_qualify], [award_status], fillvalue='pending.waiting'): bid = bid.serialize() award = make_award(self.request, auction, bid, status, now, parent=True) if bid['status'] == 'invalid': set_award_status_unsuccessful(award, now) if award.status == 'pending': award.verificationPeriod = self.verificationPeriod() award.signingPeriod = self.signingPeriod() add_award_route_url(self.request, auction, award, awarding_type) if award.status == 'pending.admission': award.admissionPeriod = { 'startDate': now, 'endDate': calculate_business_date(start=now, context=auction, **award.ADMISSION_PERIOD_PARAMS) } add_award_route_url(self.request, auction, award, awarding_type) auction.awards.append(award) return True
def next_check(self): now = get_now() checks = [] if self.status == 'active.tendering' and self.tenderPeriod 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)) # Use next_check part from awarding request = get_request_from_root(self) if request is not None: awarding_check = request.registry.getAdapter(self, IAwardingNextCheck).add_awarding_checks(self) if awarding_check is not None: checks.append(awarding_check) if self.status.startswith('active'): from openprocurement.api.utils import calculate_business_date for complaint in self.complaints: if complaint.status == 'claim' and complaint.dateSubmitted: checks.append(calculate_business_date(complaint.dateSubmitted, COMPLAINT_STAND_STILL_TIME, self)) elif complaint.status == 'answered' and complaint.dateAnswered: checks.append(calculate_business_date(complaint.dateAnswered, COMPLAINT_STAND_STILL_TIME, self)) for award in self.awards: for complaint in award.complaints: if complaint.status == 'claim' and complaint.dateSubmitted: checks.append(calculate_business_date(complaint.dateSubmitted, COMPLAINT_STAND_STILL_TIME, self)) elif complaint.status == 'answered' and complaint.dateAnswered: checks.append(calculate_business_date(complaint.dateAnswered, COMPLAINT_STAND_STILL_TIME, self)) return min(checks).isoformat() if checks else None
def create_awards(request): auction = request.validated['auction'] auction.status = 'active.qualification' now = get_now() auction.awardPeriod = type(auction).awardPeriod({'startDate': now}) bids = chef(auction.bids, auction.features or [], [], True) for i, status in enumerate(['pending.verification', 'pending.waiting']): bid = bids[i].serialize() award = type(auction).awards.model_class({ 'bid_id': bid['id'], 'status': status, 'date': now, 'value': bid['value'], 'suppliers': bid['tenderers'], 'complaintPeriod': { 'startDate': now } }) if bid['status'] == 'invalid': award.status = 'unsuccessful' award.complaintPeriod.endDate = now if award.status == 'pending.verification': award.verificationPeriod = {'startDate': now} award.verificationPeriod.endDate = calculate_business_date( now, VERIFY_AUCTION_PROTOCOL_TIME, auction, True) award.paymentPeriod = {'startDate': now} award.paymentPeriod.endDate = calculate_business_date( now, AWARD_PAYMENT_TIME, auction, True) award.signingPeriod = {'startDate': now} award.complaintPeriod.endDate = award.signingPeriod.endDate = calculate_business_date( now, CONTRACT_SIGNING_TIME, auction, True) request.response.headers['Location'] = request.route_url( '{}:Auction Awards'.format(auction.procurementMethodType), auction_id=auction.id, award_id=award['id']) auction.awards.append(award)
def create_awards(request): """ Function create NUMBER_OF_BIDS_TO_BE_QUALIFIED awards objects First award always in pending.verification status others in pending.waiting status In case that only one bid was applied, award object in pending.admission status will be created for that bid """ auction = request.validated['auction'] auction.status = 'active.qualification' now = get_now() auction.awardPeriod = type(auction).awardPeriod({'startDate': now}) awarding_type = request.content_configurator.awarding_type valid_bids = [bid for bid in auction.bids if bid['value'] is not None] if len(valid_bids) == 1: bid = valid_bids[0].serialize() award = make_award(request, auction, bid, 'pending.admission', now, parent=True) if bid['status'] == 'invalid': set_award_status_unsuccessful(award, now) if award.status == 'pending.admission': award.admissionPeriod = { 'startDate': now, 'endDate': calculate_business_date(now, VERIFY_ADMISSION_PROTOCOL_TIME, auction, True, AWARDING_PERIODS_END_DATE_HOUR) } add_award_route_url(request, auction, award, awarding_type) auction.awards.append(award) else: bids = chef(valid_bids, auction.features or [], [], True) bids_to_qualify = get_bids_to_qualify(bids) for bid, status in izip_longest(bids[:bids_to_qualify], ['pending'], fillvalue='pending.waiting'): bid = bid.serialize() award = make_award(request, auction, bid, status, now, parent=True) if bid['status'] == 'invalid': set_award_status_unsuccessful(award, now) if award.status == 'pending': award.signingPeriod = award.verificationPeriod = { 'startDate': now } add_award_route_url(request, auction, award, awarding_type) auction.awards.append(award)
def initialize(self): if not self.enquiryPeriod: self.enquiryPeriod = type(self).enquiryPeriod.model_class() if not self.tenderPeriod: self.tenderPeriod = type(self).tenderPeriod.model_class() now = get_now() self.tenderPeriod.startDate = self.enquiryPeriod.startDate = now pause_between_periods = self.auctionPeriod.startDate - (self.auctionPeriod.startDate.replace(hour=20, minute=0, second=0, microsecond=0) - timedelta(days=1)) self.enquiryPeriod.endDate = self.tenderPeriod.endDate = calculate_business_date(self.auctionPeriod.startDate, -pause_between_periods, self) self.auctionPeriod.startDate = None self.auctionPeriod.endDate = None self.date = now if self.lots: for lot in self.lots: lot.date = now
def initialize(self): if not self.enquiryPeriod: self.enquiryPeriod = type(self).enquiryPeriod.model_class() if not self.tenderPeriod: self.tenderPeriod = type(self).tenderPeriod.model_class() now = get_now() self.tenderPeriod.startDate = self.enquiryPeriod.startDate = now pause_between_periods = self.auctionPeriod.startDate - (self.auctionPeriod.startDate.replace(hour=20, minute=0, second=0, microsecond=0) - timedelta(days=1)) self.enquiryPeriod.endDate = self.tenderPeriod.endDate = calculate_business_date(self.auctionPeriod.startDate, -pause_between_periods, self) self.auctionPeriod.startDate = None self.auctionPeriod.endDate = None self.date = now if self.lots: for lot in self.lots: lot.date = now self.documents.append(type(self).documents.model_class(DGF_PLATFORM_LEGAL_DETAILS))
def validate_datePublished(self, data, datePublished): """Check if datePublished attribute is valid datePublished must be non less than `dateCreated` on some limit. """ if not data.get('datePublished'): return offset_from_date_created = calculate_business_date( datePublished, PROLONGATION_DATE_PUBLISHED_LIMIT_PERIOD, None) if offset_from_date_created < data['dateCreated']: raise ValidationError( 'datePublished must be no less on {limit} days, ' 'than dateCreated'.format( limit=PROLONGATION_DATE_PUBLISHED_LIMIT_PERIOD.days))
def test_set_dueDate_financing(self): self.prepare_mocked_contract() manager = CeasefireMilestoneManager(Mock()) milestone_mock = Mock() milestone_mock.type_ = 'financing' self.contract.dateSigned = datetime.now() target_dueDate = calculate_business_date( self.contract.dateSigned, MILESTONE_FINANCING_DUEDATE_OFFSET, context=None, working_days=True, specific_hour=18) manager.set_dueDate(milestone_mock, self.contract) self.assertEqual(milestone_mock.dueDate, target_dueDate, 'dueDate has been calculated incorrectly')
def validate_update_tender(self, operation): tender = self.request.validated['tender'] if tender.status not in ['active.tendering']: self.request.errors.add( 'body', 'data', 'Can\'t {} lot in current ({}) tender status'.format( operation, tender.status)) self.request.errors.status = 403 return if calculate_business_date(get_now(), TENDERING_EXTRA_PERIOD, tender) > tender.tenderPeriod.endDate: self.request.errors.add( 'body', 'data', 'tenderPeriod should be extended by {0.days} days'.format( TENDERING_EXTRA_PERIOD)) self.request.errors.status = 403 return return True
def test_set_dueDate_financing_with_accelerator(self): self.prepare_mocked_contract() manager = CeasefireMilestoneManager(Mock()) milestone_mock = Mock() milestone_mock.type_ = 'financing' if SANDBOX_MODE: self.contract.sandbox_parameters = 'quick, accelerator=1440' self.contract.dateSigned = datetime.now() target_dueDate = calculate_business_date( self.contract.dateSigned, MILESTONE_FINANCING_DUEDATE_OFFSET, context=self.contract, working_days=True, specific_hour=18) manager.set_dueDate(milestone_mock, self.contract) self.assertEqual(milestone_mock.dueDate, target_dueDate, 'dueDate has been calculated incorrectly')
def initialize(self): if not self.enquiryPeriod: self.enquiryPeriod = type(self).enquiryPeriod.model_class() if not self.tenderPeriod: self.tenderPeriod = type(self).tenderPeriod.model_class() now = get_now() start_date = TZ.localize(self.auctionPeriod.startDate.replace(tzinfo=None)) self.tenderPeriod.startDate = self.enquiryPeriod.startDate = now pause_between_periods = start_date - (start_date.replace(hour=20, minute=0, second=0, microsecond=0) - timedelta(days=1)) end_date = calculate_business_date(start_date, -pause_between_periods, self) self.enquiryPeriod.endDate = end_date self.tenderPeriod.endDate = self.enquiryPeriod.endDate if not self.rectificationPeriod: self.rectificationPeriod = generate_rectificationPeriod(self) self.rectificationPeriod.startDate = now self.auctionPeriod.startDate = None self.auctionPeriod.endDate = None self.date = now if self.lots: for lot in self.lots: lot.date = now
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.api.utils import calculate_business_date for complaint in self.complaints: if complaint.status == 'claim' and complaint.dateSubmitted: checks.append(calculate_business_date(complaint.dateSubmitted, COMPLAINT_STAND_STILL_TIME, self)) elif complaint.status == 'answered' and complaint.dateAnswered: checks.append(calculate_business_date(complaint.dateAnswered, COMPLAINT_STAND_STILL_TIME, self)) for award in self.awards: for complaint in award.complaints: if complaint.status == 'claim' and complaint.dateSubmitted: checks.append(calculate_business_date(complaint.dateSubmitted, COMPLAINT_STAND_STILL_TIME, self)) elif complaint.status == 'answered' and complaint.dateAnswered: checks.append(calculate_business_date(complaint.dateAnswered, COMPLAINT_STAND_STILL_TIME, self)) return min(checks).isoformat() if checks else None
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, True) 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': 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 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")}