def collection_post(self):
     """Post a cancellation
     """
     tender = self.request.validated['tender']
     if tender.status in ['complete', 'cancelled', 'unsuccessful']:
         self.request.errors.add('body', 'data', 'Can\'t add cancellation in current ({}) tender status'.format(tender.status))
         self.request.errors.status = 403
         return
     cancellation_data = self.request.validated['data']
     if any([i.status != 'active' for i in tender.lots if i.id == cancellation_data.get('relatedLot')]):
         self.request.errors.add('body', 'data', 'Can add cancellation only in active lot status')
         self.request.errors.status = 403
         return
     cancellation = Cancellation(cancellation_data)
     cancellation.__parent__ = self.request.context
     if cancellation.relatedLot and cancellation.status == 'active':
         [setattr(i, 'status', 'cancelled') for i in tender.lots if i.id == cancellation.relatedLot]
         check_tender_status(self.request)
     elif cancellation.status == 'active':
         tender.status = 'cancelled'
     tender.cancellations.append(cancellation)
     if save_tender(self.request):
         LOGGER.info('Created tender cancellation {}'.format(cancellation.id),
                     extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_cancellation_create'}, {'cancellation_id': cancellation.id}))
         self.request.response.status = 201
         self.request.response.headers['Location'] = self.request.route_url('Tender Cancellations', tender_id=tender.id, cancellation_id=cancellation.id)
         return {'data': cancellation.serialize("view")}
 def patch(self):
     """Post a complaint resolution for award
     """
     tender = self.request.validated['tender']
     if tender.status != 'active':
         self.request.errors.add('body', 'data', 'Can\'t update complaint in current ({}) tender status'.format(tender.status))
         self.request.errors.status = 403
         return
     if self.context.status not in ['draft', 'claim', 'answered', 'pending', 'accepted', 'satisfied']:
         self.request.errors.add('body', 'data', 'Can\'t update complaint in current ({}) status'.format(self.context.status))
         self.request.errors.status = 403
         return
     data = self.request.validated['data']
     complaintPeriod = self.request.validated['award'].complaintPeriod
     is_complaintPeriod = complaintPeriod.startDate < get_now() and complaintPeriod.endDate > get_now() if complaintPeriod.endDate else complaintPeriod.startDate < get_now()
     # complaint_owner
     if self.request.authenticated_role == 'complaint_owner' and self.context.status in ['draft', 'claim', 'answered', 'pending', 'accepted'] 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 is_complaintPeriod 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 is_complaintPeriod and self.context.status == 'draft' and data.get('status', self.context.status) == 'pending':
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.type = 'complaint'
         self.context.dateSubmitted = get_now()
     # tender_owner
     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('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 == '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 == 'pending' 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 == 'pending' and data.get('status', self.context.status) == 'invalid':
         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 == 'accepted' 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 == 'accepted' 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()
     else:
         self.request.errors.add('body', 'data', 'Can\'t update complaint')
         self.request.errors.status = 403
         return
     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'] and tender.status in ['active.qualification', 'active.awarded']:
         check_tender_status(self.request)
     if save_tender(self.request):
         self.LOGGER.info('Updated tender award complaint {}'.format(self.context.id),
                     extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_award_complaint_patch'}))
         return {'data': self.context.serialize("view")}
 def patch(self):
     """Post a complaint resolution
     """
     tender = self.request.validated['tender']
     if tender.status not in ['active.enquiries', 'active.tendering', 'active.auction', 'active.qualification', 'active.awarded']:
         self.request.errors.add('body', 'data', 'Can\'t update complaint in current ({}) tender status'.format(tender.status))
         self.request.errors.status = 403
         return
     if self.context.status not in ['draft', 'claim', 'answered', 'pending']:
         self.request.errors.add('body', 'data', 'Can\'t update complaint in current ({}) status'.format(self.context.status))
         self.request.errors.status = 403
         return
     data = self.request.validated['data']
     # complaint_owner
     if self.request.authenticated_role == 'complaint_owner' and self.context.status in ['draft', 'claim', 'answered', 'pending'] 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 tender.status in ['active.enquiries', '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 in ['active.enquiries', 'active.tendering'] and self.context.status == 'draft' and data.get('status', self.context.status) == 'claim':
         apply_patch(self.request, save=False, src=self.context.serialize())
         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':
         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:
         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':
         if len(data.get('resolution', self.context.resolution)) < 20:
             self.request.errors.add('body', 'data', 'Can\'t update complaint: resolution too short')
             self.request.errors.status = 403
             return
         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 == 'pending':
         apply_patch(self.request, save=False, src=self.context.serialize())
     # reviewers
     elif self.request.authenticated_role == 'reviewers' and self.context.status == 'pending' 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 == 'reviewers' and self.context.status == 'pending' and data.get('status', self.context.status) in ['resolved', 'invalid', 'declined']:
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.dateDecision = get_now()
     else:
         self.request.errors.add('body', 'data', 'Can\'t update complaint')
         self.request.errors.status = 403
         return
     if self.context.tendererAction and not self.context.tendererActionDate:
         self.context.tendererActionDate = get_now()
     if self.context.status not in ['draft', 'claim', 'answered', 'pending'] 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']
     if tender.status not in ['active.enquiries', 'active.tendering', 'active.auction', 'active.qualification', 'active.awarded']:
         self.request.errors.add('body', 'data', 'Can\'t update complaint in current ({}) tender status'.format(tender.status))
         self.request.errors.status = 403
         return
     if self.request.context.status != 'pending':
         self.request.errors.add('body', 'data', 'Can\'t update complaint in current ({}) status'.format(self.request.context.status))
         self.request.errors.status = 403
         return
     if self.request.validated['data'].get('status', self.request.context.status) == 'cancelled':
         self.request.errors.add('body', 'data', 'Can\'t cancel complaint')
         self.request.errors.status = 403
         return
     apply_patch(self.request, save=False, src=self.request.context.serialize())
     if self.request.context.status == 'resolved' and tender.status != 'active.enquiries':
         for i in tender.complaints:
             if i.status == 'pending':
                 i.status = 'cancelled'
         [setattr(i, 'status', 'cancelled') for i in tender.lots]
         tender.status = 'cancelled'
     elif self.request.context.status in ['declined', 'invalid'] and tender.status == 'active.awarded':
         check_tender_status(self.request)
     if save_tender(self.request):
         LOGGER.info('Updated tender complaint {}'.format(self.request.context.id),
                     extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_complaint_patch'}))
         return {'data': self.request.context.serialize("view")}
def check_status(request):
    tender = request.validated['tender']
    now = get_now()

    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
    ]):
        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
def check_status(request):
    tender = request.validated['tender']
    now = get_now()

    if tender.status == 'active.tendering' and tender.tenderPeriod.endDate <= now:
        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
    ]):
        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
 def patch(self):
     """Post a complaint resolution for award
     """
     tender = self.request.validated["tender"]
     if tender.status not in ["active.qualification", "active.awarded"]:
         self.request.errors.add(
             "body", "data", "Can't update complaint in current ({}) tender status".format(tender.status)
         )
         self.request.errors.status = 403
         return
     if any([i.status != "active" for i in tender.lots if i.id == self.request.validated["award"].lotID]):
         self.request.errors.add("body", "data", "Can update complaint only in active lot status")
         self.request.errors.status = 403
         return
     complaint = self.request.context
     if complaint.status != "pending":
         self.request.errors.add(
             "body", "data", "Can't update complaint in current ({}) status".format(complaint.status)
         )
         self.request.errors.status = 403
         return
     if self.request.validated["data"].get("status", complaint.status) == "cancelled":
         self.request.errors.add("body", "data", "Can't cancel complaint")
         self.request.errors.status = 403
         return
     apply_patch(self.request, save=False, src=complaint.serialize())
     if complaint.status == "resolved":
         award = self.request.validated["award"]
         if tender.status == "active.awarded":
             tender.status = "active.qualification"
             tender.awardPeriod.endDate = None
         now = get_now()
         if award.status == "unsuccessful":
             for i in tender.awards[tender.awards.index(award) :]:
                 if i.lotID != award.lotID:
                     continue
                 i.complaintPeriod.endDate = now + STAND_STILL_TIME
                 i.status = "cancelled"
                 for j in i.complaints:
                     if j.status == "pending":
                         j.status = "cancelled"
         for i in tender.contracts:
             if award.id == i.awardID:
                 i.status = "cancelled"
         award.complaintPeriod.endDate = now + STAND_STILL_TIME
         award.status = "cancelled"
         add_next_award(self.request)
     elif complaint.status in ["declined", "invalid"] and tender.status == "active.awarded":
         check_tender_status(self.request)
     if save_tender(self.request):
         LOGGER.info(
             "Updated tender award complaint {}".format(self.request.context.id),
             extra=context_unpack(self.request, {"MESSAGE_ID": "tender_award_complaint_patch"}),
         )
         return {"data": complaint.serialize("view")}
 def patch(self):
     """Update of contract
     """
     if self.request.validated['tender_status'] not in ['active.qualification', 'active.awarded']:
         self.request.errors.add('body', 'data', 'Can\'t update contract in current ({}) tender status'.format(self.request.validated['tender_status']))
         self.request.errors.status = 403
         return
     tender = self.request.validated['tender']
     if any([i.status != 'active' for i in tender.lots if i.id in [a.lotID for a in tender.awards if a.id == self.request.context.awardID]]):
         self.request.errors.add('body', 'data', 'Can update contract only in active lot status')
         self.request.errors.status = 403
         return
     data = self.request.validated['data']
     if self.request.context.status != 'active' and 'status' in data and data['status'] == 'active':
         award = [a for a in tender.awards if a.id == self.request.context.awardID][0]
         stand_still_end = award.complaintPeriod.endDate
         if stand_still_end > get_now():
             self.request.errors.add('body', 'data', 'Can\'t sign contract before stand-still period end ({})'.format(stand_still_end.isoformat()))
             self.request.errors.status = 403
             return
         pending_complaints = [
             i
             for i in tender.complaints
             if i.status in ['claim', 'answered', 'pending'] and i.relatedLot in [None, award.lotID]
         ]
         pending_awards_complaints = [
             i
             for a in tender.awards
             for i in a.complaints
             if i.status in ['claim', 'answered', 'pending'] and a.lotID == award.lotID
         ]
         if pending_complaints or pending_awards_complaints:
             self.request.errors.add('body', 'data', 'Can\'t sign contract before reviewing all complaints')
             self.request.errors.status = 403
             return
     contract_status = self.request.context.status
     apply_patch(self.request, save=False, src=self.request.context.serialize())
     if contract_status != self.request.context.status and (contract_status != 'pending' or self.request.context.status != 'active'):
         self.request.errors.add('body', 'data', 'Can\'t update contract status')
         self.request.errors.status = 403
         return
     if self.request.context.status == 'active' and not self.request.context.dateSigned:
         self.request.context.dateSigned = get_now()
     check_tender_status(self.request)
     if save_tender(self.request):
         LOGGER.info('Updated tender contract {}'.format(self.request.context.id),
                     extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_contract_patch'}))
         return {'data': self.request.context.serialize()}
 def patch(self):
     """Update of contract
     """
     if self.request.validated['tender_status'] not in ['active.qualification', 'active.awarded', 'complete']:
         self.request.errors.add('body', 'data', 'Can\'t update contract in current ({}) tender status'.format(self.request.validated['tender_status']))
         self.request.errors.status = 403
         return
     tender = self.request.validated['tender']
     if any([i.status != 'active' for i in tender.lots if i.id in [a.lotID for a in tender.awards if a.id == self.request.context.awardID]]):
         self.request.errors.add('body', 'data', 'Can update contract only in active lot status')
         self.request.errors.status = 403
         return
     data = self.request.validated['data']
     if self.request.context.status != 'active' and 'status' in data and data['status'] == 'active':
         award = [a for a in tender.awards if a.id == self.request.context.awardID][0]
         stand_still_end = award.complaintPeriod.endDate
         if stand_still_end > get_now():
             self.request.errors.add('body', 'data', 'Can\'t sign contract before stand-still period end ({})'.format(stand_still_end.isoformat()))
             self.request.errors.status = 403
             return
         pending_complaints = [
             i
             for i in tender.complaints
             if i.status in ['claim', 'answered', 'pending'] and i.relatedLot in [None, award.lotID]
         ]
         pending_awards_complaints = [
             i
             for a in tender.awards
             for i in a.complaints
             if i.status in ['claim', 'answered', 'pending'] and a.lotID == award.lotID
         ]
         if pending_complaints or pending_awards_complaints:
             self.request.errors.add('body', 'data', 'Can\'t sign contract before reviewing all complaints')
             self.request.errors.status = 403
             return
     contract_status = self.request.context.status
     apply_patch(self.request, save=False, src=self.request.context.serialize())
     if contract_status != self.request.context.status and (contract_status != 'pending' or self.request.context.status != 'active'):
         self.request.errors.add('body', 'data', 'Can\'t update contract status')
         self.request.errors.status = 403
         return
     check_tender_status(self.request)
     if save_tender(self.request):
         LOGGER.info('Updated tender contract {}'.format(self.request.context.id),
                     extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_contract_patch'}))
         return {'data': self.request.context.serialize()}
 def patch(self):
     """Post a cancellation resolution
     """
     tender = self.request.validated['tender']
     if tender.status in ['complete', 'cancelled', 'unsuccessful']:
         self.request.errors.add('body', 'data', 'Can\'t update cancellation in current ({}) tender status'.format(tender.status))
         self.request.errors.status = 403
         return
     if any([i.status != 'active' for i in tender.lots if i.id == self.request.context.relatedLot]):
         self.request.errors.add('body', 'data', 'Can update cancellation only in active lot status')
         self.request.errors.status = 403
         return
     apply_patch(self.request, save=False, src=self.request.context.serialize())
     if self.request.context.relatedLot and self.request.context.status == 'active':
         [setattr(i, 'status', 'cancelled') for i in tender.lots if i.id == self.request.context.relatedLot]
         check_tender_status(self.request)
     elif self.request.context.status == 'active':
         tender.status = 'cancelled'
     if save_tender(self.request):
         LOGGER.info('Updated tender cancellation {}'.format(self.request.context.id),
                     extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_cancellation_patch'}))
         return {'data': self.request.context.serialize("view")}
Beispiel #11
0
def check_status(request):
    tender = request.validated['tender']
    now = get_now()

    if tender.status == 'active.tendering' and tender.tenderPeriod.endDate <= now and \
            not any([i.status in BLOCK_COMPLAINT_STATUS for i in tender.complaints]) and \
            not any([i.id for i in tender.questions if not i.answer]):
        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 BLOCK_COMPLAINT_STATUS
        for q in tender.qualifications
        for i in q.complaints
    ]):
        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:
            pending_complaints = any([
                i['status'] in ['claim', 'answered', 'pending']
                for i in tender.complaints
            ])
            pending_awards_complaints = any([
                i['status'] in ['claim', 'answered', 'pending']
                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 ['claim', 'answered', 'pending'] 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_awards_complaints = any([
                    i['status'] in ['claim', 'answered', 'pending']
                    for a in lot_awards
                    for i in a.complaints
                ])
                awarded = any([
                    i['status'] == 'active'
                    for i in lot_awards
                ])
                if 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):
     """Post a complaint resolution
     """
     tender = self.request.validated['tender']
     if tender.status != 'active.tendering':
         self.request.errors.add('body', 'data', 'Can\'t update complaint in current ({}) tender status'.format(tender.status))
         self.request.errors.status = 403
         return
     if self.context.status not in ['draft', 'claim', 'answered', 'pending', 'accepted', 'satisfied', 'stopping']:
         self.request.errors.add('body', 'data', 'Can\'t update complaint in current ({}) status'.format(self.context.status))
         self.request.errors.status = 403
         return
     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):
             self.request.errors.add('body', 'data', 'Can submit claim not later than {0.days} days before tenderPeriod end'.format(CLAIM_SUBMIT_TIME))
             self.request.errors.status = 403
             return
         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() > calculate_business_date(tender.tenderPeriod.endDate, -COMPLAINT_SUBMIT_TIME, tender):
             self.request.errors.add('body', 'data', 'Can submit complaint not later than {0.days} days before tenderPeriod end'.format(COMPLAINT_SUBMIT_TIME))
             self.request.errors.status = 403
             return
         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() > calculate_business_date(tender.tenderPeriod.endDate, -COMPLAINT_SUBMIT_TIME, tender):
             self.request.errors.add('body', 'data', 'Can submit complaint not later than {0.days} days before tenderPeriod end'.format(COMPLAINT_SUBMIT_TIME))
             self.request.errors.status = 403
             return
         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:
             self.request.errors.add('body', 'data', 'Can update claim only before enquiryPeriod.clarificationsUntil')
             self.request.errors.status = 403
             return
         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:
             self.request.errors.add('body', 'data', 'Can update claim only before enquiryPeriod.clarificationsUntil')
             self.request.errors.status = 403
             return
         if len(data.get('resolution', self.context.resolution)) < 20:
             self.request.errors.add('body', 'data', 'Can\'t update complaint: resolution too short')
             self.request.errors.status = 403
             return
         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 == 'pending' and data.get('status', self.context.status) == 'invalid':
         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 == 'accepted' 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 ['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:
         self.request.errors.add('body', 'data', 'Can\'t update complaint')
         self.request.errors.status = 403
         return
     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")}
Beispiel #13
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
Beispiel #14
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):
     """Post a complaint resolution for award
     """
     tender = self.request.validated['tender']
     if tender.status not in ['active.qualification', 'active.awarded']:
         self.request.errors.add(
             'body', 'data',
             'Can\'t update complaint in current ({}) tender status'.format(
                 tender.status))
         self.request.errors.status = 403
         return
     if any([
             i.status != 'active' for i in tender.lots
             if i.id == self.request.validated['award'].lotID
     ]):
         self.request.errors.add(
             'body', 'data',
             'Can update complaint only in active lot status')
         self.request.errors.status = 403
         return
     if self.context.status not in [
             'draft', 'claim', 'answered', 'pending', 'accepted',
             'satisfied', 'stopping'
     ]:
         self.request.errors.add(
             'body', 'data',
             'Can\'t update complaint in current ({}) status'.format(
                 self.context.status))
         self.request.errors.status = 403
         return
     data = self.request.validated['data']
     complaintPeriod = self.request.validated['award'].complaintPeriod
     is_complaintPeriod = complaintPeriod.startDate < get_now(
     ) and complaintPeriod.endDate > get_now(
     ) if complaintPeriod.endDate else complaintPeriod.startDate < get_now(
     )
     # 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 is_complaintPeriod 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 is_complaintPeriod and self.context.status == 'draft' and data.get(
             'status', self.context.status) == 'claim':
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.dateSubmitted = get_now()
     elif self.request.authenticated_role == 'complaint_owner' and is_complaintPeriod and self.context.status == 'draft' and data.get(
             'status', self.context.status) == 'pending':
         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())
     # tender_owner
     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 in [
             'claim', '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':
         if len(data.get('resolution', self.context.resolution)) < 20:
             self.request.errors.add(
                 'body', 'data',
                 'Can\'t update complaint: resolution too short')
             self.request.errors.status = 403
             return
         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 == '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 == 'accepted' 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 [
             '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:
         self.request.errors.add('body', 'data', 'Can\'t update complaint')
         self.request.errors.status = 403
         return
     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 award complaint {}'.format(self.context.id),
             extra=context_unpack(
                 self.request,
                 {'MESSAGE_ID': 'tender_award_complaint_patch'}))
         return {'data': self.context.serialize("view")}
    def patch(self):
        """Update of contract
        """
        if self.request.validated['tender_status'] not in ['active.qualification', 'active.awarded']:
            self.request.errors.add('body', 'data', 'Can\'t update contract in current ({}) tender status'.format(self.request.validated['tender_status']))
            self.request.errors.status = 403
            return
        tender = self.request.validated['tender']
        if any([i.status != 'active' for i in tender.lots if i.id in [a.lotID for a in tender.awards if a.id == self.request.context.awardID]]):
            self.request.errors.add('body', 'data', 'Can update contract only in active lot status')
            self.request.errors.status = 403
            return
        if any([any([c.status == 'accepted' for c in i.complaints]) for i in tender.awards if i.lotID in [a.lotID for a in tender.awards if a.id == self.request.context.awardID]]):
            self.request.errors.add('body', 'data', 'Can\'t update contract with accepted complaint')
            self.request.errors.status = 403
            return
        data = self.request.validated['data']

        if data['value']:
            for ro_attr in ('valueAddedTaxIncluded', 'currency'):
                if data['value'][ro_attr] != getattr(self.context.value, ro_attr):
                    self.request.errors.add('body', 'data', 'Can\'t update {} for contract value'.format(ro_attr))
                    self.request.errors.status = 403
                    return

            award = [a for a in tender.awards if a.id == self.request.context.awardID][0]
            if data['value']['amount'] > award.value.amount:
                self.request.errors.add('body', 'data', 'Value amount should be less or equal to awarded amount ({})'.format(award.value.amount))
                self.request.errors.status = 403
                return

        if self.request.context.status != 'active' and 'status' in data and data['status'] == 'active':
            award = [a for a in tender.awards if a.id == self.request.context.awardID][0]
            stand_still_end = award.complaintPeriod.endDate
            if stand_still_end > get_now():
                self.request.errors.add('body', 'data', 'Can\'t sign contract before stand-still period end ({})'.format(stand_still_end.isoformat()))
                self.request.errors.status = 403
                return
            pending_complaints = [
                i
                for i in tender.complaints
                if i.status in tender.block_complaint_status and i.relatedLot in [None, award.lotID]
            ]
            pending_awards_complaints = [
                i
                for a in tender.awards
                for i in a.complaints
                if i.status in tender.block_complaint_status and a.lotID == award.lotID
            ]
            if pending_complaints or pending_awards_complaints:
                self.request.errors.add('body', 'data', 'Can\'t sign contract before reviewing all complaints')
                self.request.errors.status = 403
                return
        contract_status = self.request.context.status
        apply_patch(self.request, save=False, src=self.request.context.serialize())
        if contract_status != self.request.context.status and (contract_status != 'pending' or self.request.context.status != 'active'):
            self.request.errors.add('body', 'data', 'Can\'t update contract status')
            self.request.errors.status = 403
            return
        if self.request.context.status == 'active' and not self.request.context.dateSigned:
            self.request.context.dateSigned = get_now()
        check_tender_status(self.request)
        if save_tender(self.request):
            self.LOGGER.info('Updated tender contract {}'.format(self.request.context.id),
                        extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_contract_patch'}))
            return {'data': self.request.context.serialize()}
def check_status(request):
    tender = request.validated['tender']
    now = get_now()
    if not tender.lots and tender.status == 'active.tendering' and tender.tenderPeriod.endDate <= now and \
        not any([i.status in BLOCK_COMPLAINT_STATUS for i in tender.complaints]) and \
        not any([i.id for i in tender.questions if not i.answer]):
        for complaint in tender.complaints:
            check_complaint_status(request, complaint)
        LOGGER.info('Switched tender {} to {}'.format(tender['id'], 'active.auction'),
                    extra=context_unpack(request, {'MESSAGE_ID': 'switched_tender_active.auction'}))
        tender.status = 'active.auction'
        remove_draft_bids(request)
        check_bids(request)
        if tender.numberOfBids < 2 and tender.auctionPeriod:
            tender.auctionPeriod.startDate = None
        return
    elif tender.lots and tender.status == 'active.tendering' and tender.tenderPeriod.endDate <= now and \
        not any([i.status in BLOCK_COMPLAINT_STATUS for i in tender.complaints]) and \
        not any([i.id for i in tender.questions if not i.answer]):
        for complaint in tender.complaints:
            check_complaint_status(request, complaint)
        LOGGER.info('Switched tender {} to {}'.format(tender['id'], 'active.auction'),
                    extra=context_unpack(request, {'MESSAGE_ID': 'switched_tender_active.auction'}))
        tender.status = 'active.auction'
        remove_draft_bids(request)
        check_bids(request)
        [setattr(i.auctionPeriod, 'startDate', None) for i in tender.lots if i.numberOfBids < 2 and i.auctionPeriod]
        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 PENDING_COMPLAINT_STATUS
                for i in tender.complaints
            ])
            pending_awards_complaints = any([
                i['status'] in PENDING_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 PENDING_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 PENDING_COMPLAINT_STATUS and i.relatedLot == lot.id
                    for i in tender.complaints
                ])
                pending_awards_complaints = any([
                    i['status'] in PENDING_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 check_status(request):
    tender = request.validated['tender']
    now = get_now()
    if not tender.lots and tender.status == 'active.tendering' and tender.tenderPeriod.endDate <= now and \
        not any([i.status in BLOCK_COMPLAINT_STATUS for i in tender.complaints]) and \
        not any([i.id for i in tender.questions if not i.answer]):
        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 any([i.status in BLOCK_COMPLAINT_STATUS for i in tender.complaints]) and \
        not any([i.id for i in tender.questions if not i.answer]):
        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 PENDING_COMPLAINT_STATUS
                for i in tender.complaints
            ])
            pending_awards_complaints = any([
                i['status'] in PENDING_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 PENDING_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 PENDING_COMPLAINT_STATUS
                    and i.relatedLot == lot.id for i in tender.complaints
                ])
                pending_awards_complaints = any([
                    i['status'] in PENDING_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 check_status(request):
    tender = request.validated['tender']
    now = get_now()
    if not tender.lots and tender.status == 'active.tendering' and tender.tenderPeriod.endDate <= now and \
        not any([i.status in tender.block_tender_complaint_status for i in tender.complaints]) and \
        not any([i.id for i in tender.questions if not i.answer]):
        for complaint in tender.complaints:
            check_complaint_status(request, complaint)
        LOGGER.info('Switched tender {} to {}'.format(tender['id'],
                                                      'active.auction'),
                    extra=context_unpack(
                        request,
                        {'MESSAGE_ID': 'switched_tender_active.auction'}))
        tender.status = 'active.auction'
        remove_draft_bids(request)
        check_bids(request)
        if tender.numberOfBids < 2 and tender.auctionPeriod:
            tender.auctionPeriod.startDate = None
        return
    elif tender.lots and tender.status == 'active.tendering' and tender.tenderPeriod.endDate <= now and \
        not any([i.status in tender.block_tender_complaint_status for i in tender.complaints]) and \
        not any([i.id for i in tender.questions if not i.answer]):
        for complaint in tender.complaints:
            check_complaint_status(request, complaint)
        LOGGER.info('Switched tender {} to {}'.format(tender['id'],
                                                      'active.auction'),
                    extra=context_unpack(
                        request,
                        {'MESSAGE_ID': 'switched_tender_active.auction'}))
        tender.status = 'active.auction'
        remove_draft_bids(request)
        check_bids(request)
        [
            setattr(i.auctionPeriod, 'startDate', None) for i in tender.lots
            if i.numberOfBids < 2 and i.auctionPeriod
        ]
        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
    def patch(self):
        """Update of contract
        """
        if self.request.validated["tender_status"] not in ["active.qualification", "active.awarded"]:
            self.request.errors.add(
                "body",
                "data",
                "Can't update contract in current ({}) tender status".format(self.request.validated["tender_status"]),
            )
            self.request.errors.status = 403
            return
        tender = self.request.validated["tender"]
        if any(
            [
                i.status != "active"
                for i in tender.lots
                if i.id in [a.lotID for a in tender.awards if a.id == self.request.context.awardID]
            ]
        ):
            self.request.errors.add("body", "data", "Can update contract only in active lot status")
            self.request.errors.status = 403
            return
        data = self.request.validated["data"]

        if data["value"]:
            for ro_attr in ("valueAddedTaxIncluded", "currency"):
                if data["value"][ro_attr] != getattr(self.context.value, ro_attr):
                    self.request.errors.add("body", "data", "Can't update {} for contract value".format(ro_attr))
                    self.request.errors.status = 403
                    return

            award = [a for a in tender.awards if a.id == self.request.context.awardID][0]
            if data["value"]["amount"] > award.value.amount:
                self.request.errors.add(
                    "body",
                    "data",
                    "Value amount should be less or equal to awarded amount ({})".format(award.value.amount),
                )
                self.request.errors.status = 403
                return

        if self.request.context.status != "active" and "status" in data and data["status"] == "active":
            award = [a for a in tender.awards if a.id == self.request.context.awardID][0]
            stand_still_end = award.complaintPeriod.endDate
            if stand_still_end > get_now():
                self.request.errors.add(
                    "body",
                    "data",
                    "Can't sign contract before stand-still period end ({})".format(stand_still_end.isoformat()),
                )
                self.request.errors.status = 403
                return
            pending_complaints = [
                i
                for i in tender.complaints
                if i.status in ["claim", "answered", "pending"] and i.relatedLot in [None, award.lotID]
            ]
            pending_awards_complaints = [
                i
                for a in tender.awards
                for i in a.complaints
                if i.status in ["claim", "answered", "pending"] and a.lotID == award.lotID
            ]
            if pending_complaints or pending_awards_complaints:
                self.request.errors.add("body", "data", "Can't sign contract before reviewing all complaints")
                self.request.errors.status = 403
                return
        contract_status = self.request.context.status
        apply_patch(self.request, save=False, src=self.request.context.serialize())
        if contract_status != self.request.context.status and (
            contract_status != "pending" or self.request.context.status != "active"
        ):
            self.request.errors.add("body", "data", "Can't update contract status")
            self.request.errors.status = 403
            return
        if self.request.context.status == "active" and not self.request.context.dateSigned:
            self.request.context.dateSigned = get_now()
        check_tender_status(self.request)
        if save_tender(self.request):
            self.LOGGER.info(
                "Updated tender contract {}".format(self.request.context.id),
                extra=context_unpack(self.request, {"MESSAGE_ID": "tender_contract_patch"}),
            )
            return {"data": self.request.context.serialize()}
Beispiel #21
0
    def patch(self):
        """Update of contract
        """
        if self.request.validated['tender_status'] not in ['active.qualification', 'active.awarded']:
            self.request.errors.add('body', 'data', 'Can\'t update contract in current ({}) tender status'.format(self.request.validated['tender_status']))
            self.request.errors.status = 403
            return
        tender = self.request.validated['tender']
        if any([i.status != 'active' for i in tender.lots if i.id in [a.lotID for a in tender.awards if a.id == self.request.context.awardID]]):
            self.request.errors.add('body', 'data', 'Can update contract only in active lot status')
            self.request.errors.status = 403
            return
        data = self.request.validated['data']

        if data['value']:
            for ro_attr in ('valueAddedTaxIncluded', 'currency'):
                if data['value'][ro_attr] != getattr(self.context.value, ro_attr):
                    self.request.errors.add('body', 'data', 'Can\'t update {} for contract value'.format(ro_attr))
                    self.request.errors.status = 403
                    return

            award = [a for a in tender.awards if a.id == self.request.context.awardID][0]
            if data['value']['amount'] > award.value.amount:
                self.request.errors.add('body', 'data', 'Value amount should be less or equal to awarded amount ({})'.format(award.value.amount))
                self.request.errors.status = 403
                return

        if self.request.context.status != 'active' and 'status' in data and data['status'] == 'active':
            award = [a for a in tender.awards if a.id == self.request.context.awardID][0]
            stand_still_end = award.complaintPeriod.endDate
            if stand_still_end > get_now():
                self.request.errors.add('body', 'data', 'Can\'t sign contract before stand-still period end ({})'.format(stand_still_end.isoformat()))
                self.request.errors.status = 403
                return
            pending_complaints = [
                i
                for i in tender.complaints
                if i.status in PENDING_COMPLAINT_STATUS and i.relatedLot in [None, award.lotID]
            ]
            pending_awards_complaints = [
                i
                for a in tender.awards
                for i in a.complaints
                if i.status in PENDING_COMPLAINT_STATUS and a.lotID == award.lotID
            ]
            if pending_complaints or pending_awards_complaints:
                self.request.errors.add('body', 'data', 'Can\'t sign contract before reviewing all complaints')
                self.request.errors.status = 403
                return
        contract_status = self.request.context.status
        apply_patch(self.request, save=False, src=self.request.context.serialize())
        if contract_status != self.request.context.status and (contract_status != 'pending' or self.request.context.status != 'active'):
            self.request.errors.add('body', 'data', 'Can\'t update contract status')
            self.request.errors.status = 403
            return
        if self.request.context.status == 'active' and not self.request.context.dateSigned:
            self.request.context.dateSigned = get_now()
        check_tender_status(self.request)
        if save_tender(self.request):
            self.LOGGER.info('Updated tender contract {}'.format(self.request.context.id),
                        extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_contract_patch'}))
            return {'data': self.request.context.serialize()}
 def patch(self):
     """Post a complaint resolution for award
     """
     tender = self.request.validated["tender"]
     if tender.status not in ["active.qualification", "active.awarded"]:
         self.request.errors.add(
             "body", "data", "Can't update complaint in current ({}) tender status".format(tender.status)
         )
         self.request.errors.status = 403
         return
     if any([i.status != "active" for i in tender.lots if i.id == self.request.validated["award"].lotID]):
         self.request.errors.add("body", "data", "Can update complaint only in active lot status")
         self.request.errors.status = 403
         return
     if self.context.status not in ["draft", "claim", "answered", "pending"]:
         self.request.errors.add(
             "body", "data", "Can't update complaint in current ({}) status".format(self.context.status)
         )
         self.request.errors.status = 403
         return
     data = self.request.validated["data"]
     complaintPeriod = self.request.validated["award"].complaintPeriod
     is_complaintPeriod = (
         complaintPeriod.startDate < get_now() and complaintPeriod.endDate > get_now()
         if complaintPeriod.endDate
         else complaintPeriod.startDate < get_now()
     )
     # complaint_owner
     if (
         self.request.authenticated_role == "complaint_owner"
         and self.context.status in ["draft", "claim", "answered", "pending"]
         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 is_complaintPeriod
         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 is_complaintPeriod
         and self.context.status == "draft"
         and data.get("status", self.context.status) == "claim"
     ):
         apply_patch(self.request, save=False, src=self.context.serialize())
         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"
     ):
         apply_patch(self.request, save=False, src=self.context.serialize())
         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
     ):
         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 len(data.get("resolution", self.context.resolution or "")) >= 20
         and data.get("resolutionType", self.context.resolutionType)
         and data.get("status", self.context.status) == "answered"
     ):
         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 == "pending":
         apply_patch(self.request, save=False, src=self.context.serialize())
     # reviewers
     elif (
         self.request.authenticated_role == "reviewers"
         and self.context.status == "pending"
         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 == "reviewers"
         and self.context.status == "pending"
         and data.get("status", self.context.status) in ["resolved", "invalid", "declined"]
     ):
         apply_patch(self.request, save=False, src=self.context.serialize())
         self.context.dateDecision = get_now()
     else:
         self.request.errors.add("body", "data", "Can't update complaint")
         self.request.errors.status = 403
         return
     if self.context.tendererAction and not self.context.tendererActionDate:
         self.context.tendererActionDate = get_now()
     if self.context.status not in ["draft", "claim", "answered", "pending"] and tender.status in [
         "active.qualification",
         "active.awarded",
     ]:
         check_tender_status(self.request)
     if save_tender(self.request):
         self.LOGGER.info(
             "Updated tender award complaint {}".format(self.context.id),
             extra=context_unpack(self.request, {"MESSAGE_ID": "tender_award_complaint_patch"}),
         )
         return {"data": self.context.serialize("view")}
