def check_auction_time(self):
     if self.auctionPeriod and self.auctionPeriod.startDate and self.auctionPeriod.shouldStartAfter \
             and self.auctionPeriod.startDate > calculate_business_date(parse_date(self.auctionPeriod.shouldStartAfter), AUCTION_PERIOD_TIME, self, True):
         self.auctionPeriod.startDate = None
     for lot in self.lots:
         if lot.auctionPeriod and lot.auctionPeriod.startDate and lot.auctionPeriod.shouldStartAfter \
                 and lot.auctionPeriod.startDate > calculate_business_date(parse_date(lot.auctionPeriod.shouldStartAfter), AUCTION_PERIOD_TIME, self, True):
             lot.auctionPeriod.startDate = None
Ejemplo n.º 2
0
 def initialize(self):
     endDate = calculate_business_date(self.tenderPeriod.endDate,
                                       -QUESTIONS_STAND_STILL)
     self.enquiryPeriod = EnquiryPeriod(
         dict(startDate=self.tenderPeriod.startDate,
              endDate=endDate,
              clarificationsUntil=calculate_business_date(
                  endDate, ENQUIRY_STAND_STILL_TIME, self, True)))
Ejemplo n.º 3
0
 def tender_enquiryPeriod(self):
     endDate = calculate_business_date(self.tenderPeriod.endDate,
                                       -ENQUIRY_PERIOD_TIME, self)
     return EnquiryPeriod(
         dict(startDate=self.tenderPeriod.startDate,
              endDate=endDate,
              clarificationsUntil=calculate_business_date(
                  endDate, ENQUIRY_STAND_STILL_TIME, self, True)))
 def check_auction_time(self):
     if self.auctionPeriod and self.auctionPeriod.startDate and self.auctionPeriod.shouldStartAfter \
             and self.auctionPeriod.startDate > calculate_business_date(parse_date(self.auctionPeriod.shouldStartAfter), AUCTION_PERIOD_TIME, self, True):
         self.auctionPeriod.startDate = None
     for lot in self.lots:
         if lot.auctionPeriod and lot.auctionPeriod.startDate and lot.auctionPeriod.shouldStartAfter \
                 and lot.auctionPeriod.startDate > calculate_business_date(parse_date(lot.auctionPeriod.shouldStartAfter), AUCTION_PERIOD_TIME, self, True):
             lot.auctionPeriod.startDate = None
Ejemplo n.º 5
0
 def collection_post(self):
     """Post a complaint
     """
     tender = self.context
     if tender.status != 'active.tendering':
         self.request.errors.add(
             'body', 'data',
             'Can\'t add complaint in current ({}) tender status'.format(
                 tender.status))
         self.request.errors.status = 403
         return
     complaint = self.request.validated['complaint']
     if complaint.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
         complaint.dateSubmitted = get_now()
     elif complaint.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
         complaint.dateSubmitted = get_now()
         complaint.type = 'complaint'
     else:
         complaint.status = 'draft'
     complaint.complaintID = '{}.{}{}'.format(
         tender.tenderID, self.server_id,
         self.complaints_len(tender) + 1)
     set_ownership(complaint, self.request)
     tender.complaints.append(complaint)
     if save_tender(self.request):
         self.LOGGER.info(
             'Created tender complaint {}'.format(complaint.id),
             extra=context_unpack(self.request,
                                  {'MESSAGE_ID': 'tender_complaint_create'},
                                  {'complaint_id': complaint.id}))
         self.request.response.status = 201
         self.request.response.headers['Location'] = self.request.route_url(
             'Tender Complaints',
             tender_id=tender.id,
             complaint_id=complaint.id)
         return {
             'data': complaint.serialize(tender.status),
             'access': {
                 'token': complaint.owner_token
             }
         }
 def tender_enquiryPeriod(self):
     endDate = calculate_business_date(self.tenderPeriod.endDate,
                                       -QUESTIONS_STAND_STILL, self)
     return EnquiryPeriod(
         dict(startDate=self.tenderPeriod.startDate,
              endDate=endDate,
              invalidationDate=self.enquiryPeriod
              and self.enquiryPeriod.invalidationDate,
              clarificationsUntil=calculate_business_date(
                  endDate, ENQUIRY_STAND_STILL_TIME, self, True)))
 def validate_tenderPeriod(self, data, period):
     # if data['_rev'] is None when tender was created just now
     if not data['_rev'] and calculate_business_date(
             get_now(), -timedelta(minutes=10)) >= period.startDate:
         raise ValidationError(
             u"tenderPeriod.startDate should be in greater than current date"
         )
     if period and calculate_business_date(period.startDate, TENDER_PERIOD,
                                           data) > period.endDate:
         raise ValidationError(
             u"tenderPeriod should be greater than 15 days")
