def prepare_qualifications(request, bids=[], lotId=None):
    """ creates Qualification for each Bid
    """
    new_qualifications = []
    tender = request.validated['tender']
    if not bids:
        bids = tender.bids
    if tender.lots:
        active_lots = [lot.id for lot in tender.lots if lot.status == 'active']
        for bid in bids:
            if bid.status not in ['invalid', 'deleted']:
                for lotValue in bid.lotValues:
                    if lotValue.status == 'pending' and lotValue.relatedLot in active_lots:
                        if lotId:
                            if lotValue.relatedLot == lotId:
                                qualification = Qualification({'bidID': bid.id, 'status': 'pending', 'lotID': lotId})
                                qualification.date = get_now()
                                tender.qualifications.append(qualification)
                                new_qualifications.append(qualification.id)
                        else:
                            qualification = Qualification({'bidID': bid.id, 'status': 'pending', 'lotID': lotValue.relatedLot})
                            qualification.date = get_now()
                            tender.qualifications.append(qualification)
                            new_qualifications.append(qualification.id)
    else:
        for bid in bids:
            if bid.status == 'pending':
                qualification = Qualification({'bidID': bid.id, 'status': 'pending'})
                qualification.date = get_now()
                tender.qualifications.append(qualification)
                new_qualifications.append(qualification.id)
    return new_qualifications