def check_status(request):
    tender = request.validated["tender"]
    now = get_now()
    if (
        not tender.lots
        and tender.status == "active.tendering"
        and tender.tenderPeriod.endDate <= now
        and not has_unanswered_complaints(tender)
        and not has_unanswered_questions(tender)
    ):
        for complaint in tender.complaints:
            check_complaint_status(request, complaint)
        LOGGER.info(
            "Switched tender {} to {}".format(tender["id"], "active.auction"),
            extra=context_unpack(request, {"MESSAGE_ID": "switched_tender_active.auction"}),
        )
        tender.status = "active.auction"
        remove_draft_bids(request)
        check_bids(request)
        if tender.numberOfBids < 2 and tender.auctionPeriod:
            tender.auctionPeriod.startDate = None
        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"
        remove_draft_bids(request)
        check_bids(request)
        [setattr(i.auctionPeriod, "startDate", None) for i in tender.lots if i.numberOfBids < 2 and i.auctionPeriod]
        return
    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
def check_status(request):
    tender = request.validated['tender']
    now = get_now()

    if tender.status == 'active.tendering' and tender.tenderPeriod.endDate <= now and \
        not any([i.status in BLOCK_COMPLAINT_STATUS for i in tender.complaints]) and \
        not any([i.id for i in tender.questions if not i.answer]):
        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})
        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 BLOCK_COMPLAINT_STATUS
        for q in tender.qualifications
        for i in q.complaints
    ]):
        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:
            pending_complaints = any([
                i['status'] in ['claim', 'answered', 'pending']
                for i in tender.complaints
            ])
            pending_awards_complaints = any([
                i['status'] in ['claim', 'answered', 'pending']
                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 ['claim', 'answered', 'pending'] for i in tender.complaints]):
            return
        lots_ends = []
        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_awards_complaints = any([
                    i['status'] in ['claim', 'answered', 'pending']
                    for a in lot_awards
                    for i in a.complaints
                ])
                awarded = any([
                    i['status'] == 'active'
                    for i in lot_awards
                ])
                if 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):
     """Post a complaint resolution
     """
     tender = self.request.validated['tender']
     if tender.status != 'active.tendering':
         self.request.errors.add('body', 'data', 'Can\'t update complaint in current ({}) tender status'.format(tender.status))
         self.request.errors.status = 403
         return
     if self.context.status not in ['draft', 'claim', 'answered', 'pending', 'accepted', 'satisfied']:
         self.request.errors.add('body', 'data', 'Can\'t update complaint in current ({}) status'.format(self.context.status))
         self.request.errors.status = 403
         return
     data = self.request.validated['data']
     # complaint_owner
     if self.request.authenticated_role == 'complaint_owner' and self.context.status in ['draft', 'claim', 'answered', 'pending', 'accepted'] 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 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):
             self.request.errors.add('body', 'data', 'Can submit claim not later than {0.days} days before tenderPeriod end'.format(CLAIM_SUBMIT_TIME))
             self.request.errors.status = 403
             return
         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() > calculate_business_date(tender.tenderPeriod.endDate, -COMPLAINT_SUBMIT_TIME):
             self.request.errors.add('body', 'data', 'Can submit complaint not later than {0.days} days before tenderPeriod end'.format(COMPLAINT_SUBMIT_TIME))
             self.request.errors.status = 403
             return
         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() > calculate_business_date(tender.tenderPeriod.endDate, -COMPLAINT_SUBMIT_TIME):
             self.request.errors.add('body', 'data', 'Can submit complaint not later than {0.days} days before tenderPeriod end'.format(COMPLAINT_SUBMIT_TIME))
             self.request.errors.status = 403
             return
         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 in ['claim', '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':
         if len(data.get('resolution', self.context.resolution)) < 20:
             self.request.errors.add('body', 'data', 'Can\'t update complaint: resolution too short')
             self.request.errors.status = 403
             return
         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 == 'pending' 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 == 'pending' and data.get('status', self.context.status) == 'invalid':
         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 == 'accepted' 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 == 'accepted' 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()
     else:
         self.request.errors.add('body', 'data', 'Can\'t update complaint')
         self.request.errors.status = 403
         return
     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'] 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):
        """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.request.validated['tender']
        if self.request.authenticated_role != 'Administrator' and tender.status in ['complete', 'unsuccessful', 'cancelled']:
            self.request.errors.add('body', 'data', 'Can\'t update tender in current ({}) status'.format(tender.status))
            self.request.errors.status = 403
            return
        data = self.request.validated['data']
        if self.request.authenticated_role == 'tender_owner' and 'status' in data and data['status'] not in ['cancelled', tender.status]:
            self.request.errors.add('body', 'data', 'Can\'t update tender status')
            self.request.errors.status = 403
            return
        if self.request.authenticated_role == 'chronograph' and tender.status == 'active.tendering' and data.get('status', tender.status) == 'active.auction':
            apply_patch(self.request, save=False, src=self.request.validated['tender_src'])
            check_bids(self.request)
            save_tender(self.request)
        elif self.request.authenticated_role == 'chronograph' and tender.status in ['active.qualification', 'active.awarded'] and data.get('status', tender.status) == tender.status:
            check_tender_status(self.request)
            save_tender(self.request)
        else:
            apply_patch(self.request, src=self.request.validated['tender_src'])
        LOGGER.info('Updated tender {}'.format(tender.id),
            extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_patch'}))
        return {'data': tender.serialize(tender.status)}