Ejemplo n.º 8
0
 def initialize(self):
     endDate = calculate_business_date(self.tenderPeriod.endDate, -QUESTIONS_STAND_STILL, self)
     self.enquiryPeriod = EnquiryPeriod(dict(startDate=self.tenderPeriod.startDate,
                                             endDate=endDate,
                                             invalidationDate=self.enquiryPeriod and self.enquiryPeriod.invalidationDate,
                                             clarificationsUntil=calculate_business_date(endDate, ENQUIRY_STAND_STILL_TIME, self, True)))
     now = get_now()
     self.date = now
     if self.lots:
         for lot in self.lots:
             lot.date = now
Ejemplo n.º 9
0
 def initialize(self):
     endDate = calculate_business_date(self.tenderPeriod.endDate, -QUESTIONS_STAND_STILL, self)
     self.enquiryPeriod = EnquiryPeriod(dict(startDate=self.tenderPeriod.startDate,
                                             endDate=endDate,
                                             invalidationDate=self.enquiryPeriod and self.enquiryPeriod.invalidationDate,
                                             clarificationsUntil=calculate_business_date(endDate, ENQUIRY_STAND_STILL_TIME, self, True)))
     now = get_now()
     self.date = now
     if self.lots:
         for lot in self.lots:
             lot.date = now
 def invalidate_bids_data(self):
     if self.auctionPeriod and self.auctionPeriod.startDate and self.auctionPeriod.shouldStartAfter \
             and self.auctionPeriod.startDate > calculate_business_date(parse_date(self.auctionPeriod.shouldStartAfter), AUCTION_PERIOD_TIME, self, True):
         self.auctionPeriod.startDate = None
     for lot in self.lots:
         if lot.auctionPeriod and lot.auctionPeriod.startDate and lot.auctionPeriod.shouldStartAfter \
                 and lot.auctionPeriod.startDate > calculate_business_date(parse_date(lot.auctionPeriod.shouldStartAfter), AUCTION_PERIOD_TIME, self, True):
             lot.auctionPeriod.startDate = None
     self.enquiryPeriod.invalidationDate = get_now()
     for bid in self.bids:
         if bid.status not in ["deleted", "draft"]:
             bid.status = "invalid"
 def invalidate_bids_data(self):
     if self.auctionPeriod and self.auctionPeriod.startDate and self.auctionPeriod.shouldStartAfter \
             and self.auctionPeriod.startDate > calculate_business_date(parse_date(self.auctionPeriod.shouldStartAfter), AUCTION_PERIOD_TIME, self, True):
         self.auctionPeriod.startDate = None
     for lot in self.lots:
         if lot.auctionPeriod and lot.auctionPeriod.startDate and lot.auctionPeriod.shouldStartAfter \
                 and lot.auctionPeriod.startDate > calculate_business_date(parse_date(lot.auctionPeriod.shouldStartAfter), AUCTION_PERIOD_TIME, self, True):
             lot.auctionPeriod.startDate = None
     self.enquiryPeriod.invalidationDate = get_now()
     for bid in self.bids:
         if bid.status != "deleted":
             bid.status = "invalid"
Ejemplo n.º 12
0
 def complaintPeriod(self):
     if self.tenderPeriod.startDate < COMPLAINT_OLD_SUBMIT_TIME_BEFORE:
         return Period(
             dict(startDate=self.tenderPeriod.startDate,
                  endDate=calculate_business_date(
                      self.tenderPeriod.endDate, -COMPLAINT_OLD_SUBMIT_TIME,
                      self)))
     else:
         normalized_end = calculate_normalized_date(
             self.tenderPeriod.endDate, self)
         return Period(
             dict(startDate=self.tenderPeriod.startDate,
                  endDate=calculate_business_date(normalized_end,
                                                  -COMPLAINT_SUBMIT_TIME,
                                                  self, True)))
Ejemplo n.º 13
0
 def complaintPeriod(self):
     normalized_end = calculate_normalized_date(self.tenderPeriod.endDate,
                                                self)
     return Period(
         dict(startDate=self.tenderPeriod.startDate,
              endDate=calculate_business_date(normalized_end,
                                              -COMPLAINT_SUBMIT_TIME,
                                              self)))
Ejemplo n.º 14
0
 def validate_update_tender(self, operation):
     tender = self.request.validated['tender']
     if tender.status not in ['active.tendering']:
         self.request.errors.add('body', 'data', 'Can\'t {} lot in current ({}) tender status'.format(operation, tender.status))
         self.request.errors.status = 403
         return
     if calculate_business_date(get_now(), TENDERING_EXTRA_PERIOD) > tender.tenderPeriod.endDate:
         self.request.errors.add('body', 'data', 'tenderPeriod should be extended by {0.days} days'.format(TENDERING_EXTRA_PERIOD))
         self.request.errors.status = 403
         return
     return True
