class CompetitiveDialogueStage2UAComplaintResource(TenderUaComplaintResource): @json_view( content_type="application/json", validators=( validate_complaint_data_stage2, validate_complaint_operation_not_in_active_tendering, validate_add_complaint_with_tender_cancellation_in_pending, validate_add_complaint_with_lot_cancellation_in_pending("complaint") ), permission="create_complaint", ) def collection_post(self): return super(CompetitiveDialogueStage2UAComplaintResource, self).collection_post() @json_view( content_type="application/json", validators=( validate_patch_complaint_data_stage2, validate_complaint_update_with_cancellation_lot_pending, validate_complaint_operation_not_in_active_tendering, validate_update_complaint_not_in_allowed_complaint_status, ), permission="edit_complaint", ) def patch(self): return super(CompetitiveDialogueStage2UAComplaintResource, self).patch()
class TenderUaAwardComplaintResource(BaseTenderAwardComplaintResource): @json_view( content_type="application/json", permission="create_award_complaint", validators=( validate_complaint_data, validate_only_complaint_allowed, validate_award_complaint_operation_not_in_allowed_status, validate_award_complaint_add_only_for_active_lots, validate_add_complaint_not_in_complaint_period, validate_add_complaint_with_tender_cancellation_in_pending, validate_add_complaint_with_lot_cancellation_in_pending("award"), ), ) def collection_post(self): """Post a complaint for award """ return super(TenderUaAwardComplaintResource, self).collection_post()
class BaseTenderComplaintResource(ComplaintBotPatchMixin, ComplaintAdminPatchMixin, APIResource): patch_check_tender_excluded_statuses = ( "draft", "claim", "answered", "pending", "accepted", "satisfied", "stopping", ) patch_check_tender_statuses = ("active.qualification", "active.awarded") @staticmethod def validate_submit_claim_time_method(request): raise NotImplementedError @staticmethod def validate_update_claim_time_method(request): raise NotImplementedError def pre_create(self): tender = self.request.validated["tender"] old_rules = get_first_revision_date(tender) < RELEASE_2020_04_19 complaint = self.request.validated["complaint"] complaint.date = get_now() if complaint.status == "claim" and complaint.type == "claim": self.validate_submit_claim_time_method(self.request) complaint.dateSubmitted = get_now() elif old_rules and complaint.status == "pending": validate_submit_complaint_time(self.request) complaint.dateSubmitted = get_now() complaint.type = "complaint" else: complaint.status = "draft" return complaint @json_view(permission="view_tender") def collection_get(self): """List complaints """ return {"data": [i.serialize("view") for i in self.context.complaints]} @json_view(permission="view_tender") def get(self): """Retrieving the complaint """ return {"data": self.context.serialize("view")} @json_view( content_type="application/json", validators=( validate_complaint_data, validate_complaint_operation_not_in_active_tendering, validate_add_complaint_with_tender_cancellation_in_pending, validate_add_complaint_with_lot_cancellation_in_pending( "complaint"), ), permission="create_complaint", ) def collection_post(self): """Post a complaint for award """ tender = self.request.validated["tender"] complaint = self.pre_create() complaint.complaintID = "{}.{}{}".format( tender.tenderID, self.server_id, calculate_total_complaints(tender) + 1) access = 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_complaint_create"}, {"complaint_id": complaint.id}), ) self.request.response.status = 201 self.request.response.headers["Location"] = self.request.route_url( "{}:Tender Complaints".format(tender.procurementMethodType), tender_id=tender.id, complaint_id=complaint.id, ) return { "data": complaint.serialize(tender.status), "access": access } @json_view( content_type="application/json", permission="edit_complaint", validators=( validate_patch_complaint_data, validate_complaint_update_with_cancellation_lot_pending, validate_complaint_operation_not_in_active_tendering, validate_update_complaint_not_in_allowed_complaint_status, validate_operation_with_lot_cancellation_in_pending("complaint"), ), ) def patch(self): role_method_name = "patch_as_{role}".format( role=self.request.authenticated_role.lower()) try: role_method = getattr(self, role_method_name) except AttributeError: raise_operation_error( self.request, "Can't update complaint as {}".format( self.request.authenticated_role)) else: role_method(self.request.validated["data"]) if self.context.tendererAction and not self.context.tendererActionDate: self.context.tendererActionDate = get_now() if (self.patch_check_tender_excluded_statuses != "__all__" and self.context.status not in self.patch_check_tender_excluded_statuses and self.request.validated["tender"].status in self.patch_check_tender_statuses): check_tender_status(self.request) if save_tender(self.request): self.LOGGER.info( "Updated tender award complaint {}".format(self.context.id), extra=context_unpack( self.request, {"MESSAGE_ID": "tender_award_complaint_patch"}), ) return {"data": self.context.serialize("view")} def patch_as_complaint_owner(self, data): context = self.context status = self.context.status new_status = data.get("status", status) tender = self.request.validated["tender"] apply_rules_2020_04_19 = get_first_revision_date( tender) > RELEASE_2020_04_19 if (new_status == "cancelled" and status in ["draft", "claim", "answered"] and context.type == "claim") or (new_status == "cancelled" and status == "draft" and context.type == "complaint" and not apply_rules_2020_04_19): apply_patch(self.request, save=False, src=context.serialize()) context.dateCanceled = get_now() elif (apply_rules_2020_04_19 and status == "draft" and context.type == "complaint" and new_status == "mistaken"): context.rejectReason = "cancelledByComplainant" apply_patch(self.request, save=False, src=context.serialize()) elif (status in ["pending", "accepted"] and new_status == "stopping" and not apply_rules_2020_04_19): apply_patch(self.request, save=False, src=context.serialize()) context.dateCanceled = get_now() elif (tender.status == "active.tendering" and status == "draft" and new_status == status): apply_patch(self.request, save=False, src=context.serialize()) elif (tender.status == "active.tendering" and context.type == "claim" and status == "draft" and new_status == "claim"): self.validate_submit_claim_time_method(self.request) apply_patch(self.request, save=False, src=context.serialize()) context.dateSubmitted = get_now() elif (tender.status == "active.tendering" and status in ["draft", "claim"] and new_status == "pending" and not apply_rules_2020_04_19): validate_submit_complaint_time(self.request) validate_complaint_type_change(self.request) apply_patch(self.request, save=False, src=context.serialize()) context.type = "complaint" context.dateSubmitted = get_now() elif status == "answered" and new_status == status: apply_patch(self.request, save=False, src=context.serialize()) elif (status == "answered" and data.get("satisfied", context.satisfied) is True and new_status == "resolved"): apply_patch(self.request, save=False, src=context.serialize()) elif (status == "answered" and data.get("satisfied", context.satisfied) is False and new_status == "pending"): validate_submit_complaint_time(self.request) validate_complaint_type_change(self.request) apply_patch(self.request, save=False, src=context.serialize()) context.type = "complaint" context.dateEscalated = get_now() else: raise_operation_error( self.request, "Can't update complaint from {} to {} status".format( status, new_status)) def patch_as_tender_owner(self, data): context = self.context status = context.status new_status = data.get("status", status) if status == "claim" and new_status == status: self.validate_update_claim_time_method(self.request) apply_patch(self.request, save=False, src=context.serialize()) elif status == "satisfied" and new_status == status: apply_patch(self.request, save=False, src=context.serialize()) elif (status == "claim" and data.get("resolution", context.resolution) and data.get("resolutionType", context.resolutionType) and new_status == "answered"): self.validate_update_claim_time_method(self.request) if len(data.get("resolution", context.resolution)) < 20: raise_operation_error( self.request, "Can't update complaint: resolution too short") apply_patch(self.request, save=False, src=context.serialize()) context.dateAnswered = get_now() elif status in ["pending", "accepted"]: apply_patch(self.request, save=False, src=context.serialize()) elif (status == "satisfied" and data.get("tendererAction", context.tendererAction) and new_status == "resolved"): apply_patch(self.request, save=False, src=context.serialize()) else: raise_operation_error( self.request, "Can't update complaint from {} to {} status".format( status, new_status)) def patch_as_abovethresholdreviewers(self, data): context = self.context status = context.status new_status = data.get("status", status) tender = self.request.validated["tender"] old_rules = get_first_revision_date(tender) < RELEASE_2020_04_19 if (status in ["pending", "accepted", "stopping"] and new_status == status): apply_patch(self.request, save=False, src=context.serialize()) elif (status in ["pending", "stopping"] and ((old_rules and new_status in ["invalid", "mistaken"]) or (new_status == "invalid"))): apply_patch(self.request, save=False, src=context.serialize()) context.dateDecision = get_now() context.acceptance = False elif status == "pending" and new_status == "accepted": apply_patch(self.request, save=False, src=context.serialize()) context.dateAccepted = get_now() context.acceptance = True elif (status in ["accepted", "stopping"] and new_status in ["declined", "satisfied"]): apply_patch(self.request, save=False, src=context.serialize()) context.dateDecision = get_now() elif ((old_rules and status in ["pending", "accepted", "stopping"]) or (not old_rules and status == "accepted") and new_status == "stopped"): apply_patch(self.request, save=False, src=context.serialize()) context.dateDecision = get_now() context.dateCanceled = context.dateCanceled or get_now() else: raise_operation_error( self.request, "Can't update complaint from {} to {} status".format( status, new_status))
class TenderNegotiationAwardComplaintResource(BaseTenderAwardComplaintResource ): patch_check_tender_excluded_statuses = "__all__" def complaints_len(self, tender): return sum([len(i.complaints) for i in tender.awards]) def pre_create(self): tender = self.request.validated["tender"] old_rules = get_first_revision_date(tender) < RELEASE_2020_04_19 complaint = self.request.validated["complaint"] complaint.date = get_now() if old_rules and complaint.status == "pending": complaint.dateSubmitted = get_now() else: complaint.status = "draft" return complaint @json_view( content_type="application/json", permission="create_award_complaint", validators=( validate_complaint_data, validate_award_complaint_operation_not_in_active, validate_add_complaint_not_in_complaint_period, validate_add_complaint_with_tender_cancellation_in_pending, validate_add_complaint_with_lot_cancellation_in_pending("award"), ), ) def collection_post(self): """Post a complaint for award """ return super(TenderNegotiationAwardComplaintResource, self).collection_post() @json_view( content_type="application/json", permission="edit_complaint", validators=( validate_patch_complaint_data, validate_award_complaint_operation_not_in_active, validate_update_complaint_not_in_allowed_complaint_status, ), ) def patch(self): return super(TenderNegotiationAwardComplaintResource, self).patch() def patch_as_complaint_owner(self, data): complaint_period = self.request.validated["award"].complaintPeriod is_complaint_period = ( complaint_period.startDate <= get_now() <= complaint_period.endDate if complaint_period.endDate else complaint_period.startDate <= get_now()) status = self.context.status new_status = data.get("status", status) tender = self.request.validated["tender"] new_rules = get_first_revision_date(tender) > RELEASE_2020_04_19 if status in ["draft", "claim", "answered" ] and new_status == "cancelled": # claim ? There is no way to post claim, so this must be a backward-compatibility option apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateCanceled = get_now() elif status in ["pending", "accepted"] and new_status == "stopping": apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateCanceled = get_now() elif status == "draft": if not is_complaint_period: raise_operation_error( self.request, "Can't update draft complaint not in complaintPeriod") if new_status == status: apply_patch(self.request, save=False, src=self.context.serialize()) elif (new_rules and self.context.type == "complaint" and new_status == "mistaken"): self.context.rejectReason = "cancelledByComplainant" apply_patch(self.request, save=False, src=self.context.serialize()) elif new_status == "pending" and not new_rules: apply_patch(self.request, save=False, src=self.context.serialize()) self.context.type = "complaint" self.context.dateSubmitted = get_now() else: raise_operation_error( self.request, "Can't update draft complaint to {} status".format( new_status)) else: raise_operation_error( self.request, "Can't update complaint from {} to {}".format( status, new_status)) def patch_as_tender_owner(self, data): status = self.context.status new_status = data.get("status", status) if status in ["pending", "accepted"]: apply_patch(self.request, save=False, src=self.context.serialize()) elif status == "satisfied" and new_status == status: apply_patch(self.request, save=False, src=self.context.serialize()) elif status == "satisfied" and new_status == "resolved": if not data.get("tendererAction", self.context.tendererAction): raise_operation_error( self.request, "Can't update complaint: tendererAction required") apply_patch(self.request, save=False, src=self.context.serialize()) else: raise_operation_error( self.request, "Can't update complaint from {} to {}".format( status, new_status)) def patch_as_abovethresholdreviewers(self, data): status = self.context.status new_status = data.get("status", status) tender = self.request.validated["tender"] old_rules = get_first_revision_date(tender) < RELEASE_2020_04_19 if status in ["pending", "accepted", "stopping" ] and new_status == status: apply_patch(self.request, save=False, src=self.context.serialize()) elif (status in ["pending", "stopping"] and ((old_rules and new_status in ["invalid", "mistaken"]) or (new_status == "invalid"))): apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateDecision = get_now() self.context.acceptance = False elif status == "pending" and new_status == "accepted": apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateAccepted = get_now() self.context.acceptance = True elif status in ["accepted", "stopping" ] and new_status in ["declined", "satisfied"]: apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateDecision = get_now() elif ((old_rules and status in ["pending", "accepted", "stopping"]) or (not old_rules and status in ["accepted", "stopping"]) and new_status == "stopped"): apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateDecision = get_now() self.context.dateCanceled = self.context.dateCanceled or get_now() else: raise_operation_error( self.request, "Can't update complaint from {} to {}".format( status, new_status))
class BaseTenderAwardComplaintResource(BaseTenderComplaintResource): patch_check_tender_excluded_statuses = ( "draft", "claim", "answered", "pending", "accepted", "satisfied", "stopping", ) @staticmethod def check_tender_status_method(request): return check_tender_status(request) def validate_posting_complaint(self): tender = self.request.validated["tender"] rules_2020_04_19 = get_first_revision_date(tender, get_now()) > RELEASE_2020_04_19 context_award = self.request.validated["award"] if not rules_2020_04_19: if not any( award.status == "active" for award in tender.awards if award.lotID == context_award.lotID ): raise_operation_error(self.request, "Complaint submission is allowed only after award activation.") else: if context_award.status not in ("active", "unsuccessful"): raise_operation_error( self.request, "Complaint submission is allowed only after award activation or unsuccessful award.", ) def validate_posting_claim(self): complaint = self.request.validated["complaint"] award = self.request.validated["award"] if award.status == "unsuccessful" and award.bid_id != complaint.bid_id: raise_operation_error(self.request, "Can add claim only on unsuccessful award of your bid") if award.status == "pending": raise_operation_error(self.request, "Claim submission is not allowed on pending award") def pre_create(self): tender = self.request.validated["tender"] rules_2020_04_19 = get_first_revision_date(tender) > RELEASE_2020_04_19 complaint = self.request.validated["complaint"] complaint.date = get_now() complaint.relatedLot = self.context.lotID complaint.bid_id = get_bid_id(self.request) if complaint.status == "claim" and complaint.type == "claim": # claim self.validate_posting_claim() complaint.dateSubmitted = get_now() elif not rules_2020_04_19 and complaint.status == "pending": # complaint self.validate_posting_complaint() complaint.type = "complaint" complaint.dateSubmitted = get_now() else: # draft is neither claim nor complaint yet complaint.status = "draft" return complaint @json_view( content_type="application/json", permission="create_award_complaint", validators=( validate_complaint_data, validate_award_complaint_operation_not_in_allowed_status, validate_award_complaint_add_only_for_active_lots, validate_add_complaint_not_in_complaint_period, validate_add_complaint_with_tender_cancellation_in_pending, validate_add_complaint_with_lot_cancellation_in_pending("award"), ), ) def collection_post(self): """Post a complaint for award """ tender = self.request.validated["tender"] complaint = self.pre_create() complaint.complaintID = "{}.{}{}".format( tender.tenderID, self.server_id, calculate_total_complaints(tender) + 1, ) access = 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 Award Complaints".format(tender.procurementMethodType), tender_id=tender.id, award_id=self.request.validated["award_id"], complaint_id=complaint["id"], ) return {"data": complaint.serialize("view"), "access": access} @json_view( content_type="application/json", permission="edit_complaint", validators=( validate_patch_complaint_data, validate_update_award_with_cancellation_lot_pending, validate_award_complaint_operation_not_in_allowed_status, validate_award_complaint_update_only_for_active_lots, validate_update_complaint_not_in_allowed_complaint_status, ), ) def patch(self): role_method_name = "patch_as_{role}".format(role=self.request.authenticated_role.lower()) try: role_method = getattr(self, role_method_name) except AttributeError: raise_operation_error(self.request, "Can't update complaint as {}".format(self.request.authenticated_role)) else: role_method(self.request.validated["data"]) if self.context.tendererAction and not self.context.tendererActionDate: self.context.tendererActionDate = get_now() if ( self.context.status not in self.patch_check_tender_excluded_statuses and self.request.validated["tender"].status in self.patch_check_tender_statuses ): self.check_tender_status_method(self.request) if save_tender(self.request): self.LOGGER.info( "Updated tender award complaint {}".format(self.context.id), extra=context_unpack(self.request, {"MESSAGE_ID": "tender_award_complaint_patch"}), ) return {"data": self.context.serialize("view")} def patch_as_complaint_owner(self, data): status = self.context.status new_status = data.get("status", status) tender = self.request.validated["tender"] rules_2020_04_19 = get_first_revision_date(tender, get_now()) > RELEASE_2020_04_19 if ( new_status == "cancelled" and status in ["draft", "claim", "answered"] and self.context.type == "claim" ) or ( new_status == "cancelled" and status == "draft" and self.context.type == "complaint" and not rules_2020_04_19 ) or ( new_status == "stopping" and status in ["pending", "accepted"] and not rules_2020_04_19 ): apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateCanceled = get_now() elif status == "draft": self.patch_draft_as_complaint_owner(data) elif status == "answered" and new_status == status: apply_patch(self.request, save=False, src=self.context.serialize()) else: raise_operation_error( self.request, "Can't update complaint from {} to {} status".format(status, new_status) ) def patch_draft_as_complaint_owner(self, data): tender = self.request.validated["tender"] rules_2020_04_19 = get_first_revision_date(tender) > RELEASE_2020_04_19 complaint_period = self.request.validated["award"].complaintPeriod is_complaint_period = ( complaint_period.startDate <= get_now() <= complaint_period.endDate if complaint_period.endDate else complaint_period.startDate <= get_now() ) if not is_complaint_period: raise_operation_error(self.request, "Can't update draft complaint not in complaintPeriod") new_status = data.get("status", self.context.status) if new_status == self.context.status: apply_patch(self.request, save=False, src=self.context.serialize()) elif ( rules_2020_04_19 and self.context.type == "complaint" and new_status == "mistaken" ): self.context.rejectReason = "cancelledByComplainant" apply_patch(self.request, save=False, src=self.context.serialize()) elif self.context.type == "claim" and new_status == "claim": self.validate_posting_claim() apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateSubmitted = get_now() elif new_status == "pending" and not rules_2020_04_19: apply_patch(self.request, save=False, src=self.context.serialize()) self.context.type = "complaint" self.context.dateSubmitted = get_now() else: raise_operation_error(self.request, "Can't update draft complaint into {} status".format(new_status)) def patch_as_tender_owner(self, data): context = self.context status = context.status new_status = data.get("status", status) if status in ["pending", "accepted"] or new_status == status and status in ["claim", "satisfied"]: apply_patch(self.request, save=False, src=context.serialize()) elif status == "claim" and new_status == "answered": if not data.get("resolution", context.resolution) or not data.get("resolutionType", context.resolutionType): raise_operation_error(self.request, "Can't update complaint: resolution and resolutionType required") if len(data.get("resolution", context.resolution)) < 20: raise_operation_error(self.request, "Can't update complaint: resolution too short") apply_patch(self.request, save=False, src=context.serialize()) context.dateAnswered = get_now() elif status == "satisfied" and new_status == "resolved": if not data.get("tendererAction", context.tendererAction): raise_operation_error(self.request, "Can't update complaint: tendererAction required") apply_patch(self.request, save=False, src=context.serialize()) else: raise_operation_error(self.request, "Can't update complaint from {} to {} status".format(status, new_status)) def patch_as_abovethresholdreviewers(self, data): context = self.context status = context.status new_status = data.get("status", status) tender = self.request.validated["tender"] rules_2020_04_19 = get_first_revision_date(tender, get_now()) > RELEASE_2020_04_19 if new_status == status and status in ["pending", "accepted", "stopping"]: apply_patch(self.request, save=False, src=context.serialize()) elif ( status in ["pending", "stopping"] and ( (not rules_2020_04_19 and new_status in ["invalid", "mistaken"]) or (new_status == "invalid") ) ): apply_patch(self.request, save=False, src=context.serialize()) context.dateDecision = get_now() context.acceptance = False elif status == "pending" and new_status == "accepted": apply_patch(self.request, save=False, src=context.serialize()) context.dateAccepted = get_now() context.acceptance = True elif status in ["accepted", "stopping"] and new_status in ["declined", "satisfied"]: apply_patch(self.request, save=False, src=context.serialize()) context.dateDecision = get_now() if new_status == "satisfied": self.on_satisfy_complaint_by_reviewer() elif ( (not rules_2020_04_19 and status in ["pending", "accepted", "stopping"]) or (rules_2020_04_19 and status == "accepted") and new_status == "stopped" ): apply_patch(self.request, save=False, src=context.serialize()) context.dateDecision = get_now() context.dateCanceled = context.dateCanceled or get_now() else: raise_operation_error(self.request, "Can't update complaint from {} to {} status".format(status, new_status)) def on_satisfy_complaint_by_reviewer(self): pass
class TenderEUQualificationComplaintResource(TenderEUAwardComplaintResource): def complaints_len(self, tender): return sum( [len(i.complaints) for i in tender.awards], sum([len(i.complaints) for i in tender.qualifications], len(tender.complaints)), ) @json_view( content_type="application/json", permission="create_qualification_complaint", validators=( validate_complaint_data, validate_add_complaint_not_in_pre_qualification, validate_award_complaint_add_only_for_active_lots, validate_add_complaint_not_in_qualification_period, validate_add_complaint_with_tender_cancellation_in_pending, validate_add_complaint_with_lot_cancellation_in_pending( "qualification"), ), ) def collection_post(self): """Post a complaint """ tender = self.request.validated["tender"] complaint = self.request.validated["complaint"] complaint.relatedLot = self.context.lotID complaint.date = get_now() complaint.bid_id = get_bid_id(self.request) old_rules = get_first_revision_date(tender) < RELEASE_2020_04_19 if complaint.status == "claim": complaint.dateSubmitted = get_now() elif old_rules and complaint.status == "pending": complaint.type = "complaint" complaint.dateSubmitted = get_now() else: complaint.status = "draft" if (self.context.status == "unsuccessful" and complaint.status == "claim" and self.context.bidID != complaint.bid_id): raise_operation_error( self.request, "Can add claim only on unsuccessful qualification of your bid") complaint.complaintID = "{}.{}{}".format( tender.tenderID, self.server_id, self.complaints_len(tender) + 1) access = 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 Qualification Complaints".format( tender.procurementMethodType), tender_id=tender.id, qualification_id=self.request.validated["qualification_id"], complaint_id=complaint["id"], ) return {"data": complaint.serialize("view"), "access": access} @json_view( content_type="application/json", permission="edit_complaint", validators=( validate_patch_complaint_data, validate_qualification_update_with_cancellation_lot_pending, validate_update_complaint_not_in_pre_qualification, validate_update_qualification_complaint_only_for_active_lots, validate_update_complaint_not_in_allowed_complaint_status, ), ) def patch(self): role_method_name = "patch_as_{role}".format( role=self.request.authenticated_role.lower()) try: role_method = getattr(self, role_method_name) except AttributeError: raise_operation_error( self.request, "Can't update complaint as {}".format( self.request.authenticated_role)) else: role_method(self.request.validated["data"]) if self.context.tendererAction and not self.context.tendererActionDate: self.context.tendererActionDate = get_now() if save_tender(self.request): self.LOGGER.info( "Updated tender qualification complaint {}".format( self.context.id), extra=context_unpack( self.request, {"MESSAGE_ID": "tender_qualification_complaint_patch"}), ) return {"data": self.context.serialize("view")} def patch_as_complaint_owner(self, data): data = self.request.validated["data"] status = self.context.status new_status = data.get("status", status) tender = self.request.validated["tender"] is_qualificationPeriod = tender.qualificationPeriod.startDate < get_now( ) and (not tender.qualificationPeriod.endDate or tender.qualificationPeriod.endDate > get_now()) new_rules = get_first_revision_date(tender) > RELEASE_2020_04_19 if (status in ["draft", "claim", "answered"] and new_status == "cancelled"): apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateCanceled = get_now() elif (new_rules and status == "draft" and self.context.type == "complaint" and new_status == "mistaken"): self.context.rejectReason = "cancelledByComplainant" apply_patch(self.request, save=False, src=self.context.serialize()) elif (status in ["pending", "accepted"] and new_status == "stopping"): apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateCanceled = get_now() elif (is_qualificationPeriod and status == "draft" and new_status == status): apply_patch(self.request, save=False, src=self.context.serialize()) elif (is_qualificationPeriod and status == "draft" and new_status == "claim"): if (self.request.validated["qualification"].status == "unsuccessful" and self.request.validated["qualification"].bidID != self.context.bid_id): raise_operation_error( self.request, "Can add claim only on unsuccessful qualification of your bid" ) apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateSubmitted = get_now() elif (is_qualificationPeriod and status == "draft" and new_status == "pending" and not new_rules): apply_patch(self.request, save=False, src=self.context.serialize()) self.context.type = "complaint" self.context.dateSubmitted = get_now() elif (status == "answered" and new_status == status): apply_patch(self.request, save=False, src=self.context.serialize()) else: raise_operation_error( self.request, "Can't update complaint from {} to {} status".format( status, new_status)) def patch_as_tender_owner(self, data): data = self.request.validated["data"] status = self.context.status new_status = data.get("status", status) tender = self.request.validated["tender"] new_rules = get_first_revision_date(tender) > RELEASE_2020_04_19 if self.request.authenticated_role == "tender_owner" and status in [ "pending", "accepted" ]: apply_patch(self.request, save=False, src=self.context.serialize()) elif (status in ["claim", "satisfied"] and new_status == status): apply_patch(self.request, save=False, src=self.context.serialize()) elif (status == "claim" and data.get("resolution", self.context.resolution) and data.get("resolutionType", self.context.resolutionType) and new_status == "answered"): if len(data.get("resolution", self.context.resolution)) < 20: raise_operation_error( self.request, "Can't update complaint: resolution too short") apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateAnswered = get_now() elif (status == "satisfied" and data.get("tendererAction", self.context.tendererAction) and new_status == "resolved"): apply_patch(self.request, save=False, src=self.context.serialize()) else: raise_operation_error( self.request, "Can't update complaint from {} to {} status".format( status, new_status)) def patch_as_abovethresholdreviewers(self, data): context = self.context status = context.status new_status = data.get("status", status) tender = self.request.validated["tender"] new_rules = get_first_revision_date(tender) > RELEASE_2020_04_19 if (status in ["pending", "accepted", "stopping"] and new_status == status): apply_patch(self.request, save=False, src=self.context.serialize()) elif (status in ["pending", "stopping"] and ((not new_rules and new_status in ["invalid", "mistaken"]) or (new_status == "invalid"))): apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateDecision = get_now() self.context.acceptance = False elif (status == "pending" and new_status == "accepted"): apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateAccepted = get_now() self.context.acceptance = True elif (status in ["accepted", "stopping"] and new_status == "declined"): apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateDecision = get_now() elif (status in ["accepted", "stopping"] and new_status == "satisfied"): apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateDecision = get_now() tender.status = "active.pre-qualification" if tender.qualificationPeriod.endDate: tender.qualificationPeriod.endDate = None elif ( ((not new_rules and status in ["pending", "accepted", "stopping"]) or (new_rules and status in ["accepted", "stopping"])) and new_status == "stopped"): apply_patch(self.request, save=False, src=self.context.serialize()) self.context.dateDecision = get_now() self.context.dateCanceled = self.context.dateCanceled or get_now() else: raise_operation_error( self.request, "Can't update complaint from {} to {} status".format( status, new_status))
class TenderEUAwardComplaintResource(BaseTenderAwardComplaintResource): patch_check_tender_statuses = ("active.qualification.stand-still",) def check_tender_status_method(self, request): return check_tender_status_on_active_qualification_stand_still(request) def pre_create(self): tender = self.request.validated["tender"] old_rules = get_first_revision_date(tender) < RELEASE_2020_04_19 complaint = self.request.validated["complaint"] complaint.date = get_now() complaint.relatedLot = self.context.lotID complaint.bid_id = get_bid_id(self.request) if complaint.status == "claim" and complaint.type == "claim": self.validate_posting_claim() complaint.dateSubmitted = get_now() elif old_rules and complaint.status == "pending": complaint.type = "complaint" complaint.dateSubmitted = get_now() else: complaint.status = "draft" return complaint @json_view( content_type="application/json", permission="create_award_complaint", validators=( validate_complaint_data, validate_add_complaint_not_in_qualification_stand_still, validate_award_complaint_add_only_for_active_lots, validate_add_complaint_not_in_complaint_period, validate_add_complaint_with_tender_cancellation_in_pending, validate_add_complaint_with_lot_cancellation_in_pending("award"), ), ) def collection_post(self): return super(TenderEUAwardComplaintResource, self).collection_post() @json_view( content_type="application/json", permission="edit_complaint", validators=( validate_patch_complaint_data, validate_update_complaint_not_in_qualification, validate_award_complaint_update_only_for_active_lots, validate_update_complaint_not_in_allowed_complaint_status, ), ) def patch(self): return super(TenderEUAwardComplaintResource, self).patch() def validate_posting_complaint(self): """ we overwrite checking an active award as long as we have "validate_add_complaint_not_in_qualification_stand_still" in qualification.stand-still we always have an active award """ def on_satisfy_complaint_by_reviewer(self): tender = self.request.validated["tender"] tender.status = "active.qualification" if tender.awardPeriod.endDate: tender.awardPeriod.endDate = None