Пример #2
0
    def patch(self):
        """Update of proposal

        Example request to change bid proposal:

        .. sourcecode:: http

            PATCH /tenders/4879d3f8ee2443169b5fbbc9f89fa607/bids/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": {
                    "value": {
                        "amount": 600,
                        "currency": "UAH",
                        "valueAddedTaxIncluded": true
                    }
                }
            }

        """
        if self.request.validated['tender_status'] != 'active.tendering':
            self.request.errors.add('body', 'data', 'Can\'t update bid in current ({}) tender status'.format(self.request.validated['tender_status']))
            self.request.errors.status = 403
            return
        if self.request.authenticated_role != 'Administrator':
            bid_status_to = self.request.validated['data'].get("status", self.request.context.status)
            if bid_status_to != 'pending':
                self.request.errors.add('body', 'bid', 'Can\'t update bid to ({}) status'.format(bid_status_to))
                self.request.errors.status = 403
                return
        value = self.request.validated['data'].get("value") and self.request.validated['data']["value"].get("amount")
        if value and value != self.request.context.get("value", {}).get("amount"):
            self.request.validated['data']['date'] = get_now().isoformat()
        if self.request.context.lotValues:
            lotValues = dict([(i.relatedLot, i.value.amount) for i in self.request.context.lotValues])
            for lotvalue in self.request.validated['data'].get("lotValues", []):
                if lotvalue['relatedLot'] in lotValues and lotvalue.get("value", {}).get("amount") != lotValues[lotvalue['relatedLot']]:
                    lotvalue['date'] = get_now().isoformat()
        self.request.validated['tender'].modified = False
        if apply_patch(self.request, src=self.request.context.serialize()):
            self.LOGGER.info('Updated tender bid {}'.format(self.request.context.id),
                        extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_bid_patch'}))
            return {'data': self.request.context.serialize("view")}
 def collection_post(self):
     """Post a complaint
     """
     auction = self.context
     if auction.status not in ['active.enquiries', 'active.tendering']:
         self.request.errors.add('body', 'data', 'Can\'t add complaint in current ({}) auction status'.format(auction.status))
         self.request.errors.status = 403
         return
     complaint = self.request.validated['complaint']
     complaint.date = get_now()
     if complaint.status == 'claim':
         complaint.dateSubmitted = get_now()
     else:
         complaint.status = 'draft'
     complaint.complaintID = '{}.{}{}'.format(auction.auctionID, self.server_id, sum([len(i.complaints) for i in auction.awards], len(auction.complaints)) + 1)
     set_ownership(complaint, self.request)
     auction.complaints.append(complaint)
     if save_auction(self.request):
         self.LOGGER.info('Created auction complaint {}'.format(complaint.id),
                     extra=context_unpack(self.request, {'MESSAGE_ID': 'auction_complaint_create'}, {'complaint_id': complaint.id}))
         self.request.response.status = 201
         route = self.request.matched_route.name.replace("collection_", "")
         self.request.response.headers['Location'] = self.request.current_route_url(_route_name=route, complaint_id=complaint.id, _query={})
         return {
             'data': complaint.serialize(auction.status),
             'access': {
                 'token': complaint.owner_token
             }
         }
 def collection_post(self):
     """Post a complaint for award
     """
     tender = self.request.validated['tender']
     if tender.status != 'active':
         self.request.errors.add('body', 'data', 'Can\'t add complaint in current ({}) tender status'.format(tender.status))
         self.request.errors.status = 403
         return
     if self.context.complaintPeriod and \
        (self.context.complaintPeriod.startDate and self.context.complaintPeriod.startDate > get_now() or
             self.context.complaintPeriod.endDate and self.context.complaintPeriod.endDate < get_now()):
         self.request.errors.add('body', 'data', 'Can add complaint only in complaintPeriod')
         self.request.errors.status = 403
         return
     complaint = self.request.validated['complaint']
     complaint.date = get_now()
     complaint.type = 'complaint'
     if complaint.status == 'pending':
         complaint.dateSubmitted = get_now()
     else:
         complaint.status = 'draft'
     complaint.complaintID = '{}.{}{}'.format(tender.tenderID, self.server_id, sum([len(i.complaints) for i in tender.awards], 1))
     set_ownership(complaint, self.request)
     self.context.complaints.append(complaint)
     if save_tender(self.request):
         self.LOGGER.info('Created tender award complaint {}'.format(complaint.id),
                     extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_award_complaint_create'}, {'complaint_id': complaint.id}))
         self.request.response.status = 201
         self.request.response.headers['Location'] = self.request.route_url('Tender negotiation Award Complaints', tender_id=tender.id, award_id=self.request.validated['award_id'], complaint_id=complaint['id'])
         return {
             'data': complaint.serialize("view"),
             'access': {
                 'token': complaint.owner_token
             }
         }
    def test_empty_listing(self):
        before = get_now().isoformat()
        response = self.app.get('/tenders')
        after = get_now().isoformat()
        self.assertEqual(response.status, '200 OK')
        self.assertEqual(response.content_type, 'application/json')
        self.assertEqual(response.json['data'], [])
        self.assertFalse('{\n    "' in response.body)
        self.assertFalse('callback({' in response.body)
        self.assertTrue(before < response.json['next_page']['offset'] < after)

        response = self.app.get('/tenders?opt_jsonp=callback')
        self.assertEqual(response.status, '200 OK')
        self.assertEqual(response.content_type, 'application/javascript')
        self.assertFalse('{\n    "' in response.body)
        self.assertIn('callback({', response.body)

        response = self.app.get('/tenders?opt_pretty=1')
        self.assertEqual(response.status, '200 OK')
        self.assertEqual(response.content_type, 'application/json')
        self.assertIn('{\n    "', response.body)
        self.assertFalse('callback({' in response.body)

        response = self.app.get('/tenders?opt_jsonp=callback&opt_pretty=1')
        self.assertEqual(response.status, '200 OK')
        self.assertEqual(response.content_type, 'application/javascript')
        self.assertIn('{\n    "', response.body)
        self.assertIn('callback({', response.body)

        response = self.app.get('/tenders?offset={}&descending=1&limit=10'.format(before))
        self.assertEqual(response.status, '200 OK')
        self.assertEqual(response.content_type, 'application/json')
        self.assertEqual(response.json['data'], [])
        self.assertIn('descending=1', response.json['next_page']['uri'])
        self.assertIn('limit=10', response.json['next_page']['uri'])
    def patch(self):
        """Update of contract
        """
        if self.request.validated['tender_status'] not in ['active']:
            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
        if self.request.context.status == 'cancelled':
            self.request.errors.add('body', 'data', 'Can\'t update contract in current ({}) status'.format(self.request.context.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':
            tender = self.request.validated['tender']
            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
            if any([
                i.status in tender.block_complaint_status and a.lotID == award.lotID
                for a in tender.awards
                for i in a.complaints
            ]):
                self.request.errors.add('body', 'data', 'Can\'t sign contract before reviewing all complaints')
                self.request.errors.status = 403
                return

        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 self.request.validated['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

        contract_status = self.request.context.status
        apply_patch(self.request, save=False, src=self.request.context.serialize())
        self.request.context.date = get_now()
        if contract_status != self.request.context.status and contract_status != 'pending' and 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_negotiation_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 initialize(self):
     if not self.enquiryPeriod.startDate:
         self.enquiryPeriod.startDate = get_now()
     if not self.tenderPeriod.startDate:
         self.tenderPeriod.startDate = self.enquiryPeriod.endDate
     now = get_now()
     self.date = now
     if self.lots:
         for lot in self.lots:
             lot.date = now
 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
     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
     apply_patch(self.request, save=False, src=complaint.serialize())
     if complaint.status == "cancelled":
         self.request.errors.add("body", "data", "Can't cancel complaint")
         self.request.errors.status = 403
         return
     if complaint.status == "resolved":
         award = self.request.validated["award"]
         if tender.status == "active.awarded":
             tender.status = "active.qualification"
             tender.awardPeriod.endDate = None
         if award.status == "unsuccessful":
             for i in tender.awards[tender.awards.index(award) :]:
                 i.complaintPeriod.endDate = get_now() + STAND_STILL_TIME
                 i.status = "cancelled"
                 for j in i.complaints:
                     if j.status == "pending":
                         j.status = "cancelled"
         for i in award.contracts:
             i.status = "cancelled"
         award.complaintPeriod.endDate = get_now() + STAND_STILL_TIME
         award.status = "cancelled"
         add_next_award(self.request)
     elif complaint.status in ["declined", "invalid"] and tender.status == "active.awarded":
         pending_complaints = [i for i in tender.complaints if i.status == "pending"]
         pending_awards_complaints = [i for a in tender.awards for i in a.complaints if i.status == "pending"]
         stand_still_ends = [a.complaintPeriod.endDate for a in tender.awards if a.complaintPeriod.endDate]
         stand_still_end = max(stand_still_ends) if stand_still_ends else get_now()
         stand_still_time_expired = stand_still_end < get_now()
         if not pending_complaints and not pending_awards_complaints and stand_still_time_expired:
             active_awards = [a for a in tender.awards if a.status == "active"]
             if not active_awards:
                 tender.status = "unsuccessful"
     if save_tender(self.request):
         LOGGER.info(
             "Updated tender award complaint {}".format(self.request.context.id),
             extra={"MESSAGE_ID": "tender_award_complaint_patch"},
         )
         return {"data": complaint.serialize("view")}
 def collection_post(self):
     """Post a complaint
     """
     tender = self.request.validated["tender"]
     if tender.status not in ["active.pre-qualification.stand-still"]:
         self.request.errors.add(
             "body", "data", "Can't add 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.context.lotID]):
         self.request.errors.add("body", "data", "Can add complaint only in active lot status")
         self.request.errors.status = 403
         return
     if tender.qualificationPeriod and (
         tender.qualificationPeriod.startDate
         and tender.qualificationPeriod.startDate > get_now()
         or tender.qualificationPeriod.endDate
         and tender.qualificationPeriod.endDate < get_now()
     ):
         self.request.errors.add("body", "data", "Can add complaint only in qualificationPeriod")
         self.request.errors.status = 403
         return
     complaint = self.request.validated["complaint"]
     complaint.relatedLot = self.context.lotID
     complaint.date = get_now()
     if complaint.status == "claim":
         complaint.dateSubmitted = get_now()
     elif complaint.status == "pending":
         complaint.type = "complaint"
         complaint.dateSubmitted = get_now()
     else:
         complaint.status = "draft"
     complaint.complaintID = "{}.{}{}".format(tender.tenderID, self.server_id, self.complaints_len(tender) + 1)
     set_ownership(complaint, self.request)
     self.context.complaints.append(complaint)
     if save_tender(self.request):
         self.LOGGER.info(
             "Created tender qualification complaint {}".format(complaint.id),
             extra=context_unpack(
                 self.request,
                 {"MESSAGE_ID": "tender_qualification_complaint_create"},
                 {"complaint_id": complaint.id},
             ),
         )
         self.request.response.status = 201
         self.request.response.headers["Location"] = self.request.route_url(
             "Tender EU Qualification Complaints",
             tender_id=tender.id,
             qualification_id=self.request.validated["qualification_id"],
             complaint_id=complaint["id"],
         )
         return {"data": complaint.serialize("view"), "access": {"token": complaint.owner_token}}
Пример #10
0
 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
     apply_patch(self.request, save=False, src=self.request.context.serialize())
     if self.request.context.status == "cancelled":
         self.request.errors.add("body", "data", "Can't cancel complaint")
         self.request.errors.status = 403
         return
     if self.request.context.status == "resolved" and tender.status != "active.enquiries":
         for i in tender.complaints:
             if i.status == "pending":
                 i.status = "cancelled"
         tender.status = "cancelled"
     elif self.request.context.status in ["declined", "invalid"] and tender.status == "active.awarded":
         pending_complaints = [i for i in tender.complaints if i.status == "pending"]
         pending_awards_complaints = [i for a in tender.awards for i in a.complaints if i.status == "pending"]
         stand_still_ends = [a.complaintPeriod.endDate for a in tender.awards if a.complaintPeriod.endDate]
         stand_still_end = max(stand_still_ends) if stand_still_ends else get_now()
         stand_still_time_expired = stand_still_end < get_now()
         if not pending_complaints and not pending_awards_complaints and stand_still_time_expired:
             active_awards = [a for a in tender.awards if a.status == "active"]
             if not active_awards:
                 tender.status = "unsuccessful"
     if save_tender(self.request):
         LOGGER.info(
             "Updated tender complaint {}".format(self.request.context.id),
             extra={"MESSAGE_ID": "tender_complaint_patch"},
         )
         return {"data": self.request.context.serialize("view")}
Пример #11
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 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()}
Пример #12
0
def from18to19(db):

    def update_documents_type(item, changed):
        for document in item.get('documents', []):
            if document.get('documentType') == 'contractAnnexes':
                document['documentType'] = 'contractAnnexe'
                changed = True
        return changed

    results = db.iterview('tenders/all', 2 ** 10, include_docs=True)
    docs = []
    for i in results:
        doc = i.doc
        changed = update_documents_type(doc, False)
        for item in ('bids', 'complaints', 'cancellations', 'contracts',
                     'awards'):
            for item_val in doc.get(item, []):
                changed = update_documents_type(item_val, changed)
                if item == 'awards':
                    for complaint in item_val.get('complaints', []):
                        changed = update_documents_type(complaint, changed)
        if changed:
            doc['dateModified'] = get_now().isoformat()
            docs.append(doc)
        if len(docs) >= 2 ** 7:
            db.update(docs)
            docs = []
    if docs:
        db.update(docs)
Пример #13
0
def from14to15(db):
    results = db.view('tenders/all', include_docs=True)
    for i in results:
        doc = i.doc
        changed = False
        if not doc.get('title'):
            doc['title'] = doc["items"][0]['description']
            changed = True
        for item in doc["items"]:
            if not item.get('classification'):
                changed = True
                item['classification'] = {
                    "scheme": u"CPV",
                    "id": u"41110000-3",
                    "description": u"Drinking water"
                }
            if not item.get('additionalClassifications'):
                changed = True
                item['additionalClassifications'] = [
                    {
                        "scheme": u"ДКПП",
                        "id": u"36.00.11-00.00",
                        "description": u"Вода питна"
                    }
                ]
        if changed:
            doc['dateModified'] = get_now().isoformat()
            db.save(doc)
Пример #14
0
def from13to14(db):
    results = db.view('tenders/all', include_docs=True)
    for i in results:
        doc = i.doc
        changed = False
        for i in doc.get('documents', []):
            changed = fix_rfc2047(i, changed)
        for c in doc.get('complaints', []):
            for i in c.get('documents', []):
                changed = fix_rfc2047(i, changed)
        for b in doc.get('bids', []):
            for i in b.get('documents', []):
                changed = fix_rfc2047(i, changed)
        for a in doc.get('awards', []):
            for i in c.get('documents', []):
                changed = fix_rfc2047(i, changed)
            for c in a.get('complaints', []):
                for i in c.get('documents', []):
                    changed = fix_rfc2047(i, changed)
            for c in a.get('contracts', []):
                for i in c.get('documents', []):
                    changed = fix_rfc2047(i, changed)
        if changed:
            doc['dateModified'] = get_now().isoformat()
            db.save(doc)
Пример #15
0
def from10to11(db):
    results = db.view('tenders/all', include_docs=True)
    for i in results:
        doc = i.doc
        changed = False
        if doc.get("procuringEntity", {}).get("identifier", {}).get("scheme"):
            changed = True
            doc["procuringEntity"]["identifier"]["scheme"] = 'UA-EDR'
        for j in doc.get('bids', []):
            for i in j.get('tenderers', []):
                if i.get("identifier", {}).get("scheme"):
                    changed = True
                    i["identifier"]["scheme"] = 'UA-EDR'
        for i in doc.get('questions', []):
            if i.get("author", {}).get("identifier", {}).get("scheme"):
                changed = True
                i["author"]["identifier"]["scheme"] = 'UA-EDR'
        for i in doc.get('complaints', []):
            if i.get("author", {}).get("identifier", {}).get("scheme"):
                changed = True
                i["author"]["identifier"]["scheme"] = 'UA-EDR'
        for j in doc.get('awards', []):
            for i in j.get('suppliers', []):
                if i.get("identifier", {}).get("scheme"):
                    changed = True
                    i["identifier"]["scheme"] = 'UA-EDR'
            for i in j.get('complaints', []):
                if i.get("author", {}).get("identifier", {}).get("scheme"):
                    changed = True
                    i["author"]["identifier"]["scheme"] = 'UA-EDR'
        if changed:
            doc['dateModified'] = get_now().isoformat()
            db.save(doc)
def calculate_normalized_date(dt, tender, ceil=False):
    if (tender.revisions[0].date if tender.revisions else get_now()) > NORMALIZED_COMPLAINT_PERIOD_FROM and \
            not (SANDBOX_MODE and tender.procurementMethodDetails):
        if ceil:
            return dt.astimezone(TZ).replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(days=1)
        return dt.astimezone(TZ).replace(hour=0, minute=0, second=0, microsecond=0)
    return dt
Пример #17
0
def set_journal_handler(event):
    request = event.request
    params = {
        'TENDERS_API_VERSION': VERSION,
        'TAGS': 'python,api',
        'USER': str(request.authenticated_userid or ''),
        #'ROLE': str(request.authenticated_role),
        'CURRENT_URL': request.url,
        'CURRENT_PATH': request.path_info,
        'REMOTE_ADDR': request.remote_addr or '',
        'USER_AGENT': request.user_agent or '',
        'REQUEST_METHOD': request.method,
        'AWARD_ID': '',
        'BID_ID': '',
        'COMPLAINT_ID': '',
        'CONTRACT_ID': '',
        'DOCUMENT_ID': '',
        'QUESTION_ID': '',
        'TENDER_ID': '',
        'TIMESTAMP': get_now().isoformat(),
        'REQUEST_ID': request.environ.get('REQUEST_ID', ''),
        'CLIENT_REQUEST_ID': request.headers.get('X-Client-Request-ID', ''),
    }
    for i in LOGGER.handlers:
        LOGGER.removeHandler(i)
    LOGGER.addHandler(JournalHandler(**params))
Пример #18
0
    def test_listing_changes(self):

        tender = self.create_tender()

        data = self.db[tender['id']]

        awards = data['awards']

        for i in range(3):
            award = deepcopy(test_award)
            award['date'] = get_now().isoformat()
            award['id'] = uuid4().hex
            awards.append(award)

        self.db.save(data)

        ids = ','.join([i['id'] for i in awards])

        response = self.app.get('/tenders/{}/awards'.format(tender['id']))
        self.assertTrue(ids.startswith(','.join([i['id'] for i in response.json['data']])))


        self.assertEqual(response.status, '200 OK')
        self.assertEqual(len(response.json['data']), len(awards))
        self.assertEqual(set([i['id'] for i in response.json['data']]), set([i['id'] for i in awards]))
        self.assertEqual(set([i['date'] for i in response.json['data']]), set([i['date'] for i in awards]))
        self.assertEqual([i['date'] for i in response.json['data']], sorted([i['date'] for i in awards]))
    def patch(self):
        """Update of contract
        """
        if self.request.validated['tender_status'] not in ['active', '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
        data = self.request.validated['data']
        if self.request.context.status == 'cancelled':
            self.request.errors.add('body', 'data', 'Can\'t update contract in current ({}) status'.format(self.request.context.status))
            self.request.errors.status = 403
            return
        if self.request.context.status != 'active' and 'status' in data and data['status'] == 'active':
            tender = self.request.validated['tender']
            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

        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' and 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()}
Пример #20
0
    def test_listing_changes(self):

        tender = self.create_tender()

        data = self.db[tender['id']]

        award = data['awards'][0]
        award_complaint = award['complaints'][0]
        award_complaint_documents = award_complaint['documents']

        for i in range(3):
            document = deepcopy(test_document)
            document['dateModified'] = get_now().isoformat()
            document['id'] = uuid4().hex
            award_complaint_documents.append(document)

        self.db.save(data)

        ids = ','.join([i['id'] for i in award_complaint_documents])

        response = self.app.get('/tenders/{}/awards/{}/complaints/{}/documents'.format(tender['id'], award['id'], award_complaint['id']))
        self.assertTrue(ids.startswith(','.join([i['id'] for i in response.json['data']])))


        self.assertEqual(response.status, '200 OK')
        self.assertEqual(len(response.json['data']), len(award_complaint_documents))
        self.assertEqual(set([i['id'] for i in response.json['data']]), set([i['id'] for i in award_complaint_documents]))
        self.assertEqual(set([i['dateModified'] for i in response.json['data']]), set([i['dateModified'] for i in award_complaint_documents]))
        self.assertEqual([i['dateModified'] for i in response.json['data']], sorted([i['dateModified'] for i in award_complaint_documents]))
Пример #21
0
 def collection_post(self):
     """Post a complaint
     """
     tender = self.context
     if tender.status not in ['active.enquiries', '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':
         complaint.dateSubmitted = get_now()
     else:
         complaint.status = 'draft'
     complaint.complaintID = '{}.{}{}'.format(tender.tenderID, self.server_id, sum([len(i.complaints) for i in tender.awards], len(tender.complaints)) + 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
             }
         }
Пример #22
0
def from18to19(db):

    def update_documents_type(item):
        changed = False
        for document in item.get('documents', []):
            if document.get('documentType') == 'contractAnnexes':
                document['documentType'] = 'contractAnnexe'
                changed = True
        return changed

    changed = False
    results = db.view('tenders/all', include_docs=True)
    for i in results:
        doc = i.doc
        changed = update_documents_type(doc) or changed
        for item in ('bids', 'complaints', 'cancellations', 'contracts',
                     'awards'):
            for item_val in doc.get(item, []):
                changed = update_documents_type(item_val) or changed
                if item == 'awards':
                    for complaint in item_val.get('complaints', []):
                        changed = update_documents_type(complaint) or changed
    if changed:
        doc['dateModified'] = get_now().isoformat()
        db.save(doc)
    def test_switch_to_complaint(self):
        for status in ['invalid', 'resolved', 'declined']:
            self.app.authorization = ('Basic', ('token', ''))
            response = self.app.post_json('/auctions/{}/complaints'.format(self.auction_id), {'data': {
                'title': 'complaint title',
                'description': 'complaint description',
                'author': self.initial_organization,
                'status': 'claim'
            }})
            self.assertEqual(response.status, '201 Created')
            self.assertEqual(response.json['data']['status'], 'claim')
            complaint = response.json['data']

            response = self.app.patch_json('/auctions/{}/complaints/{}?acc_token={}'.format(self.auction_id, complaint['id'], self.auction_token), {"data": {
                "status": "answered",
                "resolution": status * 4,
                "resolutionType": status
            }})
            self.assertEqual(response.status, '200 OK')
            self.assertEqual(response.content_type, 'application/json')
            self.assertEqual(response.json['data']["status"], "answered")
            self.assertEqual(response.json['data']["resolutionType"], status)

            auction = self.db.get(self.auction_id)
            auction['complaints'][-1]['dateAnswered'] = (get_now() - timedelta(days=1 if 'procurementMethodDetails' in auction else 4)).isoformat()
            self.db.save(auction)

            self.app.authorization = ('Basic', ('chronograph', ''))
            response = self.app.patch_json('/auctions/{}'.format(self.auction_id), {'data': {'id': self.auction_id}})
            self.assertEqual(response.status, '200 OK')
            self.assertEqual(response.json['data']["complaints"][-1]['status'], status)
Пример #24
0
    def test_listing_changes(self):

        contract = self.create_contract()

        data = self.db[contract['id']]

        documents = data['documents']

        for i in range(3):
            document = deepcopy(test_document)
            document['dateModified'] = get_now().isoformat()
            document['id'] = uuid4().hex
            documents.append(document)

        self.db.save(data)

        ids = ','.join([i['id'] for i in documents])

        response = self.app.get('/contracts/{}/documents'.format(contract['id']))
        self.assertTrue(ids.startswith(','.join([i['id'] for i in response.json['data']])))


        self.assertEqual(response.status, '200 OK')
        self.assertEqual(len(response.json['data']), len(documents))
        self.assertEqual(set([i['id'] for i in response.json['data']]), set([i['id'] for i in documents]))
        self.assertEqual(set([i['dateModified'] for i in response.json['data']]), set([i['dateModified'] for i in documents]))
        self.assertEqual([i['dateModified'] for i in response.json['data']], sorted([i['dateModified'] for i in documents]))
Пример #25
0
 def next_check(self):
     now = get_now()
     checks = []
     if self.status == 'active.tendering' and self.tenderPeriod.endDate and not any([i.status in ['pending', 'accepted'] for i in self.complaints]):
         checks.append(self.tenderPeriod.endDate.astimezone(TZ))
     elif not self.lots and self.status == 'active.awarded':
         standStillEnds = [
             a.complaintPeriod.endDate.astimezone(TZ)
             for a in self.awards
             if a.complaintPeriod.endDate
         ]
         if standStillEnds:
             standStillEnd = max(standStillEnds)
             if standStillEnd > now:
                 checks.append(standStillEnd)
     elif self.lots and self.status in ['active.qualification', 'active.awarded']:
         lots_ends = []
         for lot in self.lots:
             if lot['status'] != 'active':
                 continue
             lot_awards = [i for i in self.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:
                 lots_ends.append(standStillEnd)
         if lots_ends:
             checks.append(min(lots_ends))
     return sorted(checks)[0].isoformat() if checks else None
    def test_switch_to_pending(self):
        response = self.app.post_json(
            "/auctions/{}/awards/{}/complaints".format(self.auction_id, self.award_id),
            {
                "data": {
                    "title": "complaint title",
                    "description": "complaint description",
                    "author": test_organization,
                    "status": "claim",
                }
            },
        )
        self.assertEqual(response.status, "201 Created")
        self.assertEqual(response.json["data"]["status"], "claim")

        response = self.app.patch_json(
            "/auctions/{}/awards/{}".format(self.auction_id, self.award_id), {"data": {"status": "active"}}
        )
        self.assertEqual(response.status, "200 OK")
        self.assertEqual(response.content_type, "application/json")
        self.assertEqual(response.json["data"]["status"], "active")

        auction = self.db.get(self.auction_id)
        auction["awards"][0]["complaints"][0]["dateSubmitted"] = (
            get_now() - timedelta(days=1 if "procurementMethodDetails" in auction else 4)
        ).isoformat()
        self.db.save(auction)

        self.app.authorization = ("Basic", ("chronograph", ""))
        response = self.app.patch_json("/auctions/{}".format(self.auction_id), {"data": {"id": self.auction_id}})
        self.assertEqual(response.status, "200 OK")
        self.assertEqual(response.json["data"]["awards"][0]["complaints"][0]["status"], "pending")
Пример #27
0
def add_logging_context(event):
    request = event.request
    params = {
        'TENDERS_API_VERSION': VERSION,
        'TAGS': 'python,api',
        'USER': str(request.authenticated_userid or ''),
        #'ROLE': str(request.authenticated_role),
        'CURRENT_URL': request.url,
        'CURRENT_PATH': request.path_info,
        'REMOTE_ADDR': request.remote_addr or '',
        'USER_AGENT': request.user_agent or '',
        'REQUEST_METHOD': request.method,
        'AWARD_ID': '',
        'BID_ID': '',
        'COMPLAINT_ID': '',
        'CONTRACT_ID': '',
        'DOCUMENT_ID': '',
        'QUESTION_ID': '',
        'TENDER_ID': '',
        'TIMESTAMP': get_now().isoformat(),
        'REQUEST_ID': request.environ.get('REQUEST_ID', ''),
        'CLIENT_REQUEST_ID': request.headers.get('X-Client-Request-ID', ''),
    }

    request.logging_context = params
    def delete(self):
        """Cancelling the proposal

        Example request for cancelling the proposal:

        .. sourcecode:: http

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

        """
        bid = self.request.context
        if self.request.validated['tender_status'] != 'active.tendering':
            self.request.errors.add('body', 'data', 'Can\'t delete bid in current ({}) tender status'.format(self.request.validated['tender_status']))
            self.request.errors.status = 403
            return
        tender = self.request.validated['tender']
        if tender.tenderPeriod.startDate and get_now() < tender.tenderPeriod.startDate or get_now() > tender.tenderPeriod.endDate:
            self.request.errors.add('body', 'data', 'Bid can be deleted only during the tendering period: from ({}) to ({}).'.format(tender.tenderPeriod.startDate and tender.tenderPeriod.startDate.isoformat(), tender.tenderPeriod.endDate.isoformat()))
            self.request.errors.status = 403
            return
        bid.status = 'deleted'
        if tender.lots:
            bid.lotValues = []
        self.request.validated['tender'].modified = False
        if save_tender(self.request):
            res = bid.serialize("view")
            self.LOGGER.info('Deleted tender bid {}'.format(self.request.context.id),
                        extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_bid_delete'}))
            return {'data': res}
 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 = self.request.validated["cancellation"]
     cancellation.date = get_now()
     if cancellation.status == "active":
         tender.status = "cancelled"
     tender.cancellations.append(cancellation)
     if save_tender(self.request):
         self.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")}
Пример #30
0
def validate_tender_auction_data(request):
    data = validate_patch_tender_data(request)
    tender = request.context
    if data is not None:
        if tender.status != 'active.auction':
            request.errors.add('body', 'data', 'Can\'t report auction results in current ({}) tender status'.format(tender.status))
            request.errors.status = 403
            return
        bids = data.get('bids', [])
        #if not bids:
            #request.errors.add('body', 'data', "Bids data not available")
            #request.errors.status = 422
            #return
        tender_bids_ids = [i.id for i in tender.bids]
        if len(bids) != len(tender.bids):
            request.errors.add('body', 'bids', "Number of auction results did not match the number of tender bids")
            request.errors.status = 422
            return
        #elif not all(['id' in i for i in bids]):
            #request.errors.add('body', 'bids', "Results of auction bids should contains id of bid")
            #request.errors.status = 422
            #return
        elif set([i['id'] for i in bids]) != set(tender_bids_ids):
            request.errors.add('body', 'bids', "Auction bids should be identical to the tender bids")
            request.errors.status = 422
            return
        data['bids'] = [x for (y, x) in sorted(zip([tender_bids_ids.index(i['id']) for i in bids], bids))]
    else:
        data = {}
    if request.method == 'POST':
        now = get_now().isoformat()
        data['auctionPeriod'] = {'endDate': now}
        data['awardPeriod'] = {'startDate': now}
        data['status'] = 'active.qualification'
    request.validated['data'] = data
Пример #31
0
def check_tender_status(request):
    tender = request.validated['tender']
    now = get_now()
    if tender.lots:
        if any([
                i.status in ['claim', 'answered', 'pending']
                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]
            if not lot_awards:
                continue
            last_award = lot_awards[-1]
            pending_complaints = any([
                i['status'] in ['claim', 'answered', 'pending']
                and i.relatedLot == lot.id for i in tender.complaints
            ])
            pending_awards_complaints = any([
                i.status in ['claim', 'answered', 'pending']
                for a in lot_awards for i in a.complaints
            ])
            stand_still_end = max(
                [a.complaintPeriod.endDate or now for a in lot_awards])
            if pending_complaints or pending_awards_complaints or not stand_still_end <= now:
                continue
            elif last_award.status == 'unsuccessful':
                lot.status = 'unsuccessful'
                continue
            elif last_award.status == 'active' and any([
                    i.status == 'active' and i.awardID == last_award.id
                    for i in tender.contracts
            ]):
                lot.status = 'complete'
        statuses = set([lot.status for lot in tender.lots])
        if statuses == set(['cancelled']):
            tender.status = 'cancelled'
        elif not statuses.difference(set(['unsuccessful', 'cancelled'])):
            tender.status = 'unsuccessful'
        elif not statuses.difference(
                set(['complete', 'unsuccessful', 'cancelled'])):
            tender.status = 'complete'
    else:
        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
        ])
        stand_still_ends = [
            a.complaintPeriod.endDate for a in tender.awards
            if a.complaintPeriod.endDate
        ]
        stand_still_end = max(stand_still_ends) if stand_still_ends else now
        stand_still_time_expired = stand_still_end < now
        active_awards = any(
            [a.status in ['active', 'pending'] for a in tender.awards])
        if not active_awards and not pending_complaints and not pending_awards_complaints and stand_still_time_expired:
            tender.status = 'unsuccessful'
        if tender.contracts and tender.contracts[-1].status == 'active':
            tender.status = 'complete'
    :param request:
    :return: True if OK
    """
    transfer = request.validated['transfer']
    transfer.date = get_now()
    try:
        transfer.store(request.registry.db)
    except ModelValidationError, e:  # pragma: no cover
        for i in e.message:
            request.errors.add('body', i, e.message[i])
        request.errors.status = 422
    except Exception, e:  # pragma: no cover
        request.errors.add('body', 'data', str(e))
    else:
        LOGGER.info('Saved transfer {}: at {}'.format(transfer.id,
                                                      get_now().isoformat()),
                    extra=context_unpack(request,
                                         {'MESSAGE_ID': 'save_transfer'}))
        return True


def set_ownership(item, request, access_token=None, transfer_token=None):
    """ Set ownership for item
    :param item:
    :param request:
    :param access_token:
    :param transfer_token:
    :return: None
    """
    item.owner = request.authenticated_userid
    item.access_token = sha512(access_token).hexdigest()
def check_auction_status(request):
    auction = request.validated['auction']
    now = get_now()
    if auction.lots:
        if any([
                i.status in auction.block_complaint_status
                and i.relatedLot is None for i in auction.complaints
        ]):
            return
        for lot in auction.lots:
            if lot.status != 'active':
                continue
            lot_awards = [i for i in auction.awards if i.lotID == lot.id]
            if not lot_awards:
                continue
            last_award = lot_awards[-1]
            pending_complaints = any([
                i['status'] in auction.block_complaint_status
                and i.relatedLot == lot.id for i in auction.complaints
            ])
            pending_awards_complaints = any([
                i.status in auction.block_complaint_status for a in lot_awards
                for i in a.complaints
            ])
            stand_still_end = max(
                [a.complaintPeriod.endDate or now for a in lot_awards])
            if pending_complaints or pending_awards_complaints or not stand_still_end <= now:
                continue
            elif last_award.status == 'unsuccessful':
                LOGGER.info('Switched lot {} of auction {} to {}'.format(
                    lot.id, auction.id, 'unsuccessful'),
                            extra=context_unpack(
                                request,
                                {'MESSAGE_ID': 'switched_lot_unsuccessful'},
                                {'LOT_ID': lot.id}))
                lot.status = 'unsuccessful'
                continue
            elif last_award.status == 'active' and any([
                    i.status == 'active' and i.awardID == last_award.id
                    for i in auction.contracts
            ]):
                LOGGER.info('Switched lot {} of auction {} to {}'.format(
                    lot.id, auction.id, 'complete'),
                            extra=context_unpack(
                                request,
                                {'MESSAGE_ID': 'switched_lot_complete'},
                                {'LOT_ID': lot.id}))
                lot.status = 'complete'
        statuses = set([lot.status for lot in auction.lots])
        if statuses == set(['cancelled']):
            LOGGER.info('Switched lot {} of auction {} to {}'.format(
                lot.id, auction.id, 'cancelled'),
                        extra=context_unpack(
                            request, {'MESSAGE_ID': 'switched_lot_cancelled'},
                            {'LOT_ID': lot.id}))
            auction.status = 'cancelled'
        elif not statuses.difference(set(['unsuccessful', 'cancelled'])):
            LOGGER.info('Switched lot {} of auction {} to {}'.format(
                lot.id, auction.id, 'unsuccessful'),
                        extra=context_unpack(
                            request,
                            {'MESSAGE_ID': 'switched_lot_unsuccessful'},
                            {'LOT_ID': lot.id}))
            auction.status = 'unsuccessful'
        elif not statuses.difference(
                set(['complete', 'unsuccessful', 'cancelled'])):
            LOGGER.info('Switched lot {} of auction {} to {}'.format(
                lot.id, auction.id, 'complete'),
                        extra=context_unpack(
                            request, {'MESSAGE_ID': 'switched_lot_complete'},
                            {'LOT_ID': lot.id}))
            auction.status = 'complete'
    else:
        pending_complaints = any([
            i.status in auction.block_complaint_status
            for i in auction.complaints
        ])
        pending_awards_complaints = any([
            i.status in auction.block_complaint_status for a in auction.awards
            for i in a.complaints
        ])
        stand_still_ends = [
            a.complaintPeriod.endDate for a in auction.awards
            if a.complaintPeriod.endDate
        ]
        stand_still_end = max(stand_still_ends) if stand_still_ends else now
        stand_still_time_expired = stand_still_end < now
        last_award_status = auction.awards[-1].status if auction.awards else ''
        if not pending_complaints and not pending_awards_complaints and stand_still_time_expired \
                and last_award_status == 'unsuccessful':
            LOGGER.info(
                'Switched auction {} to {}'.format(auction.id, 'unsuccessful'),
                extra=context_unpack(
                    request, {'MESSAGE_ID': 'switched_auction_unsuccessful'}))
            auction.status = 'unsuccessful'
        if auction.contracts and auction.contracts[-1].status == 'active':
            LOGGER.info(
                'Switched auction {} to {}'.format(auction.id, 'unsuccessful'),
                extra=context_unpack(
                    request, {'MESSAGE_ID': 'switched_auction_complete'}))
            auction.status = 'complete'
Пример #34
0
def patch_tender_award_complaint_document(self):
    response = self.app.post(
        "/tenders/{}/awards/{}/complaints/{}/documents?acc_token={}".format(
            self.tender_id, self.award_id, self.complaint_id,
            self.complaint_owner_token),
        upload_files=[("file", "name.doc", "content")],
    )
    self.assertEqual(response.status, "201 Created")
    self.assertEqual(response.content_type, "application/json")
    doc_id = response.json["data"]["id"]
    self.assertIn(doc_id, response.headers["Location"])

    response = self.app.patch_json(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}?acc_token={}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id,
            self.tender_token),
        {"data": {
            "description": "document description"
        }},
        status=403,
    )
    self.assertEqual(response.status, "403 Forbidden")
    self.assertEqual(response.content_type, "application/json")
    self.assertEqual(response.json["errors"][0]["description"],
                     "Can update document only author")

    response = self.app.patch_json(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}?acc_token={}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id,
            self.complaint_owner_token),
        {"data": {
            "description": "document description"
        }},
    )
    self.assertEqual(response.status, "200 OK")
    self.assertEqual(response.content_type, "application/json")
    self.assertEqual(doc_id, response.json["data"]["id"])

    response = self.app.get(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id))
    self.assertEqual(response.status, "200 OK")
    self.assertEqual(response.content_type, "application/json")
    self.assertEqual(doc_id, response.json["data"]["id"])
    self.assertEqual("document description",
                     response.json["data"]["description"])

    if get_now() < RELEASE_2020_04_19:
        response = self.app.patch_json(
            "/tenders/{}/awards/{}/complaints/{}?acc_token={}".format(
                self.tender_id, self.award_id, self.complaint_id,
                self.complaint_owner_token),
            {"data": {
                "status": "pending"
            }},
        )
    else:
        with change_auth(self.app, ("Basic", ("bot", ""))):
            response = self.app.patch_json(
                "/tenders/{}/awards/{}/complaints/{}".format(
                    self.tender_id, self.award_id, self.complaint_id),
                {"data": {
                    "status": "pending"
                }},
            )
    self.assertEqual(response.status, "200 OK")
    self.assertEqual(response.json["data"]["status"], "pending")

    response = self.app.put(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}?acc_token={}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id,
            self.complaint_owner_token),
        "content2",
        content_type="application/msword",
    )
    self.assertEqual(response.status, "200 OK")
    self.assertEqual(response.content_type, "application/json")
    key = response.json["data"]["url"].split("?")[-1]

    response = self.app.get(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}?{}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id, key))
    self.assertEqual(response.status, "200 OK")
    self.assertEqual(response.content_type, "application/msword")
    self.assertEqual(response.content_length, 8)
    self.assertEqual(response.body, "content2")

    self.set_status("complete")

    response = self.app.patch_json(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}?acc_token={}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id,
            self.complaint_owner_token),
        {"data": {
            "description": "document description"
        }},
        status=403,
    )
    self.assertEqual(response.status, "403 Forbidden")
    self.assertEqual(response.content_type, "application/json")
    self.assertEqual(
        response.json["errors"][0]["description"],
        "Can't update document in current (complete) tender status")
Пример #35
0
def put_tender_award_complaint_document(self):
    response = self.app.post(
        "/tenders/{}/awards/{}/complaints/{}/documents?acc_token={}".format(
            self.tender_id, self.award_id, self.complaint_id,
            self.complaint_owner_token),
        upload_files=[("file", "name.doc", "content")],
    )
    self.assertEqual(response.status, "201 Created")
    self.assertEqual(response.content_type, "application/json")
    doc_id = response.json["data"]["id"]
    self.assertIn(doc_id, response.headers["Location"])

    response = self.app.put(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id),
        status=404,
        upload_files=[("invalid_name", "name.doc", "content")],
    )
    self.assertEqual(response.status, "404 Not Found")
    self.assertEqual(response.content_type, "application/json")
    self.assertEqual(response.json["status"], "error")
    self.assertEqual(response.json["errors"], [{
        u"description": u"Not Found",
        u"location": u"body",
        u"name": u"file"
    }])

    response = self.app.put(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id),
        upload_files=[("file", "name.doc", "content2")],
        status=403,
    )
    self.assertEqual(response.status, "403 Forbidden")
    self.assertEqual(response.content_type, "application/json")
    self.assertEqual(response.json["errors"][0]["description"],
                     "Can update document only author")

    response = self.app.put(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}?acc_token={}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id,
            self.complaint_owner_token),
        upload_files=[("file", "name.doc", "content2")],
    )
    self.assertEqual(response.status, "200 OK")
    self.assertEqual(response.content_type, "application/json")
    self.assertEqual(doc_id, response.json["data"]["id"])
    key = response.json["data"]["url"].split("?")[-1]

    response = self.app.get(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}?{}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id, key))
    self.assertEqual(response.status, "200 OK")
    self.assertEqual(response.content_type, "application/msword")
    self.assertEqual(response.content_length, 8)
    self.assertEqual(response.body, "content2")

    response = self.app.get(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id))
    self.assertEqual(response.status, "200 OK")
    self.assertEqual(response.content_type, "application/json")
    self.assertEqual(doc_id, response.json["data"]["id"])
    self.assertEqual("name.doc", response.json["data"]["title"])

    response = self.app.put(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}?acc_token={}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id,
            self.complaint_owner_token),
        "content3",
        content_type="application/msword",
    )
    self.assertEqual(response.status, "200 OK")
    self.assertEqual(response.content_type, "application/json")
    self.assertEqual(doc_id, response.json["data"]["id"])
    key = response.json["data"]["url"].split("?")[-1]

    response = self.app.get(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}?{}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id, key))
    self.assertEqual(response.status, "200 OK")
    self.assertEqual(response.content_type, "application/msword")
    self.assertEqual(response.content_length, 8)
    self.assertEqual(response.body, "content3")

    response = self.app.put(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}?acc_token={}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id,
            self.complaint_owner_token),
        "content4",
        content_type="application/msword",
    )
    self.assertEqual(response.status, "200 OK")
    self.assertEqual(response.content_type, "application/json")
    key = response.json["data"]["url"].split("?")[-1]

    response = self.app.get(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}?{}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id, key))
    self.assertEqual(response.status, "200 OK")
    self.assertEqual(response.content_type, "application/msword")
    self.assertEqual(response.content_length, 8)
    self.assertEqual(response.body, "content4")

    if RELEASE_2020_04_19 < get_now():
        self.set_all_awards_complaint_period_end()

    cancellation = dict(**test_cancellation)
    cancellation.update({
        "status": "active",
        "cancellationOf": "lot",
        "relatedLot": self.lots[0]["id"],
    })
    response = self.app.post_json(
        "/tenders/{}/cancellations".format(self.tender_id),
        {"data": cancellation},
    )
    self.assertEqual(response.status, "201 Created")
    cancellation_id = response.json["data"]["id"]
    if RELEASE_2020_04_19 < get_now():
        activate_cancellation_after_2020_04_19(self, cancellation_id)

    response = self.app.put(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}?acc_token={}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id,
            self.complaint_owner_token),
        upload_files=[("file", "name.doc", "content3")],
        status=403,
    )
    self.assertEqual(response.status, "403 Forbidden")
    self.assertEqual(response.content_type, "application/json")
    self.assertEqual(response.json["errors"][0]["description"],
                     "Can update document only in active lot status")
 def invalidate_bids_data(self):
     self.check_auction_time()
     self.enquiryPeriod.invalidationDate = get_now()
     for bid in self.bids:
         if bid.status not in ["deleted", "draft"]:
             bid.status = "invalid"
Пример #37
0
 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
     apply_patch(self.request,
                 save=False,
                 src=self.request.context.serialize())
     if self.request.context.status == 'cancelled':
         self.request.errors.add('body', 'data', 'Can\'t cancel complaint')
         self.request.errors.status = 403
         return
     if self.request.context.status == 'resolved' and tender.status != 'active.enquiries':
         for i in tender.complaints:
             if i.status == 'pending':
                 i.status = 'cancelled'
         tender.status = 'cancelled'
     elif self.request.context.status in [
             'declined', 'invalid'
     ] and tender.status == 'active.awarded':
         pending_complaints = [
             i for i in tender.complaints if i.status == 'pending'
         ]
         pending_awards_complaints = [
             i for a in tender.awards for i in a.complaints
             if i.status == 'pending'
         ]
         stand_still_ends = [
             a.complaintPeriod.endDate for a in tender.awards
             if a.complaintPeriod.endDate
         ]
         stand_still_end = max(
             stand_still_ends) if stand_still_ends else get_now()
         stand_still_time_expired = stand_still_end < get_now()
         if not pending_complaints and not pending_awards_complaints and stand_still_time_expired:
             active_awards = [
                 a for a in tender.awards if a.status == 'active'
             ]
             if not active_awards:
                 tender.status = 'unsuccessful'
     if save_tender(self.request):
         LOGGER.info('Updated tender complaint {}'.format(
             self.request.context.id),
                     extra={'MESSAGE_ID': 'tender_complaint_patch'})
         return {'data': self.request.context.serialize("view")}
Пример #38
0
    def test_complaint_value(self):
        for item in self.initial_data['items']:
            item['deliveryDate'] = {
                "startDate": (get_now() + timedelta(days=2)).isoformat(),
                "endDate": (get_now() + timedelta(days=5)).isoformat()
            }

        self.initial_data.update({
            "enquiryPeriod": {"endDate": (get_now() + timedelta(days=8)).isoformat()},
            "tenderPeriod": {"endDate": (get_now() + timedelta(days=16)).isoformat()}
        })

        response = self.app.post_json("/tenders", {"data": self.initial_data})

        with open(TARGET_VALUE_DIR + 'complaint-creation.http', 'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders/{}/complaints'.format(response.json["data"]["id"]),
                {'data': complaint})
            self.assertEqual(response.status, '201 Created')

        self.initial_data["value"]["currency"] = self.initial_data["minimalStep"]["currency"] = "USD"

        response = self.app.post_json("/tenders", {"data": self.initial_data})

        with open(TARGET_VALUE_DIR + 'complaint-creation-decoding.http', 'w') as self.app.file_obj:

            def mock_get_uah_amount_from_value(r, *_):
                raise raise_operation_error(r, "Failure of decoding data from bank.gov.ua", status=409)

            with patch("openprocurement.tender.core.models.get_uah_amount_from_value", mock_get_uah_amount_from_value):

                self.app.post_json(
                    '/tenders/{}/complaints'.format(response.json["data"]["id"]),
                    {'data': complaint},
                    status=409
                )

        with open(TARGET_VALUE_DIR + 'complaint-creation-connection.http', 'w') as self.app.file_obj:

            def mock_get_uah_amount_from_value(r, *_):
                raise raise_operation_error(r, "Error while getting data from bank.gov.ua: Connection closed",
                                            status=409)
            with patch("openprocurement.tender.core.models.get_uah_amount_from_value", mock_get_uah_amount_from_value):

                self.app.post_json(
                    '/tenders/{}/complaints'.format(response.json["data"]["id"]),
                    {'data': complaint},
                    status=409
                )

        with open(TARGET_VALUE_DIR + 'complaint-creation-rur.http', 'w') as self.app.file_obj:

            def mock_get_uah_amount_from_value(r, *_):
                raise raise_operation_error(r, "Couldn't find currency RUR on bank.gov.ua", status=422)

            with patch("openprocurement.tender.core.models.get_uah_amount_from_value", mock_get_uah_amount_from_value):

                self.app.post_json(
                    '/tenders/{}/complaints'.format(response.json["data"]["id"]),
                    {'data': complaint},
                    status=422
                )
Пример #39
0
    def test_docs(self):
        request_path = '/tenders?opt_pretty=1'

        #### Exploring basic rules

        with open(TARGET_DIR + 'tender-listing.http', 'w') as self.app.file_obj:
            self.app.authorization = None
            response = self.app.get(request_path)
            self.assertEqual(response.status, '200 OK')
            self.app.file_obj.write("\n")

        with open(TARGET_DIR + 'tender-post-attempt.http', 'w') as self.app.file_obj:
            response = self.app.post(request_path, 'data', status=415)
            self.assertEqual(response.status, '415 Unsupported Media Type')

        self.app.authorization = ('Basic', ('broker', ''))

        with open(TARGET_DIR + 'tender-post-attempt-json.http', 'w') as self.app.file_obj:
            self.app.authorization = ('Basic', ('broker', ''))
            response = self.app.post(
                request_path, 'data', content_type='application/json', status=422)
            self.assertEqual(response.status, '422 Unprocessable Entity')

        #### Creating tender

        with open(TARGET_DIR + 'tender-post-attempt-json-data.http', 'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders?opt_pretty=1',
                {'data': test_tender_ua_data})
            self.assertEqual(response.status, '201 Created')

        tender = response.json['data']
        owner_token = response.json['access']['token']

        with open(TARGET_DIR + 'blank-tender-view.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}'.format(tender['id']))
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'tender-listing-no-auth.http', 'w') as self.app.file_obj:
            self.app.authorization = None
            response = self.app.get(request_path)
            self.assertEqual(response.status, '200 OK')

        self.app.authorization = ('Basic', ('broker', ''))

        #### Modifying tender

        tender_period_end_date = get_now() + timedelta(days=16)
        with open(TARGET_DIR + 'patch-items-value-periods.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}?acc_token={}'.format(tender['id'], owner_token),
                {'data': {"tenderPeriod": {"endDate": tender_period_end_date.isoformat()}}})

        with open(TARGET_DIR + 'tender-listing-after-patch.http', 'w') as self.app.file_obj:
            self.app.authorization = None
            response = self.app.get(request_path)
            self.assertEqual(response.status, '200 OK')

        self.app.authorization = ('Basic', ('broker', ''))
        self.tender_id = tender['id']

        # Setting Bid guarantee

        with open(TARGET_DIR + 'set-bid-guarantee.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}?acc_token={}'.format(
                    self.tender_id, owner_token),
                {'data': {"guarantee": {"amount": 8, "currency": "USD"}}})
            self.assertEqual(response.status, '200 OK')
            self.assertIn('guarantee', response.json['data'])

        #### Uploading documentation

        with open(TARGET_DIR + 'upload-tender-notice.http', 'w') as self.app.file_obj:
            response = self.app.post(
                '/tenders/{}/documents?acc_token={}'.format(
                    self.tender_id, owner_token),
                upload_files=[('file', 'Notice.pdf', b'content')])
            self.assertEqual(response.status, '201 Created')

        doc_id = response.json["data"]["id"]
        with open(TARGET_DIR + 'tender-documents.http', 'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/documents/{}?acc_token={}'.format(
                    self.tender_id, doc_id, owner_token))
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'upload-award-criteria.http', 'w') as self.app.file_obj:
            response = self.app.post(
                '/tenders/{}/documents?acc_token={}'.format(
                    self.tender_id, owner_token),
                upload_files=[('file', 'AwardCriteria.pdf', b'content')])
            self.assertEqual(response.status, '201 Created')

        doc_id = response.json["data"]["id"]

        with open(TARGET_DIR + 'tender-documents-2.http', 'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/documents?acc_token={}'.format(
                    self.tender_id, owner_token))
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'update-award-criteria.http', 'w') as self.app.file_obj:
            response = self.app.put(
                '/tenders/{}/documents/{}?acc_token={}'.format(
                    self.tender_id, doc_id, owner_token),
                upload_files=[('file', 'AwardCriteria-2.pdf', b'content2')])
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'tender-documents-3.http', 'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/documents'.format(self.tender_id))
            self.assertEqual(response.status, '200 OK')

        #### Enquiries

        with open(TARGET_DIR + 'ask-question.http', 'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders/{}/questions'.format(self.tender_id),
                {'data': question}, status=201)
            question_id = response.json['data']['id']
            self.assertEqual(response.status, '201 Created')

        with open(TARGET_DIR + 'answer-question.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/questions/{}?acc_token={}'.format(
                    self.tender_id, question_id, owner_token),
                {"data": {"answer": "Таблицю додано в файлі \"Kalorijnist.xslx\""}}, status=200)
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'list-question.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/questions'.format(
                self.tender_id))
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'get-answer.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/questions/{}'.format(
                self.tender_id, question_id))
            self.assertEqual(response.status, '200 OK')

        self.set_enquiry_period_end()
        self.app.authorization = ('Basic', ('broker', ''))
        with open(TARGET_DIR + 'update-tender-after-enqiery.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}?acc_token={}'.format(tender['id'], owner_token),
                {'data': {"value": {'amount': 501.0}}}, status=403)
            self.assertEqual(response.status, '403 Forbidden')

        with open(TARGET_DIR + 'ask-question-after-enquiry-period.http', 'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders/{}/questions'.format(self.tender_id),
                {'data': question}, status=403)
            self.assertEqual(response.status, '403 Forbidden')

        with open(TARGET_DIR + 'update-tender-after-enqiery-with-update-periods.http', 'w') as self.app.file_obj:
            tender_period_end_date = get_now() + timedelta(days=8)
            response = self.app.patch_json(
                '/tenders/{}?acc_token={}'.format(tender['id'], owner_token),
                {'data': {
                    "value": {
                        "amount": 501,
                        "currency": "UAH"
                    },
                    "tenderPeriod": {
                        "endDate": tender_period_end_date.isoformat()
                    }
                }})
            self.assertEqual(response.status, '200 OK')

        #### Registering bid

        bids_access = {}
        with open(TARGET_DIR + 'register-bidder.http', 'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders/{}/bids'.format(self.tender_id),
                {'data': bid})
            bid1_id = response.json['data']['id']
            bids_access[bid1_id] = response.json['access']['token']
            self.assertEqual(response.status, '201 Created')

        with open(TARGET_DIR + 'activate-bidder.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/bids/{}?acc_token={}'.format(
                    self.tender_id, bid1_id, bids_access[bid1_id]),
                {'data': {"status": "active"}})
            self.assertEqual(response.status, '200 OK')

        #### Proposal Uploading

        with open(TARGET_DIR + 'upload-bid-proposal.http', 'w') as self.app.file_obj:
            response = self.app.post(
                '/tenders/{}/bids/{}/documents?acc_token={}'.format(
                    self.tender_id, bid1_id, bids_access[bid1_id]),
                upload_files=[('file', 'Proposal.pdf', b'content')])
            self.assertEqual(response.status, '201 Created')

        with open(TARGET_DIR + 'bidder-documents.http', 'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/bids/{}/documents?acc_token={}'.format(
                    self.tender_id, bid1_id, bids_access[bid1_id]))
            self.assertEqual(response.status, '200 OK')

        response = self.app.patch_json(
            '/tenders/{}?acc_token={}'.format(tender['id'], owner_token),
            {'data': {"value": {'amount': 501.0}}})
        self.assertEqual(response.status, '200 OK')

        #### Bid invalidation

        with open(TARGET_DIR + 'bidder-after-changing-tender.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/bids/{}?acc_token={}'.format(
                self.tender_id, bid1_id, bids_access[bid1_id]))
            self.assertEqual(response.status, '200 OK')

        #### Bid confirmation

        with open(TARGET_DIR + 'bidder-activate-after-changing-tender.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/bids/{}?acc_token={}'.format(
                    self.tender_id, bid1_id, bids_access[bid1_id]),
                {'data': {"status": "active"}})
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'register-2nd-bidder.http', 'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders/{}/bids'.format(self.tender_id),
                {'data': bid2})
            bid2_id = response.json['data']['id']
            bids_access[bid2_id] = response.json['access']['token']
            self.assertEqual(response.status, '201 Created')

        #### Auction
        self.set_status('active.auction')
        self.app.authorization = ('Basic', ('auction', ''))
        auction_url = '{}/tenders/{}'.format(self.auctions_url, self.tender_id)
        patch_data = {
            'auctionUrl': auction_url,
            'bids': [{
                "id": bid1_id,
                "participationUrl": '{}?key_for_bid={}'.format(auction_url, bid1_id)
            }, {
                "id": bid2_id,
                "participationUrl": '{}?key_for_bid={}'.format(auction_url, bid2_id)
            }]
        }
        response = self.app.patch_json(
            '/tenders/{}/auction?acc_token={}'.format(self.tender_id, owner_token),
            {'data': patch_data})
        self.assertEqual(response.status, '200 OK')

        self.app.authorization = ('Basic', ('broker', ''))

        with open(TARGET_DIR + 'auction-url.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}'.format(self.tender_id))
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'bidder-participation-url.http', 'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/bids/{}?acc_token={}'.format(self.tender_id, bid1_id, bids_access[bid1_id]))
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'bidder2-participation-url.http', 'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/bids/{}?acc_token={}'.format(self.tender_id, bid2_id, bids_access[bid2_id]))
            self.assertEqual(response.status, '200 OK')

        #### Confirming qualification
        self.app.authorization = ('Basic', ('auction', ''))
        response = self.app.get('/tenders/{}/auction'.format(self.tender_id))
        auction_bids_data = response.json['data']['bids']
        auction_bids_data[0]["value"]["amount"] = 250  # too low price
        response = self.app.post_json(
            '/tenders/{}/auction'.format(self.tender_id),
            {'data': {'bids': auction_bids_data}})

        self.app.authorization = ('Basic', ('broker', ''))

        # get pending award
        with open(TARGET_DIR + 'get-awards-list.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/awards?acc_token={}'.format(self.tender_id, owner_token))

        award = response.json["data"][0]
        award_id = award["id"]
        award_bid_id = award["bid_id"]
        self.assertEqual(len(award.get("milestones", "")), 1)

        with open(TARGET_DIR + 'fail-disqualification.http', 'w') as self.app.file_obj:
            self.app.patch_json(
                '/tenders/{}/awards/{}?acc_token={}'.format(self.tender_id, award_id, owner_token),
                {"data": {
                    "status": "unsuccessful",
                }},
                status=403
            )

        with open(TARGET_DIR + 'post-evidence-document.http', 'w') as self.app.file_obj:
            self.app.post_json(
                "/tenders/{}/bids/{}/documents?acc_token={}".format(
                    self.tender_id, award_bid_id, bids_access[award_bid_id]),
                {"data": {
                    "title": "lorem.doc",
                    "url": self.generate_docservice_url(),
                    "hash": "md5:" + "0" * 32,
                    "format": "application/msword",
                    "documentType": "evidence"
                }},
                status=201
            )

        tender = self.db.get(self.tender_id)
        tender["awards"][0]["milestones"][0]["dueDate"] = (get_now() - timedelta(days=1)).isoformat()
        self.db.save(tender)

        with open(TARGET_DIR + 'confirm-qualification.http', 'w') as self.app.file_obj:
            self.app.patch_json(
                '/tenders/{}/awards/{}?acc_token={}'.format(self.tender_id, award_id, owner_token),
                {"data": {
                    "status": "active",
                    "qualified": True,
                    "eligible": True
                }},
                status=200
            )

        response = self.app.get('/tenders/{}/contracts?acc_token={}'.format(
            self.tender_id, owner_token))
        self.contract_id = response.json['data'][0]['id']

        ####  Set contract value

        tender = self.db.get(self.tender_id)
        for i in tender.get('awards', []):
            i['complaintPeriod']['endDate'] = i['complaintPeriod']['startDate']
        self.db.save(tender)

        with open(TARGET_DIR + 'tender-contract-set-contract-value.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/contracts/{}?acc_token={}'.format(
                    self.tender_id, self.contract_id, owner_token),
                {"data": {"value": {"amount": 238, "amountNet": 230}}})
        self.assertEqual(response.status, '200 OK')
        self.assertEqual(response.json['data']['value']['amount'], 238)

        #### Setting contract signature date

        self.tick()

        with open(TARGET_DIR + 'tender-contract-sign-date.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/contracts/{}?acc_token={}'.format(
                    self.tender_id, self.contract_id, owner_token),
                {'data': {"dateSigned": get_now().isoformat()}})
            self.assertEqual(response.status, '200 OK')

        #### Setting contract period

        period_dates = {"period": {
            "startDate": (get_now()).isoformat(),
            "endDate": (get_now() + timedelta(days=365)).isoformat()
        }}
        with open(TARGET_DIR + 'tender-contract-period.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/contracts/{}?acc_token={}'.format(
                    self.tender_id, self.contract_id, owner_token),
                {'data': {'period': period_dates["period"]}})
        self.assertEqual(response.status, '200 OK')

        #### Uploading contract documentation

        with open(TARGET_DIR + 'tender-contract-upload-document.http', 'w') as self.app.file_obj:
            response = self.app.post('/tenders/{}/contracts/{}/documents?acc_token={}'.format(
                self.tender_id, self.contract_id, owner_token),
                upload_files=[('file', 'contract_document.doc', b'content')])
            self.assertEqual(response.status, '201 Created')
            self.document_id = response.json['data']['id']

        with open(TARGET_DIR + 'tender-contract-get.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/contracts/{}?acc_token={}'.format(
                self.tender_id, self.contract_id, owner_token))
            self.assertEqual(response.status, '200 OK')

        #### Preparing the cancellation request

        with open(TARGET_DIR + 'prepare-cancellation.http', 'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders/{}/cancellations?acc_token={}'.format(
                    self.tender_id, owner_token),
                {'data': {'reason': 'cancellation reason', 'reasonType': 'unFixable'}})
            self.assertEqual(response.status, '201 Created')

        cancellation_id = response.json['data']['id']

        with open(TARGET_DIR + 'update-cancellation-reasonType.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/cancellations/{}?acc_token={}'.format(
                    self.tender_id, cancellation_id, owner_token),
                {'data': {'reasonType': 'forceMajeure'}})
            self.assertEqual(response.status, '200 OK')

        #### Filling cancellation with protocol and supplementary documentation

        with open(TARGET_DIR + 'upload-cancellation-doc.http', 'w') as self.app.file_obj:
            response = self.app.post(
                '/tenders/{}/cancellations/{}/documents?acc_token={}'.format(
                    self.tender_id, cancellation_id, owner_token),
                upload_files=[('file', 'Notice.pdf', b'content')])
            cancellation_doc_id = response.json['data']['id']
            self.assertEqual(response.status, '201 Created')

        with open(TARGET_DIR + 'patch-cancellation.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/cancellations/{}/documents/{}?acc_token={}'.format(
                    self.tender_id, cancellation_id, cancellation_doc_id, owner_token),
                {'data': {"description": 'Changed description'}})
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'update-cancellation-doc.http', 'w') as self.app.file_obj:
            response = self.app.put(
                '/tenders/{}/cancellations/{}/documents/{}?acc_token={}'.format(
                    self.tender_id, cancellation_id, cancellation_doc_id, owner_token),
                upload_files=[('file', 'Notice-2.pdf', b'content2')])
            self.assertEqual(response.status, '200 OK')

        #### Activating the request and cancelling tender
        with open(TARGET_DIR + 'pending-cancellation.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/cancellations/{}?acc_token={}'.format(
                    self.tender_id, cancellation_id, owner_token),
                {'data': {"status": "pending"}})
            self.assertEqual(response.status, '200 OK')

        self.tick(delta=timedelta(days=11))
        self.check_chronograph()

        with open(TARGET_DIR + 'active-cancellation.http', 'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/cancellations/{}?acc_token={}'.format(
                    self.tender_id, cancellation_id, owner_token))
            self.assertEqual(response.status, '200 OK')

        #### Creating tender

        with open(TARGET_DIR + 'tender-post-attempt-json-data.http', 'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders?opt_pretty=1',
                {'data': test_tender_ua_data})

            self.assertEqual(response.status, '201 Created')
    def patch(self):
        """Update of contract
        """
        if self.request.validated['auction_status'] not in [
                'active.qualification', 'active.awarded'
        ]:
            self.request.errors.add(
                'body', 'data',
                'Can\'t update contract in current ({}) auction status'.format(
                    self.request.validated['auction_status']))
            self.request.errors.status = 403
            return
        auction = self.request.validated['auction']
        if any([
                i.status != 'active' for i in auction.lots if i.id in [
                    a.lotID for a in auction.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 auction.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 greater 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 auction.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 auction.complaints
                if i.status in ['claim', 'answered', 'pending']
                and i.relatedLot in [None, award.lotID]
            ]
            pending_awards_complaints = [
                i for a in auction.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_auction_status(self.request)
        if save_auction(self.request):
            self.LOGGER.info(
                'Updated auction contract {}'.format(self.request.context.id),
                extra=context_unpack(self.request,
                                     {'MESSAGE_ID': 'auction_contract_patch'}))
            return {'data': self.request.context.serialize()}
    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 initialize(self):
     self.date = get_now()
     if self.lots:
         for lot in self.lots:
             lot.date = get_now()
 def initialize(self):
     self.date = get_now()
 def time_shift(self, status, extra=None):
     now = get_now()
     tender = self.db.get(self.tender_id)
     data = {}
     if status == 'enquiryPeriod_ends':
         data.update({
             "enquiryPeriod": {
                 "startDate": (now - timedelta(days=28)).isoformat(),
                 "endDate": (now - timedelta(days=1)).isoformat()
             },
             "tenderPeriod": {
                 "startDate": (now - timedelta(days=28)).isoformat(),
                 "endDate": (now + timedelta(days=2)).isoformat()
             },
         })
     if status == 'active.pre-qualification':
         data.update({
             "enquiryPeriod": {
                 "startDate": (now - TENDERING_DURATION).isoformat(),
                 "endDate": (now - QUESTIONS_STAND_STILL).isoformat()
             },
             "tenderPeriod": {
                 "startDate": (now - TENDERING_DURATION).isoformat(),
                 "endDate": (now).isoformat(),
             }
         })
     elif status == 'active.pre-qualification.stand-still':
         data.update({
             "enquiryPeriod": {
                 "startDate": (now - TENDERING_DURATION).isoformat(),
                 "endDate": (now - QUESTIONS_STAND_STILL).isoformat()
             },
             "tenderPeriod": {
                 "startDate": (now - TENDERING_DURATION).isoformat(),
                 "endDate": (now).isoformat(),
             },
             "qualificationPeriod": {
                 "startDate": (now).isoformat(),
             },
         })
         if 'lots' in tender and tender['lots']:
             data['lots'] = []
             for index, lot in enumerate(tender['lots']):
                 lot_data = {'id': lot['id']}
                 if lot['status'] is 'active':
                     lot_data["auctionPeriod"] = {
                         "startDate":
                         (now + COMPLAINT_STAND_STILL).isoformat()
                     }
                 data['lots'].append(lot_data)
         else:
             data.update({
                 "auctionPeriod": {
                     "startDate": (now + COMPLAINT_STAND_STILL).isoformat()
                 }
             })
     elif status == 'active.auction':
         data.update({
             "enquiryPeriod": {
                 "startDate": (now - TENDERING_DURATION -
                               COMPLAINT_STAND_STILL).isoformat(),
                 "endDate":
                 (now - COMPLAINT_STAND_STILL - TENDERING_DURATION +
                  QUESTIONS_STAND_STILL).isoformat()
             },
             "tenderPeriod": {
                 "startDate": (now - TENDERING_DURATION -
                               COMPLAINT_STAND_STILL).isoformat(),
                 "endDate": (now - COMPLAINT_STAND_STILL).isoformat()
             },
             "qualificationPeriod": {
                 "startDate": (now - COMPLAINT_STAND_STILL).isoformat(),
                 "endDate": (now).isoformat()
             }
         })
         if 'lots' in tender and tender['lots']:
             data['lots'] = []
             for index, lot in enumerate(tender['lots']):
                 lot_data = {'id': lot['id']}
                 if lot['status'] == 'active':
                     lot_data["auctionPeriod"] = {
                         "startDate": (now).isoformat()
                     }
                 data['lots'].append(lot_data)
         else:
             data.update({"auctionPeriod": {"startDate": now.isoformat()}})
     elif status == 'complete':
         data.update({
             "enquiryPeriod": {
                 "startDate":
                 (now - TENDERING_DURATION - COMPLAINT_STAND_STILL -
                  timedelta(days=3)).isoformat(),
                 "endDate":
                 (now - QUESTIONS_STAND_STILL - COMPLAINT_STAND_STILL -
                  timedelta(days=3)).isoformat()
             },
             "tenderPeriod": {
                 "startDate":
                 (now - TENDERING_DURATION - COMPLAINT_STAND_STILL -
                  timedelta(days=3)).isoformat(),
                 "endDate": (now - COMPLAINT_STAND_STILL -
                             timedelta(days=3)).isoformat()
             },
             "auctionPeriod": {
                 "startDate": (now - timedelta(days=3)).isoformat(),
                 "endDate": (now - timedelta(days=2)).isoformat()
             },
             "awardPeriod": {
                 "startDate": (now - timedelta(days=1)).isoformat(),
                 "endDate": (now).isoformat()
             }
         })
         if self.initial_lots:
             data.update({
                 'lots': [{
                     "auctionPeriod": {
                         "startDate": (now - timedelta(days=3)).isoformat(),
                         "endDate": (now - timedelta(days=2)).isoformat()
                     }
                 } for i in self.initial_lots]
             })
     if extra:
         data.update(extra)
     tender.update(apply_data_patch(tender, data))
     self.db.save(tender)
Пример #45
0
    def test_docs_tutorial(self):
        request_path = '/tenders?opt_pretty=1'

        # Exploring basic rules
        #

        with open('docs/source/tutorial/tender-listing.http', 'w') as self.app.file_obj:
            self.app.authorization = ('Basic', ('broker', ''))
            response = self.app.get('/tenders')
            self.assertEqual(response.status, '200 OK')
            self.app.file_obj.write("\n")

        with open('docs/source/tutorial/tender-post-attempt.http', 'w') as self.app.file_obj:
            response = self.app.post(request_path, 'data', status=415)
            self.assertEqual(response.status, '415 Unsupported Media Type')

        self.app.authorization = ('Basic', ('broker', ''))

        with open('docs/source/tutorial/tender-post-attempt-json.http', 'w') as self.app.file_obj:
            self.app.authorization = ('Basic', ('broker', ''))
            response = self.app.post(
                request_path, 'data', content_type='application/json', status=422)
            self.assertEqual(response.status, '422 Unprocessable Entity')

        # Creating tender
        #

        with open('docs/source/tutorial/tender-post-attempt-json-data.http', 'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders?opt_pretty=1', {"data": test_tender_data})
            self.assertEqual(response.status, '201 Created')

        tender = response.json['data']
        owner_token = response.json['access']['token']

        with open('docs/source/tutorial/blank-tender-view.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}'.format(tender['id']))
            self.assertEqual(response.status, '200 OK')

        with open('docs/source/tutorial/initial-tender-listing.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders')
            self.assertEqual(response.status, '200 OK')

        with open('docs/source/tutorial/create-tender-procuringEntity.http', 'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders?opt_pretty=1', {"data": test_tender_maximum_data})
            self.assertEqual(response.status, '201 Created')

        response = self.app.post_json('/tenders?opt_pretty=1', {"data": test_tender_data})
        self.assertEqual(response.status, '201 Created')

        with open('docs/source/tutorial/tender-listing-after-procuringEntity.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders')
            self.assertEqual(response.status, '200 OK')

        self.app.authorization = ('Basic', ('broker', ''))

        # Modifying tender
        #

        tenderPeriod_endDate = get_now() + timedelta(days=15, seconds=10)
        with open('docs/source/tutorial/patch-items-value-periods.http', 'w') as self.app.file_obj:
            response = self.app.patch_json('/tenders/{}?acc_token={}'.format(tender['id'], owner_token), {'data':
                {
                    "tenderPeriod": {
                        "endDate": tenderPeriod_endDate.isoformat()
                    }
                }
            })

        with open('docs/source/tutorial/tender-listing-after-patch.http', 'w') as self.app.file_obj:
            self.app.authorization = None
            response = self.app.get(request_path)
            self.assertEqual(response.status, '200 OK')

        self.app.authorization = ('Basic', ('broker', ''))
        self.tender_id = tender['id']

        # Setting Bid guarantee
        #
        with open('docs/source/tutorial/set-bid-guarantee.http', 'w') as self.app.file_obj:
            response = self.app.patch_json('/tenders/{}?acc_token={}'.format(
                self.tender_id, owner_token), {"data": {"guarantee": {"amount": 8, "currency": "USD"}}})
            self.assertEqual(response.status, '200 OK')
            self.assertIn('guarantee', response.json['data'])


        # Uploading documentation
        #

        with open('docs/source/tutorial/upload-tender-notice.http', 'w') as self.app.file_obj:
            response = self.app.post('/tenders/{}/documents?acc_token={}'.format(
                self.tender_id, owner_token), upload_files=[('file', u'Notice.pdf', 'content')])
            self.assertEqual(response.status, '201 Created')

        doc_id = response.json["data"]["id"]
        with open('docs/source/tutorial/tender-documents.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/documents/{}'.format(
                self.tender_id, doc_id))
            self.assertEqual(response.status, '200 OK')

        with open('docs/source/tutorial/tender-document-add-documentType.http', 'w') as self.app.file_obj:
            response = self.app.patch_json('/tenders/{}/documents/{}?acc_token={}'.format(
                self.tender_id, doc_id, owner_token), {"data": {"documentType": "technicalSpecifications"}})
            self.assertEqual(response.status, '200 OK')

        with open('docs/source/tutorial/tender-document-edit-docType-desc.http', 'w') as self.app.file_obj:
            response = self.app.patch_json('/tenders/{}/documents/{}?acc_token={}'.format(
                self.tender_id, doc_id, owner_token), {"data": {"description": "document description modified"}})
            self.assertEqual(response.status, '200 OK')

        with open('docs/source/tutorial/upload-award-criteria.http', 'w') as self.app.file_obj:
            response = self.app.post('/tenders/{}/documents?acc_token={}'.format(
                self.tender_id, owner_token), upload_files=[('file', u'AwardCriteria.pdf', 'content')])
            self.assertEqual(response.status, '201 Created')

        doc_id = response.json["data"]["id"]

        with open('docs/source/tutorial/tender-documents-2.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/documents'.format(
                self.tender_id))
            self.assertEqual(response.status, '200 OK')

        with open('docs/source/tutorial/update-award-criteria.http', 'w') as self.app.file_obj:
            response = self.app.put('/tenders/{}/documents/{}?acc_token={}'.format(
                self.tender_id, doc_id, owner_token), upload_files=[('file', 'AwardCriteria-2.pdf', 'content2')])
            self.assertEqual(response.status, '200 OK')

        with open('docs/source/tutorial/tender-documents-3.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/documents'.format(
                self.tender_id))
            self.assertEqual(response.status, '200 OK')

        # Enquiries
        #

        with open('docs/source/tutorial/ask-question.http', 'w') as self.app.file_obj:
            response = self.app.post_json('/tenders/{}/questions'.format(
                self.tender_id), question, status=201)
            question_id = response.json['data']['id']
            self.assertEqual(response.status, '201 Created')

        with open('docs/source/tutorial/answer-question.http', 'w') as self.app.file_obj:
            response = self.app.patch_json('/tenders/{}/questions/{}?acc_token={}'.format(
                self.tender_id, question_id, owner_token), answer, status=200)
            self.assertEqual(response.status, '200 OK')

        with open('docs/source/tutorial/list-question.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/questions'.format(
                self.tender_id))
            self.assertEqual(response.status, '200 OK')

        with open('docs/source/tutorial/get-answer.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/questions/{}'.format(
                self.tender_id, question_id))
            self.assertEqual(response.status, '200 OK')

        # Registering bid
        #

        self.set_status('active.tendering')
        self.app.authorization = ('Basic', ('broker', ''))
        bids_access = {}
        with open('docs/source/tutorial/register-bidder.http', 'w') as self.app.file_obj:
            response = self.app.post_json('/tenders/{}/bids'.format(
                self.tender_id), bid)
            bid1_id = response.json['data']['id']
            bids_access[bid1_id] = response.json['access']['token']
            self.assertEqual(response.status, '201 Created')

        # Proposal Uploading
        #

        with open('docs/source/tutorial/upload-bid-proposal.http', 'w') as self.app.file_obj:
            response = self.app.post('/tenders/{}/bids/{}/documents?acc_token={}'.format(
                self.tender_id, bid1_id, bids_access[bid1_id]), upload_files=[('file', 'Proposal.pdf', 'content')])
            self.assertEqual(response.status, '201 Created')

        with open('docs/source/tutorial/bidder-documents.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/bids/{}/documents?acc_token={}'.format(
                self.tender_id, bid1_id, bids_access[bid1_id]))
            self.assertEqual(response.status, '200 OK')

        # Second bidder registration
        #

        with open('docs/source/tutorial/register-2nd-bidder.http', 'w') as self.app.file_obj:
            response = self.app.post_json('/tenders/{}/bids'.format(
                self.tender_id), bid2)
            bid2_id = response.json['data']['id']
            bids_access[bid2_id] = response.json['access']['token']
            self.assertEqual(response.status, '201 Created')

        # Auction
        #

        self.set_status('active.auction')
        self.app.authorization = ('Basic', ('auction', ''))
        patch_data = {
            'auctionUrl': u'http://auction-sandbox.openprocurement.org/tenders/{}'.format(self.tender_id),
            'bids': [
                {
                    "id": bid1_id,
                    "participationUrl": u'http://auction-sandbox.openprocurement.org/tenders/{}?key_for_bid={}'.format(self.tender_id, bid1_id)
                },
                {
                    "id": bid2_id,
                    "participationUrl": u'http://auction-sandbox.openprocurement.org/tenders/{}?key_for_bid={}'.format(self.tender_id, bid2_id)
                }
            ]
        }
        response = self.app.patch_json('/tenders/{}/auction?acc_token={}'.format(self.tender_id, owner_token),
                                       {'data': patch_data})
        self.assertEqual(response.status, '200 OK')

        self.app.authorization = ('Basic', ('broker', ''))

        with open('docs/source/tutorial/auction-url.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}'.format(self.tender_id))
            self.assertEqual(response.status, '200 OK')

        with open('docs/source/tutorial/bidder-participation-url.http', 'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/bids/{}?acc_token={}'.format(self.tender_id, bid1_id, bids_access[bid1_id]))
            self.assertEqual(response.status, '200 OK')

        with open('docs/source/tutorial/bidder2-participation-url.http', 'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/bids/{}?acc_token={}'.format(self.tender_id, bid2_id, bids_access[bid2_id]))
            self.assertEqual(response.status, '200 OK')

        # Confirming qualification
        #

        self.app.authorization = ('Basic', ('auction', ''))
        response = self.app.get('/tenders/{}/auction'.format(self.tender_id))
        auction_bids_data = response.json['data']['bids']
        response = self.app.post_json('/tenders/{}/auction'.format(self.tender_id),
                                      {'data': {'bids': auction_bids_data}})

        self.app.authorization = ('Basic', ('broker', ''))

        response = self.app.get('/tenders/{}/awards'.format(self.tender_id))
        # get pending award
        award_id = [i['id'] for i in response.json['data'] if i['status'] == 'pending'][0]

        with open('docs/source/tutorial/confirm-qualification.http', 'w') as self.app.file_obj:
            self.app.patch_json('/tenders/{}/awards/{}?acc_token={}'.format(self.tender_id, award_id, owner_token), {"data": {"status": "active"}})
            self.assertEqual(response.status, '200 OK')

        response = self.app.get('/tenders/{}/contracts'.format(self.tender_id))
        self.contract_id = response.json['data'][0]['id']

        ####  Set contract value

        tender = self.db.get(self.tender_id)
        for i in tender.get('awards', []):
            i['complaintPeriod']['endDate'] = i['complaintPeriod']['startDate']
        self.db.save(tender)

        with open('docs/source/tutorial/tender-contract-set-contract-value.http', 'w') as self.app.file_obj:
            response = self.app.patch_json('/tenders/{}/contracts/{}?acc_token={}'.format(
                self.tender_id, self.contract_id, owner_token), {"data": {"contractNumber": "contract #13111", "value": {"amount": 238}}})
        self.assertEqual(response.status, '200 OK')
        self.assertEqual(response.json['data']['value']['amount'], 238)

        #### Setting contract signature date
        #

        with open('docs/source/tutorial/tender-contract-sign-date.http', 'w') as self.app.file_obj:
            response = self.app.patch_json('/tenders/{}/contracts/{}?acc_token={}'.format(
                self.tender_id, self.contract_id, owner_token), {'data': {"dateSigned": get_now().isoformat()} })
            self.assertEqual(response.status, '200 OK')

        #### Setting contract period

        period_dates = {"period": {"startDate": (now).isoformat(), "endDate": (now + timedelta(days=365)).isoformat()}}
        with open('docs/source/tutorial/tender-contract-period.http', 'w') as self.app.file_obj:
            response = self.app.patch_json('/tenders/{}/contracts/{}?acc_token={}'.format(
            self.tender_id, self.contract_id, owner_token), {'data': {'period': period_dates["period"]}})
        self.assertEqual(response.status, '200 OK')

        #### Uploading contract documentation
        #

        with open('docs/source/tutorial/tender-contract-upload-document.http', 'w') as self.app.file_obj:
            response = self.app.post('/tenders/{}/contracts/{}/documents?acc_token={}'.format(
                self.tender_id, self.contract_id, owner_token), upload_files=[('file', 'contract_first_document.doc', 'content')])
            self.assertEqual(response.status, '201 Created')

        with open('docs/source/tutorial/tender-contract-get-documents.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/contracts/{}/documents'.format(
                self.tender_id, self.contract_id))
        self.assertEqual(response.status, '200 OK')

        with open('docs/source/tutorial/tender-contract-upload-second-document.http', 'w') as self.app.file_obj:
            response = self.app.post('/tenders/{}/contracts/{}/documents?acc_token={}'.format(
                self.tender_id, self.contract_id, owner_token), upload_files=[('file', 'contract_second_document.doc', 'content')])
            self.assertEqual(response.status, '201 Created')

        with open('docs/source/tutorial/tender-contract-get-documents-again.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/contracts/{}/documents'.format(
                self.tender_id, self.contract_id))
        self.assertEqual(response.status, '200 OK')

        #### Contract signing
        #

        tender = self.db.get(self.tender_id)
        for i in tender.get('awards', []):
            i['complaintPeriod']['endDate'] = i['complaintPeriod']['startDate']
        self.db.save(tender)

        with open('docs/source/tutorial/tender-contract-sign.http', 'w') as self.app.file_obj:
            response = self.app.patch_json('/tenders/{}/contracts/{}?acc_token={}'.format(
                    self.tender_id, self.contract_id, owner_token), {'data': {'status': 'active'}})
            self.assertEqual(response.status, '200 OK')


        # Preparing the cancellation request
        #

        self.set_status('active.awarded')
        with open('docs/source/tutorial/prepare-cancellation.http', 'w') as self.app.file_obj:
            response = self.app.post_json('/tenders/{}/cancellations?acc_token={}'.format(
                self.tender_id, owner_token), cancellation)
            self.assertEqual(response.status, '201 Created')

        cancellation_id = response.json['data']['id']

        # Filling cancellation with protocol and supplementary documentation
        #

        with open('docs/source/tutorial/upload-cancellation-doc.http', 'w') as self.app.file_obj:
            response = self.app.post('/tenders/{}/cancellations/{}/documents?acc_token={}'.format(
                self.tender_id, cancellation_id, owner_token), upload_files=[('file', u'Notice.pdf', 'content')])
            cancellation_doc_id = response.json['data']['id']
            self.assertEqual(response.status, '201 Created')

        with open('docs/source/tutorial/patch-cancellation.http', 'w') as self.app.file_obj:
            response = self.app.patch_json('/tenders/{}/cancellations/{}/documents/{}?acc_token={}'.format(
                self.tender_id, cancellation_id, cancellation_doc_id, owner_token), {'data': {"description": 'Changed description'}})
            self.assertEqual(response.status, '200 OK')

        with open('docs/source/tutorial/update-cancellation-doc.http', 'w') as self.app.file_obj:
            response = self.app.put('/tenders/{}/cancellations/{}/documents/{}?acc_token={}'.format(
                self.tender_id, cancellation_id, cancellation_doc_id, owner_token), upload_files=[('file', 'Notice-2.pdf', 'content2')])
            self.assertEqual(response.status, '200 OK')

        # Activating the request and cancelling tender
        #

        with open('docs/source/tutorial/active-cancellation.http', 'w') as self.app.file_obj:
            response = self.app.patch_json('/tenders/{}/cancellations/{}?acc_token={}'.format(
                self.tender_id, cancellation_id, owner_token), {"data": {"status": "active"}})
            self.assertEqual(response.status, '200 OK')
Пример #46
0
    def test_docs(self):
        request_path = '/tenders?opt_pretty=1'

        #### Exploring basic rules

        with  open(TARGET_DIR + 'tender-listing.http', 'w') as self.app.file_obj:
            self.app.authorization = None
            response = self.app.get(request_path)
            self.assertEqual(response.status, '200 OK')
            self.app.file_obj.write("\n")

        with  open(TARGET_DIR + 'tender-post-attempt.http', 'w') as self.app.file_obj:
            response = self.app.post(request_path, 'data', status=415)
            self.assertEqual(response.status, '415 Unsupported Media Type')

        self.app.authorization = ('Basic', ('broker', ''))

        with open(TARGET_DIR + 'tender-post-attempt-json.http', 'w') as self.app.file_obj:
            self.app.authorization = ('Basic', ('broker', ''))
            response = self.app.post(
                request_path, 'data', content_type='application/json', status=422)
            self.assertEqual(response.status, '422 Unprocessable Entity')

        #### Creating tender

        with open(TARGET_DIR + 'tender-post-attempt-json-data.http', 'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders?opt_pretty=1',
                {'data': test_tender_data})
            self.assertEqual(response.status, '201 Created')

        tender = response.json['data']
        owner_token = response.json['access']['token']

        with open(TARGET_DIR + 'blank-tender-view.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}'.format(tender['id']))
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'tender-listing-no-auth.http', 'w') as self.app.file_obj:
            self.app.authorization = None
            response = self.app.get(request_path)
            self.assertEqual(response.status, '200 OK')

        # have to make two equal requests, because after first we dont see tender list
        with open(TARGET_DIR + 'tender-listing-no-auth.http', 'w') as self.app.file_obj:
            self.app.authorization = None
            response = self.app.get(request_path)
            self.assertEqual(response.status, '200 OK')

        self.app.authorization = ('Basic', ('broker', ''))

        #### Modifying tender

        tenderPeriod_endDate = get_now() + timedelta(days=30, seconds=10)
        with open(TARGET_DIR + 'patch-items-value-periods.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}?acc_token={}'.format(tender['id'], owner_token),
                {'data': {"tenderPeriod": {"endDate": tenderPeriod_endDate.isoformat()}}})
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'tender-listing-after-patch.http', 'w') as self.app.file_obj:
            self.app.authorization = None
            response = self.app.get(request_path)
            self.assertEqual(response.status, '200 OK')

        self.app.authorization = ('Basic', ('broker', ''))
        self.tender_id = tender['id']

        # Setting Bid guarantee

        with open(TARGET_DIR + 'set-bid-guarantee.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}?acc_token={}'.format(
                    self.tender_id, owner_token),
                {'data': {"guarantee": {"amount": 8, "currency": "USD"}}})
            self.assertEqual(response.status, '200 OK')
            self.assertIn('guarantee', response.json['data'])

        #### Uploading documentation

        with open(TARGET_DIR + 'upload-tender-notice.http', 'w') as self.app.file_obj:
            response = self.app.post(
                '/tenders/{}/documents?acc_token={}'.format(
                    self.tender_id, owner_token),
                upload_files=[('file', u'Notice.pdf', 'content')])
            self.assertEqual(response.status, '201 Created')

        doc_id = response.json["data"]["id"]
        with open(TARGET_DIR + 'tender-documents.http', 'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/documents/{}?acc_token={}'.format(
                    self.tender_id, doc_id, owner_token))
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'upload-award-criteria.http', 'w') as self.app.file_obj:
            response = self.app.post(
                '/tenders/{}/documents?acc_token={}'.format(
                    self.tender_id, owner_token),
                upload_files=[('file', u'AwardCriteria.pdf', 'content')])
            self.assertEqual(response.status, '201 Created')

        doc_id = response.json["data"]["id"]

        with open(TARGET_DIR + 'tender-documents-2.http', 'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/documents?acc_token={}'.format(
                    self.tender_id, owner_token))
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'update-award-criteria.http', 'w') as self.app.file_obj:
            response = self.app.put(
                '/tenders/{}/documents/{}?acc_token={}'.format(
                    self.tender_id, doc_id, owner_token),
                upload_files=[('file', 'AwardCriteria-2.pdf', 'content2')])
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'tender-documents-3.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/documents'.format(
                self.tender_id))
            self.assertEqual(response.status, '200 OK')

        #### Enquiries

        with open(TARGET_DIR + 'ask-question.http', 'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders/{}/questions'.format(self.tender_id),
                {'data': question}, status=201)
            question_id = response.json['data']['id']
            self.assertEqual(response.status, '201 Created')

        with open(TARGET_DIR + 'answer-question.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/questions/{}?acc_token={}'.format(
                    self.tender_id, question_id, owner_token),
                {"data": {"answer": "Таблицю додано в файлі \"Kalorijnist.xslx\""}}, status=200)
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'list-question.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/questions'.format(
                self.tender_id))
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'get-answer.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/questions/{}'.format(
                self.tender_id, question_id))
            self.assertEqual(response.status, '200 OK')

        self.time_shift('enquiryPeriod_ends')
        self.app.authorization = ('Basic', ('broker', ''))
        with open(TARGET_DIR + 'update-tender-after-enqiery.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}?acc_token={}'.format(tender['id'], owner_token))
            self.assertEqual(response.status, '200 OK')
            response = self.app.patch_json(
                '/tenders/{}?acc_token={}'.format(tender['id'], owner_token),
                {'data': {"minValue": {'amount': 501.0}}}, status=403)
            self.assertEqual(response.status, '403 Forbidden')

        with open(TARGET_DIR + 'ask-question-after-enquiry-period.http', 'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders/{}/questions'.format(self.tender_id),
                {'data': question}, status=403)
            self.assertEqual(response.status, '403 Forbidden')

        with open(TARGET_DIR + 'update-tender-after-enqiery-with-update-periods.http', 'w') as self.app.file_obj:
            tenderPeriod_endDate = get_now() + timedelta(days=8)
            response = self.app.patch_json(
                '/tenders/{}?acc_token={}'.format(tender['id'], owner_token),
                {'data': {
                    "minValue": {
                        "amount": 501,
                        "currency": u"UAH"
                    },
                    "tenderPeriod": {
                        "endDate": tenderPeriod_endDate.isoformat()
                    }
                }})
            self.assertEqual(response.status, '200 OK')

        ### Registering bid

        bids_access = {}
        with open(TARGET_DIR + 'register-bidder.http', 'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders/{}/bids'.format(self.tender_id),
                {'data': bid})
            bid1_id = response.json['data']['id']
            bids_access[bid1_id] = response.json['access']['token']
            self.assertEqual(response.status, '201 Created')

        with open(TARGET_DIR + 'activate-bidder.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/bids/{}?acc_token={}'.format(
                    self.tender_id, bid1_id, bids_access[bid1_id]),
                {'data': {"status": "pending"}})
            self.assertEqual(response.status, '200 OK')

        #### Proposal Uploading

        with open(TARGET_DIR + 'upload-bid-proposal.http', 'w') as self.app.file_obj:
            response = self.app.post(
                '/tenders/{}/bids/{}/documents?acc_token={}'.format(
                    self.tender_id, bid1_id, bids_access[bid1_id]),
                upload_files=[('file', 'Proposal.pdf', 'content')])
            self.assertEqual(response.status, '201 Created')

        with open(TARGET_DIR + 'upload-bid-private-proposal.http', 'w') as self.app.file_obj:
            response = self.app.post(
                '/tenders/{}/bids/{}/documents?acc_token={}'.format(
                    self.tender_id, bid1_id, bids_access[bid1_id]),
                upload_files=[('file', 'Proposal_top_secrets.pdf', 'content')])
            self.assertEqual(response.status, '201 Created')
            priv_doc_id = response.json['data']['id']

        # set confidentiality properties
        with open(TARGET_DIR + 'mark-bid-doc-private.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/bids/{}/documents/{}?acc_token={}'.format(
                    self.tender_id, bid1_id, priv_doc_id, bids_access[bid1_id]),
                {'data': {
                    'confidentiality': 'buyerOnly',
                    'confidentialityRationale': 'Only our company sells badgers with pink hair.',
                }})
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'upload-bid-financial-document-proposal.http', 'w') as self.app.file_obj:
            response = self.app.post(
                '/tenders/{}/bids/{}/financial_documents?acc_token={}'.format(
                    self.tender_id, bid1_id, bids_access[bid1_id]),
                upload_files=[('file', 'financial_doc.pdf', '1000$')])
            self.assertEqual(response.status, '201 Created')

            response = self.app.post(
                '/tenders/{}/bids/{}/financial_documents?acc_token={}'.format(
                    self.tender_id, bid1_id, bids_access[bid1_id]),
                upload_files=[('file', 'financial_doc2.pdf', '1000$')])
            self.assertEqual(response.status, '201 Created')

        with open(TARGET_DIR + 'bidder-documents.http', 'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/bids/{}/documents?acc_token={}'.format(
                    self.tender_id, bid1_id, bids_access[bid1_id]))
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'bidder-financial-documents.http', 'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/bids/{}/financial_documents?acc_token={}'.format(
                    self.tender_id, bid1_id, bids_access[bid1_id]))
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'upload-bid-eligibility-document-proposal.http', 'w') as self.app.file_obj:
            response = self.app.post(
                '/tenders/{}/bids/{}/eligibility_documents?acc_token={}'.format(
                    self.tender_id, bid1_id, bids_access[bid1_id]),
                upload_files=[('file', 'eligibility_doc.pdf', 'content')])
            self.assertEqual(response.status, '201 Created')

        with open(TARGET_DIR + 'upload-bid-qualification-document-proposal.http', 'w') as self.app.file_obj:
            response = self.app.post(
                '/tenders/{}/bids/{}/qualification_documents?acc_token={}'.format(
                    self.tender_id, bid1_id, bids_access[bid1_id]),
                upload_files=[('file', 'qualification_document.pdf', 'content')])
            self.assertEqual(response.status, '201 Created')

        with open(TARGET_DIR + 'bidder-view-financial-documents.http', 'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/bids/{}?acc_token={}'.format(
                    self.tender_id, bid1_id, bids_access[bid1_id]))
            self.assertEqual(response.status, '200 OK')

        response = self.app.patch_json(
            '/tenders/{}?acc_token={}'.format(tender['id'], owner_token),
            {'data': {"minValue": {'amount': 501.0}}})
        self.assertEqual(response.status, '200 OK')

        #### Bid invalidation

        with open(TARGET_DIR + 'bidder-after-changing-tender.http', 'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/bids/{}?acc_token={}'.format(
                    self.tender_id, bid1_id, bids_access[bid1_id]))
            self.assertEqual(response.status, '200 OK')

        #### Bid confirmation

        with open(TARGET_DIR + 'bidder-activate-after-changing-tender.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/bids/{}?acc_token={}'.format(
                    self.tender_id, bid1_id, bids_access[bid1_id]),
                {'data': {"status": "pending"}})
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'register-2nd-bidder.http', 'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders/{}/bids'.format(self.tender_id),
                {'data': bid2})
            bid2_id = response.json['data']['id']
            bids_access[bid2_id] = response.json['access']['token']
            self.assertEqual(response.status, '201 Created')

        bid_document2.update({
            'confidentiality': 'buyerOnly',
            'confidentialityRationale': 'Only our company sells badgers with pink hair.'
        })
        bid3["documents"] = [bid_document, bid_document2]
        for document in bid3['documents']:
            document['url'] = self.generate_docservice_url()
        for document in bid3['eligibilityDocuments']:
            document['url'] = self.generate_docservice_url()
        for document in bid3['financialDocuments']:
            document['url'] = self.generate_docservice_url()
        for document in bid3['qualificationDocuments']:
            document['url'] = self.generate_docservice_url()

        with open(TARGET_DIR + 'register-3rd-bidder.http', 'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders/{}/bids'.format(self.tender_id),
                {'data': bid3})
            bid3_id = response.json['data']['id']
            bids_access[bid3_id] = response.json['access']['token']
            self.assertEqual(response.status, '201 Created')

        # Pre-qualification

        self.set_status(
            'active.pre-qualification',
            {"id": self.tender_id, 'status': 'active.tendering'})
        auth = self.app.authorization
        self.app.authorization = ('Basic', ('chronograph', ''))
        response = self.app.patch_json(
            '/tenders/{}'.format(self.tender_id),
            {'data': {"id": self.tender_id}})
        self.app.authorization = auth

        with open(TARGET_DIR + 'qualifications-listing.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}'.format(self.tender_id))
            self.assertEqual(response.status, "200 OK")
            qualifications = response.json['data']['qualifications']
            self.assertEqual(len(qualifications), 3)
            self.assertEqual(qualifications[0]['bidID'], bid1_id)
            self.assertEqual(qualifications[1]['bidID'], bid2_id)
            self.assertEqual(qualifications[2]['bidID'], bid3_id)

        with open(TARGET_DIR + 'approve-qualification1.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/qualifications/{}?acc_token={}'.format(
                    self.tender_id, qualifications[0]['id'], owner_token),
                {"data": {
                    "status": "active",
                    "qualified": True,
                    "eligible": True
                }})
            self.assertEqual(response.status, "200 OK")
        with open(TARGET_DIR + 'approve-qualification2.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/qualifications/{}?acc_token={}'.format(
                    self.tender_id, qualifications[1]['id'], owner_token),
                {"data": {
                    "status": "active",
                    "qualified": True,
                    "eligible": True
                }})
            self.assertEqual(response.status, "200 OK")

        with open(TARGET_DIR + 'reject-qualification3.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/qualifications/{}?acc_token={}'.format(
                    self.tender_id, qualifications[2]['id'], owner_token),
                {'data': {"status": "unsuccessful"}})
            self.assertEqual(response.status, "200 OK")

        with open(TARGET_DIR + 'qualificated-bids-view.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/bids?acc_token={}'.format(
                self.tender_id, owner_token))
            self.assertEqual(response.status, "200 OK")

        with open(TARGET_DIR + 'rejected-bid-view.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/bids/{}?acc_token={}'.format(
                self.tender_id, bid3_id, owner_token))
            self.assertEqual(response.status, "200 OK")

        # active.pre-qualification.stand-still
        with open(TARGET_DIR + 'pre-qualification-confirmation.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}?acc_token={}'.format(self.tender_id, owner_token),
                {"data": {"status": "active.pre-qualification.stand-still"}})
            self.assertEqual(response.status, "200 OK")
            self.assertEqual(response.json['data']['status'], "active.pre-qualification.stand-still")

        #### Auction

        self.set_status('active.auction')
        self.app.authorization = ('Basic', ('auction', ''))
        auction_url = u'{}/tenders/{}'.format(self.auctions_url, self.tender_id)
        patch_data = {
            'auctionUrl': auction_url,
            'bids': [{
                "id": bid1_id,
                "participationUrl": u'{}?key_for_bid={}'.format(auction_url, bid1_id)
            }, {
                "id": bid2_id,
                "participationUrl": u'{}?key_for_bid={}'.format(auction_url, bid2_id)
            }, {
                "id": bid3_id
            }]
        }
        response = self.app.patch_json(
            '/tenders/{}/auction?acc_token={}'.format(self.tender_id, owner_token),
            {'data': patch_data})
        self.assertEqual(response.status, '200 OK')

        self.app.authorization = ('Basic', ('broker', ''))

        with open(TARGET_DIR + 'auction-url.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}'.format(self.tender_id))
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'bidder-participation-url.http', 'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/bids/{}?acc_token={}'.format(self.tender_id, bid1_id, bids_access[bid1_id]))
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'bidder2-participation-url.http', 'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/bids/{}?acc_token={}'.format(self.tender_id, bid2_id, bids_access[bid2_id]))
            self.assertEqual(response.status, '200 OK')

        #### Confirming qualification
        self.app.authorization = ('Basic', ('auction', ''))
        response = self.app.get('/tenders/{}/auction'.format(self.tender_id))
        auction_bids_data = response.json['data']['bids']
        response = self.app.post_json(
            '/tenders/{}/auction'.format(self.tender_id),
            {'data': {'bids': auction_bids_data}})

        self.app.authorization = ('Basic', ('broker', ''))

        response = self.app.get('/tenders/{}/awards?acc_token={}'.format(self.tender_id, owner_token))

        # get pending award
        award_id = [i['id'] for i in response.json['data'] if i['status'] == 'pending'][0]

        with open(TARGET_DIR + 'confirm-qualification.http', 'w') as self.app.file_obj:
            self.app.patch_json(
                '/tenders/{}/awards/{}?acc_token={}'.format(self.tender_id, award_id, owner_token),
                {"data": {
                    "status": "active",
                    "qualified": True,
                    "eligible": True
                }})
            self.assertEqual(response.status, '200 OK')

        response = self.app.get('/tenders/{}/contracts?acc_token={}'.format(
            self.tender_id, owner_token))
        self.contract_id = response.json['data'][0]['id']

        ####  Set contract value

        self.tick()

        tender = self.db.get(self.tender_id)
        for i in tender.get('awards', []):
            i['complaintPeriod']['endDate'] = i['complaintPeriod']['startDate']
        self.db.save(tender)

        with open(TARGET_DIR + 'tender-contract-set-contract-value.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/contracts/{}?acc_token={}'.format(
                    self.tender_id, self.contract_id, owner_token),
                {"data": {
                    "contractNumber": "contract#1",
                    "value": {"amountNet": response.json['data'][0]['value']['amount'] - 1}
                }})
            self.assertEqual(response.status, '200 OK')
        self.assertEqual(
            response.json['data']['value']['amountNet'],
            response.json['data']['value']['amount'] - 1)

        with open(TARGET_DIR + 'tender-contract-sign-date.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/contracts/{}?acc_token={}'.format(
                    self.tender_id, self.contract_id, owner_token),
                {'data': {"dateSigned": get_now().isoformat()}})
            self.assertEqual(response.status, '200 OK')

        #### Setting contract period

        period_dates = {"period": {
            "startDate": (get_now()).isoformat(),
            "endDate": (get_now() + timedelta(days=365)).isoformat()
        }}
        with open(TARGET_DIR + 'tender-contract-period.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/contracts/{}?acc_token={}'.format(
                    self.tender_id, self.contract_id, owner_token),
                {'data': {'period': period_dates["period"]}})
        self.assertEqual(response.status, '200 OK')

        #### Uploading contract documentation

        with open(TARGET_DIR + 'tender-contract-upload-document.http', 'w') as self.app.file_obj:
            response = self.app.post('/tenders/{}/contracts/{}/documents?acc_token={}'.format(
                self.tender_id, self.contract_id, owner_token),
                upload_files=[('file', 'contract_first_document.doc', 'content')])
            self.assertEqual(response.status, '201 Created')

        with open(TARGET_DIR + 'tender-contract-get-documents.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/contracts/{}/documents'.format(
                self.tender_id, self.contract_id))
        self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'tender-contract-upload-second-document.http', 'w') as self.app.file_obj:
            response = self.app.post('/tenders/{}/contracts/{}/documents?acc_token={}'.format(
                self.tender_id, self.contract_id, owner_token),
                upload_files=[('file', 'contract_second_document.doc', 'content')])
            self.assertEqual(response.status, '201 Created')
            self.document_id = response.json['data']['id']

        with open(TARGET_DIR + 'tender-contract-patch-document.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/contracts/{}/documents/{}?acc_token={}'.format(
                    self.tender_id, self.contract_id, self.document_id, owner_token),
                {'data': {
                    "language": 'en',
                    'title_en': 'Title of Document',
                    'description_en': 'Description of Document'
                }})
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'tender-contract-get-documents-again.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/contracts/{}/documents'.format(
                self.tender_id, self.contract_id))
        self.assertEqual(response.status, '200 OK')

        tender_contract_separate_id = response.json['data'][0]['id']

        with open(TARGET_DIR + 'tender-contract-get-separate.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/contracts/{}/documents/{}?acc_token={}'.format(
                self.tender_id, self.contract_id, tender_contract_separate_id, owner_token))
            self.assertEqual(response.status, '200 OK')

        #### Preparing the cancellation request

        with open(TARGET_DIR + 'prepare-cancellation.http', 'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders/{}/cancellations?acc_token={}'.format(
                    self.tender_id, owner_token),
                {'data': {'reason': 'cancellation reason', 'reasonType': 'unFixable'}})
            self.assertEqual(response.status, '201 Created')

        cancellation_id = response.json['data']['id']

        with open(TARGET_DIR + 'update-cancellation-reasonType.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/cancellations/{}?acc_token={}'.format(
                    self.tender_id, cancellation_id, owner_token),
                {'data': {'reasonType': 'expensesCut'}})
            self.assertEqual(response.status, '200 OK')

        #### Filling cancellation with protocol and supplementary documentation

        with open(TARGET_DIR + 'upload-cancellation-doc.http', 'w') as self.app.file_obj:
            response = self.app.post(
                '/tenders/{}/cancellations/{}/documents?acc_token={}'.format(
                    self.tender_id, cancellation_id, owner_token),
                upload_files=[('file', u'Notice.pdf', 'content')])
            cancellation_doc_id = response.json['data']['id']
            self.assertEqual(response.status, '201 Created')

        with open(TARGET_DIR + 'patch-cancellation.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/cancellations/{}/documents/{}?acc_token={}'.format(
                    self.tender_id, cancellation_id, cancellation_doc_id, owner_token),
                {'data': {"description": 'Changed description'}})
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'update-cancellation-doc.http', 'w') as self.app.file_obj:
            response = self.app.put(
                '/tenders/{}/cancellations/{}/documents/{}?acc_token={}'.format(
                    self.tender_id, cancellation_id, cancellation_doc_id, owner_token),
                upload_files=[('file', 'Notice-2.pdf', 'content2')])
            self.assertEqual(response.status, '200 OK')

        #### Activating the request and cancelling tender
        with open(TARGET_DIR + 'pending-cancellation.http', 'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/cancellations/{}?acc_token={}'.format(
                    self.tender_id, cancellation_id, owner_token),
                {'data': {"status": "pending"}})
            self.assertEqual(response.status, '200 OK')

        self.tick(delta=timedelta(days=11))
        self.check_chronograph()

        with open(TARGET_DIR + 'active-cancellation.http', 'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/cancellations/{}?acc_token={}'.format(
                    self.tender_id, cancellation_id, owner_token))
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'tender-cancelled.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}?acc_token={}'.format(
                self.tender_id, owner_token))
            self.assertEqual(response.status, '200 OK')
 def next_check(self):
     now = get_now()
     checks = []
     if self.status == 'active.tendering' and self.tenderPeriod.endDate:
         checks.append(self.tenderPeriod.endDate.astimezone(TZ))
     elif self.status == 'active.pre-qualification.stand-still' and self.qualificationPeriod and self.qualificationPeriod.endDate and not any(
         [
             i.status in self.block_complaint_status
             for q in self.qualifications for i in q.complaints
         ]):
         checks.append(self.qualificationPeriod.endDate.astimezone(TZ))
     elif not self.lots and self.status == 'active.auction' and self.auctionPeriod and self.auctionPeriod.startDate and not self.auctionPeriod.endDate:
         if now < self.auctionPeriod.startDate:
             checks.append(self.auctionPeriod.startDate.astimezone(TZ))
         elif now < calc_auction_end_time(
                 self.numberOfBids,
                 self.auctionPeriod.startDate).astimezone(TZ):
             checks.append(
                 calc_auction_end_time(
                     self.numberOfBids,
                     self.auctionPeriod.startDate).astimezone(TZ))
     elif self.lots and self.status == 'active.auction':
         for lot in self.lots:
             if lot.status != 'active' or not lot.auctionPeriod or not lot.auctionPeriod.startDate or lot.auctionPeriod.endDate:
                 continue
             if now < lot.auctionPeriod.startDate:
                 checks.append(lot.auctionPeriod.startDate.astimezone(TZ))
             elif now < calc_auction_end_time(
                     lot.numberOfBids,
                     lot.auctionPeriod.startDate).astimezone(TZ):
                 checks.append(
                     calc_auction_end_time(
                         lot.numberOfBids,
                         lot.auctionPeriod.startDate).astimezone(TZ))
     elif not self.lots and self.status == 'active.awarded' and not any(
         [i.status in self.block_complaint_status
          for i in self.complaints]) and not any([
              i.status in self.block_complaint_status for a in self.awards
              for i in a.complaints
          ]):
         standStillEnds = [
             a.complaintPeriod.endDate.astimezone(TZ) for a in self.awards
             if a.complaintPeriod.endDate
         ]
         last_award_status = self.awards[-1].status if self.awards else ''
         if standStillEnds and last_award_status == 'unsuccessful':
             checks.append(max(standStillEnds))
     elif self.lots and self.status in [
             'active.qualification', 'active.awarded'
     ] and not any([
             i.status in self.block_complaint_status
             and i.relatedLot is None for i in self.complaints
     ]):
         for lot in self.lots:
             if lot['status'] != 'active':
                 continue
             lot_awards = [i for i in self.awards if i.lotID == lot.id]
             pending_complaints = any([
                 i['status'] in self.block_complaint_status
                 and i.relatedLot == lot.id for i in self.complaints
             ])
             pending_awards_complaints = any([
                 i.status in self.block_complaint_status for a in lot_awards
                 for i in a.complaints
             ])
             standStillEnds = [
                 a.complaintPeriod.endDate.astimezone(TZ)
                 for a in lot_awards if a.complaintPeriod.endDate
             ]
             last_award_status = lot_awards[-1].status if lot_awards else ''
             if not pending_complaints and not pending_awards_complaints and standStillEnds and last_award_status == 'unsuccessful':
                 checks.append(max(standStillEnds))
     return min(checks).isoformat() if checks else None
def from0to1(registry):
    class Request(object):
        def __init__(self, registry):
            self.registry = registry

    results = registry.db.iterview('auctions/all', 2**10, include_docs=True)

    request = Request(registry)
    root = Root(request)

    docs = []
    for i in results:
        auction = i.doc
        if auction['procurementMethodType'] not in ['dgfOtherAssets', 'dgfFinancialAssets'] \
                or auction['status'] not in ['active.qualification', 'active.awarded'] \
                or 'awards' not in auction:
            continue

        now = get_now().isoformat()
        awards = auction["awards"]
        unique_awards = len(set([a['bid_id'] for a in awards]))

        if unique_awards > 2:
            switch_auction_to_unsuccessful(auction)
        else:
            invalidate_bids_under_threshold(auction)
            if all(bid['status'] == 'invalid' for bid in auction['bids']):
                switch_auction_to_unsuccessful(auction)

        if auction['status'] != 'unsuccessful':
            award = [
                a for a in auction["awards"]
                if a['status'] in ['active', 'pending']
            ][0]

            award_create_date = award['complaintPeriod']['startDate']

            periods = {
                'verificationPeriod': {
                    'startDate': award_create_date,
                    'endDate': award_create_date
                },
                'paymentPeriod': {
                    'startDate':
                    award_create_date,
                    'endDate':
                    calculate_business_date(parse_date(award_create_date,
                                                       TZ), AWARD_PAYMENT_TIME,
                                            auction, True).isoformat()
                },
                'signingPeriod': {
                    'startDate':
                    award_create_date,
                    'endDate':
                    calculate_business_date(parse_date(award_create_date, TZ),
                                            CONTRACT_SIGNING_TIME, auction,
                                            True).isoformat()
                }
            }

            award.update(periods)

            if award['status'] == 'pending':
                award['status'] = 'pending.payment'

            elif award['status'] == 'active':
                award['verificationPeriod']['endDate'] = award[
                    'paymentPeriod']['endDate'] = now

            if unique_awards == 1:
                bid = chef(auction['bids'], auction.get('features'), [],
                           True)[1]

                award = {
                    'id': uuid4().hex,
                    'bid_id': bid['id'],
                    'status': 'pending.waiting',
                    'date': awards[0]['date'],
                    'value': bid['value'],
                    'suppliers': bid['tenderers'],
                    'complaintPeriod': {
                        'startDate': awards[0]['date']
                    }
                }
                if bid['status'] == 'invalid':
                    award['status'] == 'unsuccessful'
                    award['complaintPeriod']['endDate'] = now

                awards.append(award)

        model = registry.auction_procurementMethodTypes.get(
            auction['procurementMethodType'])
        if model:
            try:
                auction = model(auction)
                auction.__parent__ = root
                auction = auction.to_primitive()
            except:
                LOGGER.error(
                    "Failed migration of auction {} to schema 1.".format(
                        auction.id),
                    extra={
                        'MESSAGE_ID': 'migrate_data_failed',
                        'AUCTION_ID': auction.id
                    })
            else:
                auction['dateModified'] = get_now().isoformat()
                docs.append(auction)
        if len(docs) >= 2**7:
            registry.db.update(docs)
            docs = []
    if docs:
        registry.db.update(docs)
Пример #49
0
    def test_docs_tutorial(self):
        request_path = '/tenders?opt_pretty=1'

        # Exploring basic rules

        with open(TARGET_DIR + 'tender-listing.http',
                  'w') as self.app.file_obj:
            self.app.authorization = ('Basic', ('broker', ''))
            response = self.app.get('/tenders')
            self.assertEqual(response.status, '200 OK')
            self.app.file_obj.write("\n")

        with open(TARGET_DIR + 'tender-post-attempt.http',
                  'w') as self.app.file_obj:
            response = self.app.post(request_path, 'data', status=415)
            self.assertEqual(response.status, '415 Unsupported Media Type')

        self.app.authorization = ('Basic', ('broker', ''))

        with open(TARGET_DIR + 'tender-post-attempt-json.http',
                  'w') as self.app.file_obj:
            self.app.authorization = ('Basic', ('broker', ''))
            response = self.app.post(request_path,
                                     'data',
                                     content_type='application/json',
                                     status=422)
            self.assertEqual(response.status, '422 Unprocessable Entity')

        # Creating tender

        agreement_id = uuid4().hex
        agreements = {'agreements': [{'id': agreement_id}]}

        test_features[0]['relatedItem'] = test_agreement['items'][0]['id']
        test_agreement['features'] = test_features
        for contract in test_agreement['contracts']:
            contract['parameters'] = parameters

        lot = deepcopy(test_lots[0])
        lot['id'] = uuid4().hex

        test_tender_data.update(agreements)
        test_tender_data['lots'] = [lot]
        for item in test_tender_data['items']:
            item['relatedLot'] = lot['id']
            item['deliveryDate'] = {
                "startDate": (get_now() + timedelta(days=2)).isoformat(),
                "endDate": (get_now() + timedelta(days=5)).isoformat()
            }

        with open(TARGET_DIR + 'tender-post-attempt-json-data.http',
                  'w') as self.app.file_obj:
            response = self.app.post_json('/tenders?opt_pretty=1',
                                          {'data': test_tender_data})
            self.assertEqual(response.status, '201 Created')

        tender = response.json['data']
        owner_token = response.json['access']['token']

        with open(TARGET_DIR + 'blank-tender-view.http',
                  'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}'.format(tender['id']))
            self.assertEqual(response.status, '200 OK')

        test_tender_maximum_data.update(agreements)
        test_tender_maximum_data['lots'] = [lot]
        test_tender_maximum_data['items'][0]['id'] = uuid4().hex
        test_tender_maximum_data['items'][0]['relatedLot'] = lot['id']

        with open(TARGET_DIR + 'create-tender-procuringEntity.http',
                  'w') as self.app.file_obj:
            response = self.app.post_json('/tenders?opt_pretty=1',
                                          {'data': test_tender_maximum_data})
            self.assertEqual(response.status, '201 Created')

        response = self.app.post_json('/tenders?opt_pretty=1',
                                      {'data': test_tender_data})
        self.assertEqual(response.status, '201 Created')

        self.app.authorization = ('Basic', ('broker', ''))

        with open(TARGET_DIR + 'tender-switch-draft-pending.http',
                  'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}?acc_token={}'.format(tender['id'], owner_token),
                {'data': {
                    'status': 'draft.pending'
                }})
            data = response.json['data']
            self.assertEqual(response.status, '200 OK')
            self.assertEqual(response.json['data']['status'], 'draft.pending')

        self.app.authorization = ('Basic', (BOT_NAME, ''))

        agreement = deepcopy(test_agreement)
        agreement['features'] = test_features

        response = self.app.patch_json(
            '/tenders/{}/agreements/{}'.format(tender['id'], agreement_id),
            {'data': agreement})
        self.assertEqual(response.status, '200 OK')

        response = self.app.patch_json('/tenders/{}'.format(
            tender['id']), {'data': {
                'status': 'active.enquiries'
            }})
        self.assertEqual(response.json['data']['status'], 'active.enquiries')

        self.app.authorization = ('Basic', ('broker', ''))

        with open(TARGET_DIR + 'tender-in-active-enquiries.http',
                  'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}'.format(tender['id']))
            self.assertEqual(response.json['data']['status'],
                             'active.enquiries')
            tender = response.json['data']

        # start couchdb index views
        response = self.app.get('/tenders')

        # wait until couchdb index views complete
        sleep(8)

        with open(TARGET_DIR + 'initial-tender-listing.http',
                  'w') as self.app.file_obj:
            response = self.app.get('/tenders')
            self.assertEqual(response.status, '200 OK')

        response = self.app.post_json('/tenders', {'data': data})
        self.assertEqual((response.status, response.content_type),
                         ('201 Created', 'application/json'))
        self.tender_id = response.json['data']['id']
        self.tender_token = owner_token = response.json['access']['token']

        response = self.app.patch_json(
            '/tenders/{}?acc_token={}'.format(self.tender_id,
                                              self.tender_token),
            {'data': {
                'agreements': [test_agreement]
            }})
        self.assertEqual((response.status, response.content_type),
                         ('200 OK', 'application/json'))

        response = self.app.patch_json(
            '/tenders/{}?acc_token={}'.format(self.tender_id,
                                              self.tender_token),
            {'data': {
                'status': 'draft.pending'
            }})

        self.assertEqual((response.status, response.content_type),
                         ('200 OK', 'application/json'))
        self.assertEqual(response.json['data']['status'], 'draft.pending')

        self.app.authorization = ('Basic', (BOT_NAME, ''))
        response = self.app.patch_json(
            '/tenders/{}?acc_token={}'.format(self.tender_id,
                                              self.tender_token),
            {'data': {
                'agreements': [test_agreement]
            }})
        self.assertEqual((response.status, response.content_type),
                         ('200 OK', 'application/json'))

        response = self.app.patch_json(
            '/tenders/{}?acc_token={}'.format(self.tender_id,
                                              self.tender_token),
            {'data': {
                'status': 'active.enquiries'
            }})
        tender = response.json['data']
        self.assertEqual((response.status, response.content_type),
                         ('200 OK', 'application/json'))
        self.assertEqual(response.json['data']['status'], 'active.enquiries')

        self.app.authorization = ('Basic', ('broker', ''))

        # Modifying tender

        tender_period_end_date = get_now() + timedelta(days=15, seconds=10)
        with open(TARGET_DIR + 'patch-items-value-periods.http',
                  'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}?acc_token={}'.format(tender['id'], owner_token), {
                    'data': {
                        "tenderPeriod": {
                            "endDate": tender_period_end_date.isoformat()
                        },
                        "items": [{
                            "quantity": 6
                        }]
                    }
                })
            self.assertEqual(response.status, '200 OK')
            self.assertEqual(response.content_type, 'application/json')

        with open(TARGET_DIR + 'tender-listing-after-patch.http',
                  'w') as self.app.file_obj:
            self.app.authorization = None
            response = self.app.get(request_path)
            self.assertEqual(response.status, '200 OK')

        self.app.authorization = ('Basic', ('broker', ''))
        self.tender_id = tender['id']

        # Setting Bid guarantee

        with open(TARGET_DIR + 'set-bid-guarantee.http',
                  'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/lots/{}?acc_token={}'.format(
                    self.tender_id, lot['id'], owner_token),
                {"data": {
                    "guarantee": {
                        "amount": 8,
                        "currency": "USD"
                    }
                }})
            self.assertEqual(response.status, '200 OK')
            self.assertIn('guarantee', response.json['data'])

        # Uploading documentation

        with open(TARGET_DIR + 'upload-tender-notice.http',
                  'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders/{}/documents?acc_token={}'.format(
                    self.tender_id, owner_token), {
                        'data': {
                            'title': u'Notice.pdf',
                            'url': self.generate_docservice_url(),
                            'hash': 'md5:' + '0' * 32,
                            'format': 'application/pdf',
                        }
                    })
            self.assertEqual(response.status, '201 Created')

        doc_id = response.json["data"]["id"]
        with open(TARGET_DIR + 'tender-documents.http',
                  'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/documents/{}'.format(
                self.tender_id, doc_id))
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'tender-document-add-documentType.http',
                  'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/documents/{}?acc_token={}'.format(
                    self.tender_id, doc_id, owner_token),
                {'data': {
                    "documentType": "technicalSpecifications"
                }})
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'tender-document-edit-docType-desc.http',
                  'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/documents/{}?acc_token={}'.format(
                    self.tender_id, doc_id, owner_token),
                {'data': {
                    "description": "document description modified"
                }})
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'upload-award-criteria.http',
                  'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders/{}/documents?acc_token={}'.format(
                    self.tender_id, owner_token), {
                        'data': {
                            'title': u'AwardCriteria.pdf',
                            'url': self.generate_docservice_url(),
                            'hash': 'md5:' + '0' * 32,
                            'format': 'application/pdf',
                        }
                    })
            self.assertEqual(response.status, '201 Created')

        doc_id = response.json["data"]["id"]

        with open(TARGET_DIR + 'tender-documents-2.http',
                  'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/documents'.format(
                self.tender_id))
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'update-award-criteria.http',
                  'w') as self.app.file_obj:
            response = self.app.put_json(
                '/tenders/{}/documents/{}?acc_token={}'.format(
                    self.tender_id, doc_id, owner_token), {
                        'data': {
                            'title': u'AwardCriteria-2.pdf',
                            'url': self.generate_docservice_url(),
                            'hash': 'md5:' + '0' * 32,
                            'format': 'application/pdf',
                        }
                    })
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'tender-documents-3.http',
                  'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/documents'.format(
                self.tender_id))
            self.assertEqual(response.status, '200 OK')

        # Switch tender to active.tendering

        self.set_status('active.enquiries', start_end='end')
        self.app.authorization = ('Basic', ('chronograph', ''))
        response = self.app.patch_json('/tenders/{}'.format(tender['id']),
                                       {'data': {}})
        self.assertEqual(response.json['data']['status'], 'active.tendering')

        # Registering bid

        self.app.authorization = ('Basic', ('broker', ''))
        bids_access = {}

        bid['parameters'] = parameters
        bid['lotValues'][0]['relatedLot'] = lot['id']
        with open(TARGET_DIR + 'register-bidder-invalid.http',
                  'w') as self.app.file_obj:
            response = self.app.post_json('/tenders/{}/bids'.format(
                self.tender_id), {'data': bid},
                                          status=403)
            self.assertEqual(response.status, '403 Forbidden')

        bid['tenderers'] = tender['agreements'][0]['contracts'][0]['suppliers']
        with open(TARGET_DIR + 'register-bidder.http',
                  'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders/{}/bids'.format(self.tender_id), {'data': bid})
            bid1_id = response.json['data']['id']
            bids_access[bid1_id] = response.json['access']['token']
            self.assertEqual(response.status, '201 Created')

        with open(TARGET_DIR + 'activate-bidder.http',
                  'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/bids/{}?acc_token={}'.format(
                    self.tender_id, bid1_id, bids_access[bid1_id]),
                {'data': {
                    "status": "active"
                }})
            self.assertEqual(response.status, '200 OK')

        # Proposal Uploading

        with open(TARGET_DIR + 'upload-bid-proposal.http',
                  'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders/{}/bids/{}/documents?acc_token={}'.format(
                    self.tender_id, bid1_id, bids_access[bid1_id]), {
                        'data': {
                            'title': u'Proposal.pdf',
                            'url': self.generate_docservice_url(),
                            'hash': 'md5:' + '0' * 32,
                            'format': 'application/pdf',
                        }
                    })
            self.assertEqual(response.status, '201 Created')

        with open(TARGET_DIR + 'bidder-documents.http',
                  'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/bids/{}/documents?acc_token={}'.format(
                    self.tender_id, bid1_id, bids_access[bid1_id]))
            self.assertEqual(response.status, '200 OK')

        # Second bid registration with documents

        bid2['parameters'] = parameters
        bid2['lotValues'][0]['relatedLot'] = lot['id']
        bid2['tenderers'] = tender['agreements'][0]['contracts'][1][
            'suppliers']
        with open(TARGET_DIR + 'register-2nd-bidder.http',
                  'w') as self.app.file_obj:
            for document in bid2['documents']:
                document['url'] = self.generate_docservice_url()
            response = self.app.post_json(
                '/tenders/{}/bids'.format(self.tender_id), {'data': bid2})
            bid2_id = response.json['data']['id']
            bids_access[bid2_id] = response.json['access']['token']
            self.assertEqual(response.status, '201 Created')

        bid3 = deepcopy(bid2)
        bid3['tenderers'] = tender['agreements'][0]['contracts'][3][
            'suppliers']
        for document in bid3['documents']:
            document['url'] = self.generate_docservice_url()
        response = self.app.post_json(
            '/tenders/{}/bids'.format(self.tender_id), {'data': bid3})
        bid3_id = response.json['data']['id']
        bids_access[bid3_id] = response.json['access']['token']
        self.assertEqual(response.status, '201 Created')

        # Auction

        self.set_status('active.auction')
        self.app.authorization = ('Basic', ('auction', ''))
        auction_url = u'{}/tenders/{}_{}'.format(self.auctions_url,
                                                 self.tender_id, lot['id'])
        patch_data = {
            'lots': [{
                'auctionUrl': auction_url,
            }],
            'bids': [{
                "id":
                bid1_id,
                "lotValues": [{
                    "participationUrl":
                    u'{}?key_for_bid={}'.format(auction_url, bid1_id)
                }]
            }, {
                "id":
                bid2_id,
                "lotValues": [{
                    "participationUrl":
                    u'{}?key_for_bid={}'.format(auction_url, bid2_id)
                }]
            }, {
                "id":
                bid3_id,
                "lotValues": [{
                    "participationUrl":
                    u'{}?key_for_bid={}'.format(auction_url, bid3_id)
                }]
            }]
        }
        response = self.app.patch_json(
            '/tenders/{}/auction/{}?acc_token={}'.format(
                self.tender_id, lot['id'], owner_token), {'data': patch_data})
        self.assertEqual(response.status, '200 OK')

        self.app.authorization = ('Basic', ('broker', ''))

        with open(TARGET_DIR + 'auction-url.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}'.format(self.tender_id))
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'bidder-participation-url.http',
                  'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/bids/{}?acc_token={}'.format(
                self.tender_id, bid1_id, bids_access[bid1_id]))
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'bidder2-participation-url.http',
                  'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/bids/{}?acc_token={}'.format(
                self.tender_id, bid2_id, bids_access[bid2_id]))
            self.assertEqual(response.status, '200 OK')

        # Confirming qualification

        self.app.authorization = ('Basic', ('auction', ''))
        response = self.app.get('/tenders/{}/auction'.format(self.tender_id))
        auction_bids_data = response.json['data']['bids']
        response = self.app.post_json(
            '/tenders/{}/auction/{}'.format(self.tender_id, lot['id']),
            {'data': {
                'bids': auction_bids_data
            }})

        self.app.authorization = ('Basic', ('broker', ''))

        with open(TARGET_DIR + 'awards-get.http', 'w') as self.app.file_obj:
            response = self.app.get('/tenders/{}/awards'.format(
                self.tender_id))
            self.assertEqual(response.status, '200 OK')

        # get pending award
        award_id = [
            i['id'] for i in response.json['data'] if i['status'] == 'pending'
        ][0]

        with open(TARGET_DIR + 'award-qualification-unsuccessful.http',
                  'w') as self.app.file_obj:
            self.app.patch_json('/tenders/{}/awards/{}?acc_token={}'.format(
                self.tender_id, award_id, owner_token),
                                {"data": {
                                    "status": "unsuccessful"
                                }},
                                status=403)

        with open(TARGET_DIR + 'award-qualification-active.http',
                  'w') as self.app.file_obj:
            self.app.patch_json(
                '/tenders/{}/awards/{}?acc_token={}'.format(
                    self.tender_id, award_id, owner_token),
                {"data": {
                    "status": "active"
                }})

        with open(TARGET_DIR + 'award-qualification-cancelled.http',
                  'w') as self.app.file_obj:
            self.app.patch_json(
                '/tenders/{}/awards/{}?acc_token={}'.format(
                    self.tender_id, award_id, owner_token),
                {"data": {
                    "status": "cancelled"
                }})

        # get new pending award
        response = self.app.get('/tenders/{}/awards'.format(self.tender_id))
        award_id = [
            i['id'] for i in response.json['data'] if i['status'] == 'pending'
        ][0]

        with open(TARGET_DIR + 'award-qualification-unsuccessful1.http',
                  'w') as self.app.file_obj:
            self.app.patch_json(
                '/tenders/{}/awards/{}?acc_token={}'.format(
                    self.tender_id, award_id, owner_token),
                {"data": {
                    "status": "unsuccessful"
                }})

        # post document for unsuccessful award
        with open(
                TARGET_DIR + 'award-qualification-unsuccessful1_document.http',
                'w') as self.app.file_obj:
            self.app.post_json(
                '/tenders/{}/awards/{}/documents?acc_token={}'.format(
                    self.tender_id, award_id, owner_token), {
                        "data": {
                            "title": u"explanation.pdf",
                            "url": self.generate_docservice_url(),
                            "hash": "md5:" + "0" * 32,
                            "format": "application/pdf",
                        }
                    })

        # get new pending award
        response = self.app.get('/tenders/{}/awards'.format(self.tender_id))
        award_id = [
            i['id'] for i in response.json['data'] if i['status'] == 'pending'
        ][0]

        with open(TARGET_DIR + 'confirm-qualification.http',
                  'w') as self.app.file_obj:
            self.app.patch_json(
                '/tenders/{}/awards/{}?acc_token={}'.format(
                    self.tender_id, award_id, owner_token),
                {"data": {
                    "status": "active"
                }})
            self.assertEqual(response.status, '200 OK')

        response = self.app.get('/tenders/{}/contracts'.format(self.tender_id))
        self.contract_id = [
            c for c in response.json['data'] if c['status'] == 'pending'
        ][0]['id']

        #  Set contract value

        with open(TARGET_DIR + 'tender-contract-set-contract-value.http',
                  'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/contracts/{}?acc_token={}'.format(
                    self.tender_id, self.contract_id, owner_token), {
                        "data": {
                            "contractNumber": "contract #13111",
                            "value": {
                                "amount": 238,
                                "amountNet": 230
                            }
                        }
                    })
        self.assertEqual(response.status, '200 OK')
        self.assertEqual(response.json['data']['value']['amount'], 238)

        # Setting contract signature date

        with open(TARGET_DIR + 'tender-contract-sign-date.http',
                  'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/contracts/{}?acc_token={}'.format(
                    self.tender_id, self.contract_id, owner_token),
                {'data': {
                    "dateSigned": get_now().isoformat()
                }})
            self.assertEqual(response.status, '200 OK')

        # Setting contract period

        period_dates = {
            "period": {
                "startDate": get_now().isoformat(),
                "endDate": (get_now() + timedelta(days=365)).isoformat()
            }
        }
        with open(TARGET_DIR + 'tender-contract-period.http',
                  'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/contracts/{}?acc_token={}'.format(
                    self.tender_id, self.contract_id, owner_token),
                {'data': {
                    'period': period_dates["period"]
                }})
        self.assertEqual(response.status, '200 OK')

        # Uploading contract documentation

        with open(TARGET_DIR + 'tender-contract-upload-document.http',
                  'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders/{}/contracts/{}/documents?acc_token={}'.format(
                    self.tender_id, self.contract_id, owner_token), {
                        'data': {
                            'title': u'contract_first_document.doc',
                            'url': self.generate_docservice_url(),
                            'hash': 'md5:' + '0' * 32,
                            'format': 'application/msword',
                        }
                    })
            self.assertEqual(response.status, '201 Created')

        with open(TARGET_DIR + 'tender-contract-get-documents.http',
                  'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/contracts/{}/documents'.format(
                    self.tender_id, self.contract_id))
        self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'tender-contract-upload-second-document.http',
                  'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders/{}/contracts/{}/documents?acc_token={}'.format(
                    self.tender_id, self.contract_id, owner_token), {
                        'data': {
                            'title': u'contract_second_document.doc',
                            'url': self.generate_docservice_url(),
                            'hash': 'md5:' + '0' * 32,
                            'format': 'application/msword',
                        }
                    })
            self.assertEqual(response.status, '201 Created')

        with open(TARGET_DIR + 'tender-contract-get-documents-again.http',
                  'w') as self.app.file_obj:
            response = self.app.get(
                '/tenders/{}/contracts/{}/documents'.format(
                    self.tender_id, self.contract_id))
        self.assertEqual(response.status, '200 OK')

        # Setting contract signature date

        with open(TARGET_DIR + 'tender-contract-sign-date.http',
                  'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/contracts/{}?acc_token={}'.format(
                    self.tender_id, self.contract_id, owner_token),
                {'data': {
                    "dateSigned": get_now().isoformat()
                }})
            self.assertEqual(response.status, '200 OK')

        # Contract signing

        with open(TARGET_DIR + 'tender-contract-sign.http',
                  'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/contracts/{}?acc_token={}'.format(
                    self.tender_id, self.contract_id, owner_token),
                {'data': {
                    'status': 'active'
                }})
            self.assertEqual(response.status, '200 OK')

        # Preparing the cancellation request

        self.set_status('active.awarded')
        with open(TARGET_DIR + 'prepare-cancellation.http',
                  'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders/{}/cancellations?acc_token={}'.format(
                    self.tender_id, owner_token), {
                        'data': {
                            'reason': 'cancellation reason',
                            'reasonType': 'noDemand',
                        }
                    })
            self.assertEqual(response.status, '201 Created')

        cancellation_id = response.json['data']['id']

        # Filling cancellation with protocol and supplementary documentation

        with open(TARGET_DIR + 'upload-cancellation-doc.http',
                  'w') as self.app.file_obj:
            response = self.app.post_json(
                '/tenders/{}/cancellations/{}/documents?acc_token={}'.format(
                    self.tender_id, cancellation_id, owner_token), {
                        'data': {
                            'title': u'Notice.pdf',
                            'url': self.generate_docservice_url(),
                            'hash': 'md5:' + '0' * 32,
                            'format': 'application/pdf',
                        }
                    })
            cancellation_doc_id = response.json['data']['id']
            self.assertEqual(response.status, '201 Created')

        with open(TARGET_DIR + 'patch-cancellation.http',
                  'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/cancellations/{}/documents/{}?acc_token={}'.
                format(self.tender_id, cancellation_id, cancellation_doc_id,
                       owner_token),
                {'data': {
                    "description": 'Changed description'
                }})
            self.assertEqual(response.status, '200 OK')

        with open(TARGET_DIR + 'update-cancellation-doc.http',
                  'w') as self.app.file_obj:
            response = self.app.put_json(
                '/tenders/{}/cancellations/{}/documents/{}?acc_token={}'.
                format(self.tender_id, cancellation_id, cancellation_doc_id,
                       owner_token), {
                           'data': {
                               'title': u'Notice-2.pdf',
                               'url': self.generate_docservice_url(),
                               'hash': 'md5:' + '0' * 32,
                               'format': 'application/pdf',
                           }
                       })
            self.assertEqual(response.status, '200 OK')

        # Activating the request and cancelling tender

        with open(TARGET_DIR + 'active-cancellation.http',
                  'w') as self.app.file_obj:
            response = self.app.patch_json(
                '/tenders/{}/cancellations/{}?acc_token={}'.format(
                    self.tender_id, cancellation_id, owner_token),
                {"data": {
                    "status": "active"
                }})
            self.assertEqual(response.status, '200 OK')
 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")}
Пример #51
0
def patch_tender_award_complaint_document(self):
    response = self.app.post(
        "/tenders/{}/awards/{}/complaints/{}/documents?acc_token={}".format(
            self.tender_id, self.award_id, self.complaint_id,
            self.complaint_owner_token),
        upload_files=[("file", "name.doc", "content")],
    )
    self.assertEqual(response.status, "201 Created")
    self.assertEqual(response.content_type, "application/json")
    doc_id = response.json["data"]["id"]
    self.assertIn(doc_id, response.headers["Location"])

    response = self.app.patch_json(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id),
        {"data": {
            "description": "document description"
        }},
        status=403,
    )
    self.assertEqual(response.status, "403 Forbidden")
    self.assertEqual(response.content_type, "application/json")
    self.assertEqual(response.json["errors"][0]["description"],
                     "Can update document only author")

    response = self.app.patch_json(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}?acc_token={}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id,
            self.complaint_owner_token),
        {"data": {
            "description": "document description"
        }},
    )
    self.assertEqual(response.status, "200 OK")
    self.assertEqual(response.content_type, "application/json")
    self.assertEqual(doc_id, response.json["data"]["id"])

    response = self.app.get(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id))
    self.assertEqual(response.status, "200 OK")
    self.assertEqual(response.content_type, "application/json")
    self.assertEqual(doc_id, response.json["data"]["id"])
    self.assertEqual("document description",
                     response.json["data"]["description"])

    if get_now() < RELEASE_2020_04_19:
        response = self.app.patch_json(
            "/tenders/{}/awards/{}/complaints/{}?acc_token={}".format(
                self.tender_id, self.award_id, self.complaint_id,
                self.complaint_owner_token),
            {"data": {
                "status": "pending"
            }},
        )
    else:
        with change_auth(self.app, ("Basic", ("bot", ""))):
            response = self.app.patch_json(
                "/tenders/{}/awards/{}/complaints/{}".format(
                    self.tender_id, self.award_id, self.complaint_id),
                {"data": {
                    "status": "pending"
                }},
            )

    self.assertEqual(response.status, "200 OK")
    self.assertEqual(response.json["data"]["status"], "pending")

    response = self.app.patch_json(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}?acc_token={}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id,
            self.complaint_owner_token),
        {"data": {
            "description": "document description2"
        }},
    )
    self.assertEqual(response.status, "200 OK")
    self.assertEqual(response.content_type, "application/json")
    self.assertEqual(response.json["data"]["description"],
                     "document description2")

    cancellation = dict(**test_cancellation)
    cancellation.update({
        "status": "active",
        "cancellationOf": "lot",
        "relatedLot": self.lots[0]["id"],
    })
    response = self.app.post_json(
        "/tenders/{}/cancellations".format(self.tender_id),
        {"data": cancellation},
    )
    self.assertEqual(response.status, "201 Created")
    cancellation_id = response.json["data"]["id"]
    if RELEASE_2020_04_19 < get_now():
        activate_cancellation_after_2020_04_19(self, cancellation_id)

    response = self.app.patch_json(
        "/tenders/{}/awards/{}/complaints/{}/documents/{}?acc_token={}".format(
            self.tender_id, self.award_id, self.complaint_id, doc_id,
            self.complaint_owner_token),
        {"data": {
            "description": "document description"
        }},
        status=403,
    )
    self.assertEqual(response.status, "403 Forbidden")
    self.assertEqual(response.content_type, "application/json")
    self.assertEqual(response.json["errors"][0]["description"],
                     "Can update document only in active lot status")
Пример #52
0
    def post(self):
        """This API request is targeted to creating new Tenders by procuring organizations.

        Creating new Tender
        -------------------

        Example request to create tender:

        .. sourcecode:: http

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

            {
                "data": {
                    "procuringEntity": {
                        "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": 500,
                        "currency": "UAH",
                        "valueAddedTaxIncluded": true
                    },
                    "itemsToBeProcured": [
                        {
                            "description": "футляри до державних нагород",
                            "primaryClassification": {
                                "scheme": "CPV",
                                "id": "44617100-9",
                                "description": "Cartons"
                            },
                            "additionalClassification": [
                                {
                                    "scheme": "ДКПП",
                                    "id": "17.21.1",
                                    "description": "папір і картон гофровані, паперова й картонна тара"
                                }
                            ],
                            "unitOfMeasure": "item",
                            "quantity": 5
                        }
                    ],
                    "enquiryPeriod": {
                        "endDate": "2014-10-31T00:00:00"
                    },
                    "tenderPeriod": {
                        "startDate": "2014-11-03T00:00:00",
                        "endDate": "2014-11-06T10:00:00"
                    },
                    "awardPeriod": {
                        "endDate": "2014-11-13T00:00:00"
                    },
                    "deliveryDate": {
                        "endDate": "2014-11-20T00:00:00"
                    },
                    "minimalStep": {
                        "amount": 35,
                        "currency": "UAH"
                    }
                }
            }

        This is what one should expect in response:

        .. sourcecode:: http

            HTTP/1.1 201 Created
            Location: http://localhost/api/0.1/tenders/64e93250be76435397e8c992ed4214d1
            Content-Type: application/json

            {
                "data": {
                    "id": "64e93250be76435397e8c992ed4214d1",
                    "tenderID": "UA-64e93250be76435397e8c992ed4214d1",
                    "dateModified": "2014-10-27T08:06:58.158Z",
                    "procuringEntity": {
                        "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": 500,
                        "currency": "UAH",
                        "valueAddedTaxIncluded": true
                    },
                    "itemsToBeProcured": [
                        {
                            "description": "футляри до державних нагород",
                            "primaryClassification": {
                                "scheme": "CPV",
                                "id": "44617100-9",
                                "description": "Cartons"
                            },
                            "additionalClassification": [
                                {
                                    "scheme": "ДКПП",
                                    "id": "17.21.1",
                                    "description": "папір і картон гофровані, паперова й картонна тара"
                                }
                            ],
                            "unitOfMeasure": "item",
                            "quantity": 5
                        }
                    ],
                    "enquiryPeriod": {
                        "endDate": "2014-10-31T00:00:00"
                    },
                    "tenderPeriod": {
                        "startDate": "2014-11-03T00:00:00",
                        "endDate": "2014-11-06T10:00:00"
                    },
                    "awardPeriod": {
                        "endDate": "2014-11-13T00:00:00"
                    },
                    "deliveryDate": {
                        "endDate": "2014-11-20T00:00:00"
                    },
                    "minimalStep": {
                        "amount": 35,
                        "currency": "UAH"
                    }
                }
            }

        """
        tender_id = generate_id()
        tender = self.request.validated['tender']
        tender.id = tender_id
        tender.tenderID = generate_tender_id(get_now(), self.db,
                                             self.server_id)
        if hasattr(tender, "initialize"):
            tender.initialize()
        set_ownership(tender, self.request)
        self.request.validated['tender'] = tender
        self.request.validated['tender_src'] = {}
        if save_tender(self.request):
            self.LOGGER.info(
                'Created tender {} ({})'.format(tender_id, tender.tenderID),
                extra=context_unpack(self.request,
                                     {'MESSAGE_ID': 'tender_create'}, {
                                         'tender_id': tender_id,
                                         'tenderID': tender.tenderID
                                     }))
            self.request.response.status = 201
            self.request.response.headers['Location'] = self.request.route_url(
                'Tender', tender_id=tender_id)
            return {
                'data': tender.serialize(tender.status),
                'access': {
                    'token': tender.owner_token
                }
            }
def check_status(request):
    auction = request.validated['auction']
    now = get_now()
    for complaint in auction.complaints:
        check_complaint_status(request, complaint, now)
    for award in auction.awards:
        for complaint in award.complaints:
            check_complaint_status(request, complaint, now)
    if auction.status == 'active.enquiries' and not auction.tenderPeriod.startDate and auction.enquiryPeriod.endDate.astimezone(
            TZ) <= now:
        LOGGER.info('Switched auction {} to {}'.format(auction.id,
                                                       'active.tendering'),
                    extra=context_unpack(
                        request,
                        {'MESSAGE_ID': 'switched_auction_active.tendering'}))
        auction.status = 'active.tendering'
        return
    elif auction.status == 'active.enquiries' and auction.tenderPeriod.startDate and auction.tenderPeriod.startDate.astimezone(
            TZ) <= now:
        LOGGER.info('Switched auction {} to {}'.format(auction.id,
                                                       'active.tendering'),
                    extra=context_unpack(
                        request,
                        {'MESSAGE_ID': 'switched_auction_active.tendering'}))
        auction.status = 'active.tendering'
        return
    elif not auction.lots and auction.status == 'active.tendering' and auction.tenderPeriod.endDate <= now:
        LOGGER.info('Switched auction {} to {}'.format(auction['id'],
                                                       'active.auction'),
                    extra=context_unpack(
                        request,
                        {'MESSAGE_ID': 'switched_auction_active.auction'}))
        auction.status = 'active.auction'
        remove_draft_bids(request)
        check_bids(request)
        if auction.numberOfBids < 2 and auction.auctionPeriod:
            auction.auctionPeriod.startDate = None
        return
    elif auction.lots and auction.status == 'active.tendering' and auction.tenderPeriod.endDate <= now:
        LOGGER.info('Switched auction {} to {}'.format(auction['id'],
                                                       'active.auction'),
                    extra=context_unpack(
                        request,
                        {'MESSAGE_ID': 'switched_auction_active.auction'}))
        auction.status = 'active.auction'
        remove_draft_bids(request)
        check_bids(request)
        [
            setattr(i.auctionPeriod, 'startDate', None) for i in auction.lots
            if i.numberOfBids < 2 and i.auctionPeriod
        ]
        return
    elif not auction.lots and auction.status == 'active.awarded':
        standStillEnds = [
            a.complaintPeriod.endDate.astimezone(TZ) for a in auction.awards
            if a.complaintPeriod.endDate
        ]
        if not standStillEnds:
            return
        standStillEnd = max(standStillEnds)
        if standStillEnd <= now:
            check_auction_status(request)
    elif auction.lots and auction.status in [
            'active.qualification', 'active.awarded'
    ]:
        if any([
                i['status'] in auction.block_complaint_status
                and i.relatedLot is None for i in auction.complaints
        ]):
            return
        for lot in auction.lots:
            if lot['status'] != 'active':
                continue
            lot_awards = [i for i in auction.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_auction_status(request)
                return
Пример #54
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 add_next_award(request):
    auction = request.validated['auction']
    now = get_now()
    if not auction.awardPeriod:
        auction.awardPeriod = type(auction).awardPeriod({})
    if not auction.awardPeriod.startDate:
        auction.awardPeriod.startDate = now
    if auction.lots:
        statuses = set()
        for lot in auction.lots:
            if lot.status != 'active':
                continue
            lot_awards = [i for i in auction.awards if i.lotID == lot.id]
            if lot_awards and lot_awards[-1].status in ['pending', 'active']:
                statuses.add(
                    lot_awards[-1].status if lot_awards else 'unsuccessful')
                continue
            lot_items = [i.id for i in auction.items if i.relatedLot == lot.id]
            features = [
                i for i in (auction.features or [])
                if i.featureOf == 'tenderer'
                or i.featureOf == 'lot' and i.relatedItem == lot.id
                or i.featureOf == 'item' and i.relatedItem in lot_items
            ]
            codes = [i.code for i in features]
            bids = [{
                'id':
                bid.id,
                'value': [i for i in bid.lotValues
                          if lot.id == i.relatedLot][0].value,
                'tenderers':
                bid.tenderers,
                'parameters': [i for i in bid.parameters if i.code in codes],
                'date': [i for i in bid.lotValues
                         if lot.id == i.relatedLot][0].date
            } for bid in auction.bids
                    if lot.id in [i.relatedLot for i in bid.lotValues]]
            if not bids:
                lot.status = 'unsuccessful'
                statuses.add('unsuccessful')
                continue
            unsuccessful_awards = [
                i.bid_id for i in lot_awards if i.status == 'unsuccessful'
            ]
            bids = chef(bids, features, unsuccessful_awards, True)
            if bids:
                bid = bids[0]
                award = type(auction).awards.model_class({
                    'bid_id':
                    bid['id'],
                    'lotID':
                    lot.id,
                    'status':
                    'pending',
                    'value':
                    bid['value'],
                    'date':
                    get_now(),
                    'suppliers':
                    bid['tenderers'],
                    'complaintPeriod': {
                        'startDate': now.isoformat()
                    }
                })
                auction.awards.append(award)
                request.response.headers['Location'] = request.route_url(
                    '{}:Auction Awards'.format(auction.procurementMethodType),
                    auction_id=auction.id,
                    award_id=award['id'])
                statuses.add('pending')
            else:
                statuses.add('unsuccessful')
        if statuses.difference(set(['unsuccessful', 'active'])):
            auction.awardPeriod.endDate = None
            auction.status = 'active.qualification'
        else:
            auction.awardPeriod.endDate = now
            auction.status = 'active.awarded'
    else:
        if not auction.awards or auction.awards[-1].status not in [
                'pending', 'active'
        ]:
            unsuccessful_awards = [
                i.bid_id for i in auction.awards if i.status == 'unsuccessful'
            ]
            bids = chef(auction.bids, auction.features or [],
                        unsuccessful_awards, True)
            if bids:
                bid = bids[0].serialize()
                award = type(auction).awards.model_class({
                    'bid_id':
                    bid['id'],
                    'status':
                    'pending',
                    'date':
                    get_now(),
                    'value':
                    bid['value'],
                    'suppliers':
                    bid['tenderers'],
                    'complaintPeriod': {
                        'startDate': get_now().isoformat()
                    }
                })
                auction.awards.append(award)
                request.response.headers['Location'] = request.route_url(
                    '{}:Auction Awards'.format(auction.procurementMethodType),
                    auction_id=auction.id,
                    award_id=award['id'])
        if auction.awards[-1].status == 'pending':
            auction.awardPeriod.endDate = None
            auction.status = 'active.qualification'
        else:
            auction.awardPeriod.endDate = now
            auction.status = 'active.awarded'
Пример #56
0
    def test_patch_tender_contract(self):
        response = self.app.get('/tenders/{}/contracts'.format(self.tender_id))
        contract = response.json['data'][0]

        fake_contractID = "myselfID"
        fake_items_data = [{"description": "New Description"}]
        fake_suppliers_data = [{"name": "New Name"}]

        response = self.app.patch_json(
            '/tenders/{}/contracts/{}'.format(self.tender_id, contract['id']),
            {
                "data": {
                    "contractID": fake_contractID,
                    "items": fake_items_data,
                    "suppliers": fake_suppliers_data
                }
            })

        response = self.app.get('/tenders/{}/contracts/{}'.format(
            self.tender_id, contract['id']))
        self.assertNotEqual(fake_contractID,
                            response.json['data']['contractID'])
        self.assertNotEqual(fake_items_data, response.json['data']['items'])
        self.assertNotEqual(fake_suppliers_data,
                            response.json['data']['suppliers'])

        response = self.app.patch_json('/tenders/{}/contracts/{}'.format(
            self.tender_id,
            contract['id']), {"data": {
                "value": {
                    "currency": "USD"
                }
            }},
                                       status=403)
        self.assertEqual(response.status, '403 Forbidden')
        self.assertEqual(response.json['errors'][0]["description"],
                         "Can\'t update currency for contract value")

        response = self.app.patch_json(
            '/tenders/{}/contracts/{}'.format(self.tender_id, contract['id']),
            {"data": {
                "value": {
                    "valueAddedTaxIncluded": False
                }
            }},
            status=403)
        self.assertEqual(response.status, '403 Forbidden')
        self.assertEqual(
            response.json['errors'][0]["description"],
            "Can\'t update valueAddedTaxIncluded for contract value")

        response = self.app.patch_json('/tenders/{}/contracts/{}'.format(
            self.tender_id, contract['id']),
                                       {"data": {
                                           "value": {
                                               "amount": 501
                                           }
                                       }},
                                       status=403)
        self.assertEqual(response.status, '403 Forbidden')
        self.assertEqual(
            response.json['errors'][0]["description"],
            "Value amount should be less or equal to awarded amount (500.0)")

        response = self.app.patch_json(
            '/tenders/{}/contracts/{}'.format(self.tender_id, contract['id']),
            {"data": {
                "value": {
                    "amount": 238
                }
            }})
        self.assertEqual(response.status, '200 OK')
        self.assertEqual(response.json['data']['value']['amount'], 238)

        response = self.app.patch_json('/tenders/{}/contracts/{}'.format(
            self.tender_id, contract['id']), {"data": {
                "status": "active"
            }},
                                       status=403)
        self.assertEqual(response.status, '403 Forbidden')
        self.assertEqual(response.content_type, 'application/json')
        self.assertIn("Can't sign contract before stand-still period end (",
                      response.json['errors'][0]["description"])

        self.set_status('complete', {'status': 'active.awarded'})

        response = self.app.post_json(
            '/tenders/{}/awards/{}/complaints'.format(self.tender_id,
                                                      self.award_id),
            {
                'data': {
                    'title': 'complaint title',
                    'description': 'complaint description',
                    'author': self.supplier_info
                }
            })
        self.assertEqual(response.status, '201 Created')
        complaint = response.json['data']
        owner_token = response.json['access']['token']

        response = self.app.patch_json(
            '/tenders/{}/awards/{}/complaints/{}?acc_token={}'.format(
                self.tender_id, self.award_id, complaint['id'], owner_token),
            {"data": {
                "status": "pending"
            }})
        self.assertEqual(response.status, '200 OK')

        tender = self.db.get(self.tender_id)
        for i in tender.get('awards', []):
            i['complaintPeriod']['endDate'] = i['complaintPeriod']['startDate']
        self.db.save(tender)

        response = self.app.patch_json(
            '/tenders/{}/contracts/{}'.format(self.tender_id, contract['id']),
            {"data": {
                "dateSigned": i['complaintPeriod']['endDate']
            }},
            status=422)
        self.assertEqual(response.status, '422 Unprocessable Entity')
        self.assertEqual(response.json['errors'], [{
            u'description': [
                u'Contract signature date should be after award complaint period end date ({})'
                .format(i['complaintPeriod']['endDate'])
            ],
            u'location':
            u'body',
            u'name':
            u'dateSigned'
        }])

        one_hour_in_furure = (get_now() + timedelta(hours=1)).isoformat()
        response = self.app.patch_json('/tenders/{}/contracts/{}'.format(
            self.tender_id,
            contract['id']), {"data": {
                "dateSigned": one_hour_in_furure
            }},
                                       status=422)
        self.assertEqual(response.status, '422 Unprocessable Entity')
        self.assertEqual(response.json['errors'], [{
            u'description':
            [u"Contract signature date can't be in the future"],
            u'location':
            u'body',
            u'name':
            u'dateSigned'
        }])

        custom_signature_date = get_now().isoformat()
        response = self.app.patch_json(
            '/tenders/{}/contracts/{}'.format(self.tender_id, contract['id']),
            {"data": {
                "dateSigned": custom_signature_date
            }})
        self.assertEqual(response.status, '200 OK')

        response = self.app.patch_json('/tenders/{}/contracts/{}'.format(
            self.tender_id, contract['id']), {"data": {
                "status": "active"
            }},
                                       status=403)
        self.assertEqual(response.status, '403 Forbidden')
        self.assertEqual(response.content_type, 'application/json')
        self.assertEqual(
            response.json['errors'][0]["description"],
            "Can't sign contract before reviewing all complaints")

        response = self.app.patch_json(
            '/tenders/{}/awards/{}/complaints/{}?acc_token={}'.format(
                self.tender_id, self.award_id, complaint['id'], owner_token),
            {"data": {
                "status": "stopping",
                "cancellationReason": "reason"
            }})
        self.assertEqual(response.status, '200 OK')
        self.assertEqual(response.content_type, 'application/json')
        self.assertEqual(response.json['data']["status"], "stopping")

        authorization = self.app.authorization
        self.app.authorization = ('Basic', ('reviewer', ''))
        response = self.app.patch_json(
            '/tenders/{}/awards/{}/complaints/{}'.format(
                self.tender_id, self.award_id, complaint['id']),
            {'data': {
                'status': 'stopped'
            }})
        self.assertEqual(response.status, '200 OK')
        self.assertEqual(response.json['data']["status"], "stopped")

        self.app.authorization = authorization
        response = self.app.patch_json(
            '/tenders/{}/contracts/{}'.format(self.tender_id, contract['id']),
            {"data": {
                "status": "active"
            }})
        self.assertEqual(response.status, '200 OK')
        self.assertEqual(response.content_type, 'application/json')
        self.assertEqual(response.json['data']["status"], "active")

        response = self.app.patch_json('/tenders/{}/contracts/{}'.format(
            self.tender_id, contract['id']), {
                "data": {
                    "value": {
                        "amount": 232
                    },
                    "contractID": "myselfID",
                    "title": "New Title",
                    "items": [{
                        "description": "New Description"
                    }],
                    "suppliers": [{
                        "name": "New Name"
                    }]
                }
            },
                                       status=403)
        self.assertEqual(response.status, '403 Forbidden')
        self.assertEqual(
            response.json['errors'][0]["description"],
            "Can't update contract in current (complete) tender status")

        response = self.app.patch_json('/tenders/{}/contracts/{}'.format(
            self.tender_id, contract['id']), {"data": {
                "status": "active"
            }},
                                       status=403)
        self.assertEqual(response.status, '403 Forbidden')
        self.assertEqual(response.content_type, 'application/json')
        self.assertEqual(
            response.json['errors'][0]["description"],
            "Can't update contract in current (complete) tender status")

        response = self.app.patch_json('/tenders/{}/contracts/{}'.format(
            self.tender_id, contract['id']), {"data": {
                "status": "pending"
            }},
                                       status=403)
        self.assertEqual(response.status, '403 Forbidden')
        self.assertEqual(response.content_type, 'application/json')
        self.assertEqual(
            response.json['errors'][0]["description"],
            "Can't update contract in current (complete) tender status")

        response = self.app.patch_json('/tenders/{}/contracts/some_id'.format(
            self.tender_id), {"data": {
                "status": "active"
            }},
                                       status=404)
        self.assertEqual(response.status, '404 Not Found')
        self.assertEqual(response.content_type, 'application/json')
        self.assertEqual(response.json['status'], 'error')
        self.assertEqual(response.json['errors'], [{
            u'description': u'Not Found',
            u'location': u'url',
            u'name': u'contract_id'
        }])

        response = self.app.patch_json('/tenders/some_id/contracts/some_id',
                                       {"data": {
                                           "status": "active"
                                       }},
                                       status=404)
        self.assertEqual(response.status, '404 Not Found')
        self.assertEqual(response.content_type, 'application/json')
        self.assertEqual(response.json['status'], 'error')
        self.assertEqual(response.json['errors'], [{
            u'description': u'Not Found',
            u'location': u'url',
            u'name': u'tender_id'
        }])

        response = self.app.get('/tenders/{}/contracts/{}'.format(
            self.tender_id, contract['id']))
        self.assertEqual(response.status, '200 OK')
        self.assertEqual(response.content_type, 'application/json')
        self.assertEqual(response.json['data']["status"], "active")
        self.assertEqual(response.json['data']["value"]['amount'], 238)
Пример #57
0
def check_status(request):
    tender = request.validated['tender']
    now = get_now()
    for complaint in tender.complaints:
        check_complaint_status(request, complaint, now)
    for award in tender.awards:
        for complaint in award.complaints:
            check_complaint_status(request, complaint, now)
    if tender.status == 'active.enquiries' and not tender.tenderPeriod.startDate and tender.enquiryPeriod.endDate.astimezone(
            TZ) <= now:
        LOGGER.info('Switched tender {} to {}'.format(tender.id,
                                                      'active.tendering'),
                    extra=context_unpack(
                        request,
                        {'MESSAGE_ID': 'switched_tender_active.tendering'}))
        tender.status = 'active.tendering'
        return
    elif tender.status == 'active.enquiries' and tender.tenderPeriod.startDate and tender.tenderPeriod.startDate.astimezone(
            TZ) <= now:
        LOGGER.info('Switched tender {} to {}'.format(tender.id,
                                                      'active.tendering'),
                    extra=context_unpack(
                        request,
                        {'MESSAGE_ID': 'switched_tender_active.tendering'}))
        tender.status = 'active.tendering'
        return
    elif not tender.lots and tender.status == 'active.tendering' and tender.tenderPeriod.endDate <= now:
        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:
        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 ['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']
                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 ['claim', 'answered', 'pending']
                    and i.relatedLot == lot.id for i in tender.complaints
                ])
                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_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)
Пример #58
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
 def validate_dateSigned(self, data, value):
     if value and value > get_now():
         raise ValidationError(
             u"Contract signature date can't be in the future")
Пример #60
0
def check_status(request):
    tender = request.validated["tender"]
    now = get_now()
    configurator = request.content_configurator

    check_complaint_statuses_at_complaint_period_end(tender, now)
    check_cancellation_status(request, CancelTenderLot)

    active_lots = [lot.id for lot in tender.lots
                   if lot.status == "active"] if tender.lots else [None]
    for award in tender.awards:
        if award.status == "active" and not any(
            [i.awardID == award.id for i in tender.contracts]):
            add_contract(request, award, now)
            add_next_award(
                request,
                reverse=configurator.reverse_awarding_criteria,
                awarding_criteria_key=configurator.awarding_criteria_key,
            )

    if block_tender(request):
        return

    if (tender.status == "active.tendering"
            and tender.tenderPeriod.endDate <= now
            and not has_unanswered_complaints(tender)
            and not has_unanswered_questions(tender)):
        for complaint in tender.complaints:
            check_complaint_status(request, complaint)
        LOGGER.info(
            "Switched tender {} to {}".format(tender["id"],
                                              "active.pre-qualification"),
            extra=context_unpack(
                request,
                {"MESSAGE_ID": "switched_tender_active.pre-qualification"}),
        )
        tender.status = "active.pre-qualification"
        tender.qualificationPeriod = type(tender).qualificationPeriod(
            {"startDate": now})
        remove_draft_bids(request)
        check_initial_bids_count(request)
        prepare_qualifications(request)

    elif (tender.status == "active.pre-qualification.stand-still"
          and tender.qualificationPeriod
          and tender.qualificationPeriod.endDate <= now and not any([
              i.status in tender.block_complaint_status
              for q in tender.qualifications
              for i in q.complaints if q.lotID in active_lots
          ])):
        LOGGER.info(
            "Switched tender {} to {}".format(tender["id"], "active.auction"),
            extra=context_unpack(
                request, {"MESSAGE_ID": "switched_tender_active.auction"}),
        )
        tender.status = "active.auction"
        check_initial_bids_count(request)

    elif not tender.lots and tender.status == "active.awarded":
        standStillEnds = [
            a.complaintPeriod.endDate.astimezone(TZ) for a in tender.awards
            if a.complaintPeriod and a.complaintPeriod.endDate
        ]
        if standStillEnds:
            standStillEnd = max(standStillEnds)
            if standStillEnd <= now:
                check_tender_status(request)
    elif tender.lots and tender.status in [
            "active.qualification", "active.awarded"
    ]:
        for lot in tender.lots:
            if lot["status"] != "active":
                continue
            lot_awards = [i for i in tender.awards if i.lotID == lot.id]
            standStillEnds = [
                a.complaintPeriod.endDate.astimezone(TZ) for a in lot_awards
                if a.complaintPeriod and a.complaintPeriod.endDate
            ]
            if not standStillEnds:
                continue
            standStillEnd = max(standStillEnds)
            if standStillEnd <= now:
                check_tender_status(request)
                break