Ejemplo n.º 15
0
 def validate_update_tender(self, operation):
      if self.request.authenticated_role != 'auction' and self.request.validated['tender_status'] not in ['active.tendering', STAGE2_STATUS] or \
         self.request.authenticated_role == 'auction' and self.request.validated['tender_status'] not in ['active.auction', 'active.qualification']:
          self.request.errors.add('body', 'data', 'Can\'t {} document in current ({}) tender status'.format(operation, self.request.validated['tender_status']))
          self.request.errors.status = 403
          return
      if self.request.validated['tender_status'] == 'active.tendering' and calculate_business_date(get_now(), TENDERING_EXTRA_PERIOD, self.request.validated['tender']) > self.request.validated['tender'].tenderPeriod.endDate:
          self.request.errors.add('body', 'data', 'tenderPeriod should be extended by {0.days} days'.format(TENDERING_EXTRA_PERIOD))
          self.request.errors.status = 403
          return
      return True
 def validate_update_tender(self, operation):
     if self.request.authenticated_role != 'auction' and self.request.validated['tender_status'] != 'active.tendering' or \
        self.request.authenticated_role == 'auction' and self.request.validated['tender_status'] not in ['active.auction', 'active.qualification']:
         self.request.errors.add('body', 'data', 'Can\'t {} document in current ({}) tender status'.format(operation, self.request.validated['tender_status']))
         self.request.errors.status = 403
         return
     if self.request.validated['tender_status'] == 'active.tendering' and calculate_business_date(get_now(), TENDERING_EXTRA_PERIOD) > self.request.validated['tender'].tenderPeriod.endDate:
         self.request.errors.add('body', 'data', 'tenderPeriod should be extended by {0.days} days'.format(TENDERING_EXTRA_PERIOD))
         self.request.errors.status = 403
         return
     return True
Ejemplo n.º 17
0
 def shouldStartAfter(self):
     if self.endDate:
         return
     tender = self.__parent__
     if tender.lots or tender.status not in ['active.tendering', 'active.pre-qualification.stand-still', 'active.auction']:
         return
     if tender.status == 'active.tendering' and tender.tenderPeriod.endDate:
         return calculate_business_date(tender.tenderPeriod.endDate, TENDERING_AUCTION, tender).isoformat()
     elif self.startDate and get_now() > calc_auction_end_time(tender.numberOfBids, self.startDate):
         return calc_auction_end_time(tender.numberOfBids, self.startDate).isoformat()
     elif tender.qualificationPeriod and tender.qualificationPeriod.endDate:
         return tender.qualificationPeriod.endDate.isoformat()
 def validate_update_tender(self, operation):
     tender = self.request.validated['tender']
     if self.request.authenticated_role != 'auction' and self.request.validated['tender_status'] != 'active.tendering' or \
        self.request.authenticated_role == 'auction' and self.request.validated['tender_status'] not in ['active.auction', 'active.qualification']:
         self.request.errors.add('body', 'data', 'Can\'t {} document in current ({}) tender status'.format(operation, self.request.validated['tender_status']))
         self.request.errors.status = 403
         return
     if self.request.validated['tender_status'] == 'active.tendering':
         if calculate_business_date(get_now(), timedelta(days=7)) >= tender.tenderPeriod.endDate:
             self.request.errors.add('body', 'data', 'tenderPeriod should be extended by 7 days')
             self.request.errors.status = 403
             return
     return True
 def collection_post(self):
     """Post a complaint
     """
     tender = self.context
     if tender.status != 'active.tendering':
         self.request.errors.add('body', 'data', 'Can\'t add complaint in current ({}) tender status'.format(tender.status))
         self.request.errors.status = 403
         return
     complaint = self.request.validated['complaint']
     if complaint.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
         complaint.dateSubmitted = get_now()
     elif complaint.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
         complaint.dateSubmitted = get_now()
         complaint.type = 'complaint'
     else:
         complaint.status = 'draft'
     complaint.complaintID = '{}.{}{}'.format(tender.tenderID, self.server_id, self.complaints_len(tender) + 1)
     set_ownership(complaint, self.request)
     tender.complaints.append(complaint)
     if save_tender(self.request):
         self.LOGGER.info('Created tender complaint {}'.format(complaint.id),
                     extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_complaint_create'}, {'complaint_id': complaint.id}))
         self.request.response.status = 201
         self.request.response.headers['Location'] = self.request.route_url('Tender Complaints', tender_id=tender.id, complaint_id=complaint.id)
         return {
             'data': complaint.serialize(tender.status),
             'access': {
                 'token': complaint.owner_token
             }
         }
 def shouldStartAfter(self):
     if self.endDate:
         return
     tender = self.__parent__
     if tender.lots or tender.status not in ['active.tendering', 'active.pre-qualification.stand-still', 'active.auction']:
         return
     start_after = None
     if tender.status == 'active.tendering' and tender.tenderPeriod.endDate:
         start_after = calculate_business_date(tender.tenderPeriod.endDate, TENDERING_AUCTION, tender)
     elif self.startDate and get_now() > calc_auction_end_time(tender.numberOfBids, self.startDate):
         start_after = calc_auction_end_time(tender.numberOfBids, self.startDate)
     elif tender.qualificationPeriod and tender.qualificationPeriod.endDate:
         start_after = tender.qualificationPeriod.endDate
     if start_after:
         return rounding_shouldStartAfter(start_after, tender).isoformat()
 def validate_update_tender(self, operation):
     tender = self.request.validated["tender"]
     if tender.status not in ["active.tendering"]:
         self.request.errors.add(
             "body", "data", "Can't {} lot in current ({}) tender status".format(operation, tender.status)
         )
         self.request.errors.status = 403
         return
     if calculate_business_date(get_now(), TENDERING_EXTRA_PERIOD, tender) > tender.tenderPeriod.endDate:
         self.request.errors.add(
             "body", "data", "tenderPeriod should be extended by {0.days} days".format(TENDERING_EXTRA_PERIOD)
         )
         self.request.errors.status = 403
         return
     return True
Ejemplo n.º 22
0
 def shouldStartAfter(self):
     if self.endDate:
         return
     tender = get_tender(self)
     lot = self.__parent__
     if tender.status not in ['active.tendering', 'active.pre-qualification.stand-still', 'active.auction'] or lot.status != 'active':
         return
     start_after = None
     if tender.status == 'active.tendering' and tender.tenderPeriod.endDate:
         start_after = calculate_business_date(tender.tenderPeriod.endDate, TENDERING_AUCTION, tender)
     elif self.startDate and get_now() > calc_auction_end_time(lot.numberOfBids, self.startDate):
         start_after = calc_auction_end_time(lot.numberOfBids, self.startDate)
     elif tender.qualificationPeriod and tender.qualificationPeriod.endDate:
         start_after = tender.qualificationPeriod.endDate
     if start_after:
         return rounding_shouldStartAfter(start_after, tender).isoformat()
Ejemplo n.º 23
0
 def validate_update_tender(self, operation):
     tender = self.request.validated['tender']
     if tender.status not in ['active.tendering']:
         self.request.errors.add(
             'body', 'data',
             'Can\'t {} lot in current ({}) tender status'.format(
                 operation, tender.status))
         self.request.errors.status = 403
         return
     if calculate_business_date(get_now(), TENDERING_EXTRA_PERIOD,
                                tender) > tender.tenderPeriod.endDate:
         self.request.errors.add(
             'body', 'data',
             'tenderPeriod should be extended by {0.days} days'.format(
                 TENDERING_EXTRA_PERIOD))
         self.request.errors.status = 403
         return
     return True
Ejemplo n.º 24
0
 def shouldStartAfter(self):
     if self.endDate:
         return
     tender = get_tender(self)
     lot = self.__parent__
     if tender.status not in [
             'active.tendering', 'active.pre-qualification.stand-still',
             'active.auction'
     ] or lot.status != 'active':
         return
     if tender.status == 'active.tendering' and tender.tenderPeriod.endDate:
         return calculate_business_date(tender.tenderPeriod.endDate,
                                        TENDERING_AUCTION).isoformat()
     elif self.startDate and get_now() > calc_auction_end_time(
             lot.numberOfBids, self.startDate):
         return calc_auction_end_time(lot.numberOfBids,
                                      self.startDate).isoformat()
     elif tender.qualificationPeriod and tender.qualificationPeriod.endDate:
         return tender.qualificationPeriod.endDate.isoformat()
 def patch(self):
     """Post an Answer
     """
     tender = self.request.validated['tender']
     if tender.status != 'active.tendering':
         self.request.errors.add('body', 'data', 'Can\'t update question 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.relatedItem]):
         self.request.errors.add('body', 'data', 'Can update question only in active lot status')
         self.request.errors.status = 403
         return
     now = get_now()
     #TODO business days
     if now > calculate_business_date(tender.enquiryPeriod.endDate, ENQUIRY_STAND_STILL_TIME):
         self.request.errors.add('body', 'data', 'Can update question only in enquiryPeriod.stand-still')
         self.request.errors.status = 403
         return
     if apply_patch(self.request, src=self.request.context.serialize()):
         self.LOGGER.info('Updated tender question {}'.format(self.request.context.id),
                     extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_question_patch'}))
         return {'data': self.request.context.serialize(tender.status)}
Ejemplo n.º 26
0
    def patch(self):
        """Tender Edit (partial)

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

        .. sourcecode:: http

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

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

        And here is the response to be expected:

        .. sourcecode:: http

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

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

        """
        tender = self.context
        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 ['active.pre-qualification.stand-still', tender.status]:
            self.request.errors.add('body', 'data', 'Can\'t update tender status')
            self.request.errors.status = 403
            return

        if self.request.authenticated_role == 'tender_owner' and self.request.validated['tender_status'] == 'active.tendering':
            if 'tenderPeriod' in data and 'endDate' in data['tenderPeriod']:
                self.request.validated['tender'].tenderPeriod.import_data(data['tenderPeriod'])
                if calculate_business_date(get_now(), TENDERING_EXTRA_PERIOD) > self.request.validated['tender'].tenderPeriod.endDate:
                    self.request.errors.add('body', 'data', 'tenderPeriod should be extended by {0.days} days'.format(TENDERING_EXTRA_PERIOD))
                    self.request.errors.status = 403
                    return
                self.request.validated['tender'].initialize()
                self.request.validated['data']["enquiryPeriod"] = self.request.validated['tender'].enquiryPeriod.serialize()

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

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

        Example request to change the award:

        .. sourcecode:: http

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

            {
                "data": {
                    "value": {
                        "amount": 600
                    }
                }
            }

        And here is the response to be expected:

        .. sourcecode:: http

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

            {
                "data": {
                    "id": "4879d3f8ee2443169b5fbbc9f89fa607",
                    "date": "2014-10-28T11:44:17.947Z",
                    "status": "active",
                    "suppliers": [
                        {
                            "id": {
                                "name": "Державне управління справами",
                                "scheme": "https://ns.openprocurement.org/ua/edrpou",
                                "uid": "00037256",
                                "uri": "http://www.dus.gov.ua/"
                            },
                            "address": {
                                "countryName": "Україна",
                                "postalCode": "01220",
                                "region": "м. Київ",
                                "locality": "м. Київ",
                                "streetAddress": "вул. Банкова, 11, корпус 1"
                            }
                        }
                    ],
                    "value": {
                        "amount": 600,
                        "currency": "UAH",
                        "valueAddedTaxIncluded": true
                    }
                }
            }

        """
        tender = self.request.validated['tender']
        if tender.status != 'active':
            self.request.errors.add(
                'body', 'data',
                'Can\'t update award in current ({}) tender status'.format(
                    tender.status))
            self.request.errors.status = 403
            return

        award = self.request.context
        if not self.validate_lot_cancellation('update'):
            return
        award_status = award.status
        apply_patch(self.request,
                    save=False,
                    src=self.request.context.serialize())
        if award.status == "active" and not award.qualified:
            self.request.errors.add(
                'body', 'data',
                'Can\'t update award to active status with not qualified')
            self.request.errors.status = 403
            return
        if award.lotID and \
                [aw.lotID for aw in tender.awards if aw.status in['pending', 'active']].count(award.lotID) > 1:
            self.request.errors.add(
                'body', 'lotID', 'Another award is already using this lotID.')
            self.request.errors.status = 403
            return
        if award_status == 'pending' and award.status == 'active':
            normalized_end = calculate_normalized_date(get_now(), tender, True)
            award.complaintPeriod.endDate = calculate_business_date(
                normalized_end, self.stand_still_delta, tender)
            tender.contracts.append(
                type(tender).contracts.model_class({
                    'awardID':
                    award.id,
                    'suppliers':
                    award.suppliers,
                    'date':
                    get_now(),
                    'value':
                    award.value,
                    'items':
                    [i for i in tender.items if i.relatedLot == award.lotID],
                    'contractID':
                    '{}-{}{}'.format(tender.tenderID, self.server_id,
                                     len(tender.contracts) + 1)
                }))
            # add_next_award(self.request)
        elif award_status == 'active' and award.status == 'cancelled' and any(
            [i.status == 'satisfied' for i in award.complaints]):
            now = get_now()
            cancelled_awards = []
            for i in tender.awards:
                if i.lotID != award.lotID:
                    continue
                if not i.complaintPeriod.endDate or i.complaintPeriod.endDate > now:
                    i.complaintPeriod.endDate = now
                i.status = 'cancelled'
                cancelled_awards.append(i.id)
            for i in tender.contracts:
                if i.awardID in cancelled_awards:
                    i.status = 'cancelled'
        elif award_status == 'active' and award.status == 'cancelled':
            now = get_now()
            if award.complaintPeriod.endDate > now:
                award.complaintPeriod.endDate = now
            for i in tender.contracts:
                if i.awardID == award.id:
                    i.status = 'cancelled'
            # add_next_award(self.request)
        elif award_status == 'pending' and award.status == 'unsuccessful':
            award.complaintPeriod.endDate = get_now()
            # add_next_award(self.request)
        elif award_status == 'unsuccessful' and award.status == 'cancelled' and any(
            [i.status == 'satisfied' for i in award.complaints]):
            now = get_now()
            cancelled_awards = []
            for i in tender.awards:
                if i.lotID != award.lotID:
                    continue
                if not i.complaintPeriod.endDate or i.complaintPeriod.endDate > now:
                    i.complaintPeriod.endDate = now
                i.status = 'cancelled'
                cancelled_awards.append(i.id)
            for i in tender.contracts:
                if i.awardID in cancelled_awards:
                    i.status = 'cancelled'
        elif award_status != award.status:
            self.request.errors.add(
                'body', 'data',
                'Can\'t update award in current ({}) status'.format(
                    award_status))
            self.request.errors.status = 403
            return
        elif self.request.authenticated_role != 'Administrator' and award_status != 'pending':
            self.request.errors.add(
                'body', 'data',
                'Can\'t update award in current ({}) status'.format(
                    award_status))
            self.request.errors.status = 403
            return

        if save_tender(self.request):
            self.LOGGER.info(
                'Updated tender award {}'.format(self.request.context.id),
                extra=context_unpack(self.request,
                                     {'MESSAGE_ID': 'tender_award_patch'},
                                     {'TENDER_REV': tender.rev}))
            return {'data': award.serialize("view")}
Ejemplo n.º 28
0
    def patch(self):
        """Update of award

        Example request to change the award:

        .. sourcecode:: http

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

            {
                "data": {
                    "value": {
                        "amount": 600
                    }
                }
            }

        And here is the response to be expected:

        .. sourcecode:: http

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

            {
                "data": {
                    "id": "4879d3f8ee2443169b5fbbc9f89fa607",
                    "date": "2014-10-28T11:44:17.947Z",
                    "status": "active",
                    "suppliers": [
                        {
                            "id": {
                                "name": "Державне управління справами",
                                "scheme": "https://ns.openprocurement.org/ua/edrpou",
                                "uid": "00037256",
                                "uri": "http://www.dus.gov.ua/"
                            },
                            "address": {
                                "countryName": "Україна",
                                "postalCode": "01220",
                                "region": "м. Київ",
                                "locality": "м. Київ",
                                "streetAddress": "вул. Банкова, 11, корпус 1"
                            }
                        }
                    ],
                    "value": {
                        "amount": 600,
                        "currency": "UAH",
                        "valueAddedTaxIncluded": true
                    }
                }
            }

        """
        tender = self.request.validated['tender']
        if tender.status not in ['active.qualification', 'active.awarded']:
            self.request.errors.add('body', 'data', 'Can\'t update award in current ({}) tender status'.format(tender.status))
            self.request.errors.status = 403
            return
        award = self.request.context
        if any([i.status != 'active' for i in tender.lots if i.id == award.lotID]):
            self.request.errors.add('body', 'data', 'Can update award only in active lot status')
            self.request.errors.status = 403
            return
        award_status = award.status
        apply_patch(self.request, save=False, src=self.request.context.serialize())
        if award_status == 'pending' and award.status == 'active':
            award.complaintPeriod.endDate = calculate_business_date(get_now(), STAND_STILL_TIME)
            tender.contracts.append(type(tender).contracts.model_class({
                'awardID': award.id,
                'suppliers': award.suppliers,
                'value': award.value,
                'items': [i for i in tender.items if i.relatedLot == award.lotID ],
                'contractID': '{}-{}{}'.format(tender.tenderID, self.server_id, len(tender.contracts) +1) }))
            add_next_award(self.request)
        elif award_status == 'active' and award.status == 'cancelled':
            award.complaintPeriod.endDate = get_now()
            for i in tender.contracts:
                if i.awardID == award.id:
                    i.status = 'cancelled'
            add_next_award(self.request)
        elif award_status == 'pending' and award.status == 'unsuccessful':
            award.complaintPeriod.endDate = calculate_business_date(get_now(), STAND_STILL_TIME)
            add_next_award(self.request)
        elif award_status == 'unsuccessful' and award.status == 'cancelled' and any([i.status == 'satisfied' for i in award.complaints]):
            if tender.status == 'active.awarded':
                tender.status = 'active.qualification'
                tender.awardPeriod.endDate = None
            now = get_now()
            award.complaintPeriod.endDate = now
            cancelled_awards = []
            for i in tender.awards:
                if i.lotID != award.lotID:
                    continue
                i.complaintPeriod.endDate = now
                i.status = 'cancelled'
                cancelled_awards.append(i.id)
            for i in tender.contracts:
                if i.awardID in cancelled_awards:
                    i.status = 'cancelled'
            add_next_award(self.request)
        else:
            self.request.errors.add('body', 'data', 'Can\'t update award in current ({}) status'.format(award_status))
            self.request.errors.status = 403
            return
        if save_tender(self.request):
            self.LOGGER.info('Updated tender award {}'.format(self.request.context.id),
                        extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_award_patch'}))
            return {'data': award.serialize("view")}
 def complaintPeriod(self):
     return Period(dict(startDate=self.tenderPeriod.startDate, endDate=calculate_business_date(self.tenderPeriod.startDate , COMPLAINT_SUBMIT_TIME, self)))
Ejemplo n.º 30
0
 def initialize(self):
     self.enquiryPeriod = Period(dict(startDate=self.tenderPeriod.startDate, endDate=calculate_business_date(self.tenderPeriod.endDate, -ENQUIRY_PERIOD_TIME)))
Ejemplo n.º 31
0
 def tender_enquiryPeriod(self):
     endDate = calculate_business_date(self.tenderPeriod.endDate, -QUESTIONS_STAND_STILL, self)
     return EnquiryPeriod(dict(startDate=self.tenderPeriod.startDate,
                               endDate=endDate,
                               invalidationDate=self.enquiryPeriod and self.enquiryPeriod.invalidationDate,
                               clarificationsUntil=calculate_business_date(endDate, ENQUIRY_STAND_STILL_TIME, self, True)))
 def complaintPeriod(self):
     return Period(dict(startDate=self.tenderPeriod.startDate, endDate=calculate_business_date(self.tenderPeriod.endDate, -COMPLAINT_SUBMIT_TIME)))
Ejemplo n.º 33
0
 def initialize(self):
     if not self.tenderPeriod.startDate:
         self.tenderPeriod.startDate = get_now()
     self.enquiryPeriod = Period(dict(startDate=self.tenderPeriod.startDate, endDate=calculate_business_date(self.tenderPeriod.endDate, -ENQUIRY_PERIOD_TIME)))
     if hasattr(self, "auctionPeriod") and hasattr(self.auctionPeriod, "startDate"):
         self.auctionPeriod.startDate = ""
Ejemplo n.º 34
0
 def validate_tenderPeriod(self, data, period):
     if period and calculate_business_date(period.startDate, TENDER_PERIOD) > period.endDate:
         raise ValidationError(u"tenderPeriod should be greater than 15 days")
Ejemplo n.º 35
0
 def tender_enquiryPeriod(self):
     return Period(dict(startDate=self.tenderPeriod.startDate, endDate=calculate_business_date(self.tenderPeriod.endDate, -ENQUIRY_PERIOD_TIME)))
Ejemplo n.º 36
0
    def patch(self):
        """Tender Edit (partial)

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

        .. sourcecode:: http

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

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

        And here is the response to be expected:

        .. sourcecode:: http

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

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

        """
        tender = self.context
        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 [
                    'active.pre-qualification.stand-still', tender.status
                ]:
            self.request.errors.add('body', 'data',
                                    'Can\'t update tender status')
            self.request.errors.status = 403
            return

        if self.request.authenticated_role == 'tender_owner' and self.request.validated[
                'tender_status'] == 'active.tendering':
            if 'tenderPeriod' in data and 'endDate' in data['tenderPeriod']:
                self.request.validated['tender'].tenderPeriod.import_data(
                    data['tenderPeriod'])
                if calculate_business_date(
                        get_now(), TENDERING_EXTRA_PERIOD,
                        self.request.validated['tender']
                ) > self.request.validated['tender'].tenderPeriod.endDate:
                    self.request.errors.add(
                        'body', 'data',
                        'tenderPeriod should be extended by {0.days} days'.
                        format(TENDERING_EXTRA_PERIOD))
                    self.request.errors.status = 403
                    return
                self.request.validated['tender'].initialize()
                self.request.validated['data'][
                    "enquiryPeriod"] = self.request.validated[
                        'tender'].enquiryPeriod.serialize()

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

        save_tender(self.request)
        self.LOGGER.info('Updated tender {}'.format(tender.id),
                         extra=context_unpack(self.request,
                                              {'MESSAGE_ID': 'tender_patch'}))
        return {'data': tender.serialize(tender.status)}
Ejemplo n.º 37
0
 def validate_tenderPeriod(self, data, period):
     if period and calculate_business_date(period.startDate, TENDER_PERIOD,
                                           data, True) > period.endDate:
         raise ValidationError(
             u"tenderPeriod should be greater than {0.days} working days".
             format(TENDER_PERIOD))
 def validate_tenderPeriod(self, data, period):
     # if data['_rev'] is None when tender was created just now
     if not data['_rev'] and calculate_business_date(get_now(), -timedelta(minutes=10)) >= period.startDate:
         raise ValidationError(u"tenderPeriod.startDate should be in greater than current date")
     if period and calculate_business_date(period.startDate, TENDER_PERIOD) > period.endDate:
         raise ValidationError(u"tenderPeriod should be greater than 15 days")
Ejemplo n.º 39
0
    def patch(self):
        """Tender Edit (partial)

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

        .. sourcecode:: http

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

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

        And here is the response to be expected:

        .. sourcecode:: http

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

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

        """
        tender = self.context
        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

        # TODO move to validators
        if self.request.authenticated_role == 'tender_owner' and self.request.validated['tender_status'] == 'active.tendering':
            if 'tenderPeriod' in data and 'endDate' in data['tenderPeriod']:
                self.request.validated['tender'].tenderPeriod.import_data(data['tenderPeriod'])
                if calculate_business_date(get_now(), timedelta(days=7)) > self.request.validated['tender'].tenderPeriod.endDate:
                    self.request.errors.add('body', 'data', 'tenderPeriod should be extended by 7 days')
                    self.request.errors.status = 403
                    return
                self.request.validated['tender'].initialize()
                self.request.validated['data']["enquiryPeriod"] = self.request.validated['tender'].enquiryPeriod.serialize()
                self.request.validated['data']["auctionPeriod"] = {'startDate': None}

        if self.request.authenticated_role == 'chronograph':
            apply_patch(self.request, save=False, src=self.request.validated['tender_src'])
            check_status(self.request)
            save_tender(self.request)
        else:
            if tender.status == data.get('status', tender.status):
                # invalidate bids on tender change
                data = get_invalidated_bids_data(self.request)
            else:
                data = self.request.validated['data']
            apply_patch(self.request, data=data, 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)}
 def tender_enquiryPeriod(self):
     endDate = calculate_business_date(self.tenderPeriod.endDate, -ENQUIRY_PERIOD_TIME, self)
     return EnquiryPeriod(dict(startDate=self.tenderPeriod.startDate,
                               endDate=endDate,
                               clarificationsUntil=calculate_business_date(endDate, ENQUIRY_STAND_STILL_TIME, self, True)))
    def patch(self):
        """Tender Edit (partial)

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

        .. sourcecode:: http

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

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

        And here is the response to be expected:

        .. sourcecode:: http

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

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

        """
        tender = self.context
        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 ["active.pre-qualification.stand-still", tender.status]
        ):
            self.request.errors.add("body", "data", "Can't update tender status")
            self.request.errors.status = 403
            return

        if (
            self.request.authenticated_role == "tender_owner"
            and self.request.validated["tender_status"] == "active.tendering"
        ):
            if "tenderPeriod" in data and "endDate" in data["tenderPeriod"]:
                self.request.validated["tender"].tenderPeriod.import_data(data["tenderPeriod"])
                if (
                    calculate_business_date(get_now(), TENDERING_EXTRA_PERIOD, self.request.validated["tender"])
                    > self.request.validated["tender"].tenderPeriod.endDate
                ):
                    self.request.errors.add(
                        "body",
                        "data",
                        "tenderPeriod should be extended by {0.days} days".format(TENDERING_EXTRA_PERIOD),
                    )
                    self.request.errors.status = 403
                    return
                self.request.validated["tender"].initialize()
                self.request.validated["data"]["enquiryPeriod"] = self.request.validated[
                    "tender"
                ].enquiryPeriod.serialize()

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

        save_tender(self.request)
        self.LOGGER.info(
            "Updated tender {}".format(tender.id), extra=context_unpack(self.request, {"MESSAGE_ID": "tender_patch"})
        )
        return {"data": tender.serialize(tender.status)}
 def tender_enquiryPeriod(self):
     return Period(dict(startDate=self.tenderPeriod.startDate, endDate=calculate_business_date(self.tenderPeriod.endDate, -ENQUIRY_PERIOD_TIME)))
Ejemplo n.º 43
0
 def complaintPeriod(self):
     normalized_end = calculate_normalized_date(self.tenderPeriod.endDate, self)
     return Period(dict(startDate=self.tenderPeriod.startDate, endDate=calculate_business_date(normalized_end, -COMPLAINT_SUBMIT_TIME, self)))
 def validate_tenderPeriod(self, data, period):
     if period and calculate_business_date(period.startDate, TENDER_PERIOD) > period.endDate:
         raise ValidationError(u"tenderPeriod should be greater than 5 days")
 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")}
 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 initialize(self):
     endDate = calculate_business_date(self.tenderPeriod.endDate, -ENQUIRY_PERIOD_TIME, self)
     self.enquiryPeriod = EnquiryPeriod(dict(startDate=self.tenderPeriod.startDate,
                                             endDate=endDate,
                                             invalidationDate=self.enquiryPeriod and self.enquiryPeriod.invalidationDate,
                                             clarificationsUntil=calculate_business_date(endDate, ENQUIRY_STAND_STILL_TIME, self, True)))