Merge pull request #391 from Kitzanos/feature/GEPAFINBE-6326

GEPAFINBE-6326 (Ranking Management)
This commit is contained in:
Rinaldo
2026-04-08 13:22:51 +02:00
committed by GitHub
28 changed files with 834 additions and 8 deletions

View File

@@ -108,6 +108,12 @@ public class GepafinConstant {
public static final String INVALID_STATUS_CHANGE_FROM_PUBLISH_TO_DRAFT = "invalid.status.change.from.publish.to.draft";
public static final String STATUS_CANNOT_BE_CHANGED = "status.cannot.be.changed";
public static final String PUBLISHED_CALL_NOT_UPDATE = "published.call.not.update";
public static final String PUBLISHED_CALL_STEP1_ONLY_RANKING_TYPE_ALLOWED = "published.call.step1.only.ranking.type.allowed";
public static final String CALL_RANKING_TYPE_UPDATED_SUCCESSFULLY = "call.ranking.type.updated.successfully";
public static final String APPLICATION_RANKING_ACTION_UPDATED_SUCCESSFULLY = "application.ranking.action.updated.successfully";
public static final String APPLICATION_RANKING_FETCHED_SUCCESSFULLY = "application.ranking.fetched.successfully";
public static final String CALL_MUST_BE_CLOSED_FOR_RANKING_ACTION = "call.must.be.closed.for.ranking.action";
public static final String APPLICATION_RANKING_ACTION_INVALID = "application.ranking.action.invalid";
public static final String INVALID_USER = "invalid_user";
public static final String FLOW_CREATED_SUCCESSFULLY = "flow.created.successfully";
public static final String FLOW_FETCHED_SUCCESSFULLY = "flow.fetched.successfully";

View File

@@ -72,6 +72,7 @@ import java.time.OffsetDateTime;
import java.time.temporal.ChronoUnit;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -215,6 +216,9 @@ public class ApplicationDao {
@Autowired
private ApplicationFormViewRepository applicationFormViewRepository;
@Autowired
private ApplicationRankingViewRepository applicationRankingViewRepository;
@Autowired
private FormRepository formRepository;
@@ -509,6 +513,11 @@ public class ApplicationDao {
responseBean.setDateAccepted(applicationEntity.getDateAccepted());
responseBean.setDateRejected(applicationEntity.getDateRejected());
responseBean.setNdg(applicationEntity.getNdg());
if (applicationEntity.getRankingActionType() != null && !applicationEntity.getRankingActionType().isBlank()) {
responseBean.setRankingActionType(
ApplicationRankingActionTypeEnum.valueOf(applicationEntity.getRankingActionType().trim()));
}
responseBean.setManualRanking(applicationEntity.getManualRanking());
return responseBean;
}
@@ -1152,6 +1161,114 @@ public class ApplicationDao {
return getApplicationResponse(applicationEntity);
}
public ApplicationResponse updateApplicationRankingAction(HttpServletRequest request, Long applicationId,
ApplicationRankingActionTypeEnum rankingActionType, Long manualRanking) {
ApplicationEntity applicationEntity = validateApplication(applicationId);
validator.validateRequest(request, RoleStatusEnum.ROLE_SUPER_ADMIN);
validateCallClosedForRankingAction(applicationEntity.getCall());
if (!ApplicationStatusTypeEnum.APPROVED.getValue().equals(applicationEntity.getStatus())) {
throw new CustomValidationException(Status.BAD_REQUEST,
Translator.toLocale(GepafinConstant.APPLICATION_RANKING_ACTION_INVALID));
}
validateRankingActionRequest(rankingActionType, manualRanking);
ApplicationEntity oldApplicationEntity = Utils.getClonedEntityForData(applicationEntity);
if (rankingActionType == null) {
applicationEntity.setRankingActionType(null);
applicationEntity.setManualRanking(null);
} else {
if (rankingActionType == ApplicationRankingActionTypeEnum.REPOSITION
&& applicationRepository.existsByCallIdAndManualRankingAndIsDeletedFalseAndIdNot(
applicationEntity.getCall().getId(), manualRanking, applicationEntity.getId())) {
throw new CustomValidationException(Status.BAD_REQUEST,
Translator.toLocale(GepafinConstant.APPLICATION_RANKING_ACTION_INVALID));
}
applicationEntity.setRankingActionType(rankingActionType.getValue());
applicationEntity.setManualRanking(
rankingActionType == ApplicationRankingActionTypeEnum.REPOSITION ? manualRanking : null);
}
applicationEntity = applicationRepository.save(applicationEntity);
loggingUtil.addVersionHistory(
VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE)
.oldData(oldApplicationEntity).newData(applicationEntity).build());
return getApplicationResponse(applicationEntity);
}
public CallRankingSummaryResponse getApplicationRanking(Long callId,
List<ApplicationRankingActionTypeEnum> rankingActionTypes) {
CallEntity call = callRepository.findById(callId)
.orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND,
Translator.toLocale(GepafinConstant.CALL_NOT_FOUND)));
Specification<ApplicationRankingView> spec = (root, query, criteriaBuilder) -> {
List<jakarta.persistence.criteria.Predicate> predicates = new ArrayList<>();
predicates.add(criteriaBuilder.equal(root.get("callId"), callId));
if (rankingActionTypes != null && !rankingActionTypes.isEmpty()) {
List<String> types = rankingActionTypes.stream()
.filter(Objects::nonNull)
.map(ApplicationRankingActionTypeEnum::getValue)
.distinct()
.collect(Collectors.toList());
if (!types.isEmpty()) {
predicates.add(root.get("rankingActionType").in(types));
}
}
query.orderBy(criteriaBuilder.asc(root.get("rank")));
return criteriaBuilder.and(predicates.toArray(new jakarta.persistence.criteria.Predicate[0]));
};
List<ApplicationRankingView> rows = applicationRankingViewRepository.findAll(spec);
CallRankingSummaryResponse summary = new CallRankingSummaryResponse();
summary.setCallId(call.getId());
summary.setCallName(call.getName());
summary.setAmount(call.getAmount());
if (call.getRankingType() != null && !call.getRankingType().isBlank()) {
summary.setRankingType(CallRankingTypeEnum.valueOf(call.getRankingType().trim()));
}
summary.setApplications(rows.stream()
.map(this::convertToApplicationRankingResponse)
.collect(Collectors.toList()));
return summary;
}
private ApplicationRankingResponse convertToApplicationRankingResponse(ApplicationRankingView entity) {
ApplicationRankingResponse response = new ApplicationRankingResponse();
response.setApplicationId(entity.getApplicationId());
response.setCallId(entity.getCallId());
response.setUserId(entity.getUserId());
response.setStatus(entity.getStatus());
response.setSubmissionDate(entity.getSubmissionDate());
response.setProtocolDatetime(entity.getProtocolDatetime());
response.setProtocolNumber(entity.getProtocolNumber());
response.setNdg(entity.getNdg());
response.setAmountAccepted(entity.getAmountAccepted());
response.setPecEmail(entity.getPecEmail());
response.setManualRanking(entity.getManualRanking());
response.setRank(entity.getRank());
response.setTotalScore(entity.getTotalScore());
if (entity.getRankingActionType() != null && !entity.getRankingActionType().isBlank()) {
response.setRankingActionType(ApplicationRankingActionTypeEnum.valueOf(entity.getRankingActionType().trim()));
}
if (entity.getRankingType() != null && !entity.getRankingType().isBlank()) {
response.setRankingType(CallRankingTypeEnum.valueOf(entity.getRankingType().trim()));
}
return response;
}
private void validateCallClosedForRankingAction(CallEntity callEntity) {
if (!CallStatusEnum.EXPIRED.getValue().equals(callEntity.getStatus())) {
throw new CustomValidationException(Status.BAD_REQUEST,
Translator.toLocale(GepafinConstant.CALL_MUST_BE_CLOSED_FOR_RANKING_ACTION));
}
}
private void validateRankingActionRequest(ApplicationRankingActionTypeEnum rankingActionType, Long manualRanking) {
if (rankingActionType == ApplicationRankingActionTypeEnum.REPOSITION
&& (manualRanking == null || manualRanking <= 0)) {
throw new CustomValidationException(Status.BAD_REQUEST,
Translator.toLocale(GepafinConstant.APPLICATION_RANKING_ACTION_INVALID));
}
}
/**
* Sets all non-terminal amendments, the application evaluation (if any), and the assigned application row to CLOSE.
*/

View File

@@ -228,6 +228,9 @@ public class CallDao {
if (createCallRequest.getAllowMultipleApplications() != null) {
callEntity.setAllowMultipleApplications(createCallRequest.getAllowMultipleApplications());
}
if (createCallRequest.getRankingType() != null) {
callEntity.setRankingType(createCallRequest.getRankingType().getValue());
}
callEntity = callRepository.save(callEntity);
log.info("CallEntity saved with ID: {} for call name: '{}'", callEntity.getId(), callEntity.getName());
@@ -418,6 +421,9 @@ public class CallDao {
createCallResponseBean.setEmail(callEntity.getEmail());
createCallResponseBean.setCreatedDate(callEntity.getCreatedDate());
createCallResponseBean.setUpdatedDate(callEntity.getUpdatedDate());
if (callEntity.getRankingType() != null && !callEntity.getRankingType().isBlank()) {
createCallResponseBean.setRankingType(CallRankingTypeEnum.valueOf(callEntity.getRankingType().trim()));
}
return createCallResponseBean;
}
@@ -583,6 +589,41 @@ public class CallDao {
}
}
private boolean hasNonRankingStep1Field(UpdateCallRequestStep1 r) {
return r.getName() != null
|| r.getDescriptionShort() != null
|| r.getDescriptionLong() != null
|| r.getDates() != null
|| r.getAmount() != null
|| r.getAmountMax() != null
|| r.getAimedTo() != null
|| r.getDocumentationRequested() != null
|| r.getAmountMin() != null
|| r.getEmail() != null
|| r.getPhoneNumber() != null
|| r.getStartTime() != null
|| r.getEndTime() != null
|| r.getConfidi() != null
|| r.getAllowMultipleApplications() != null
|| r.getFaq() != null
|| r.getNumberOfCheck() != null
|| r.getAppointmentTemplateId() != null
|| r.getEvaluationVersion() != null;
}
private CallResponse updatePublishedCallStep1RankingTypeOnly(HttpServletRequest request, CallEntity callEntity,
CallEntity oldCallEntity, UpdateCallRequestStep1 updateCallRequest) {
setIfUpdated(callEntity::getRankingType, callEntity::setRankingType,
updateCallRequest.getRankingType() != null ? updateCallRequest.getRankingType().getValue() : null);
callEntity = callRepository.save(callEntity);
loggingUtil.addVersionHistory(
VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCallEntity).newData(callEntity).build());
CallResponse response = getCallResponseBean(callEntity);
response.setCurrentStep(GepafinConstant.STEP_1);
log.info("Published call step 1: ranking type only | callId={}", callEntity.getId());
return response;
}
public void isValidDateRange(UpdateCallRequestStep1 updateCallRequest, CallEntity callEntity) {
List<LocalDateTime> dates = updateCallRequest.getDates();
@@ -615,6 +656,18 @@ public class CallDao {
public CallResponse updateCallStep1(HttpServletRequest request,CallEntity callEntity, UpdateCallRequestStep1 updateCallRequest, UserEntity userEntity) {
log.info("Updating Call ID: {}, by User ID: {}", callEntity.getId(),userEntity.getId() );
CallEntity oldCallEntity = Utils.getClonedEntityForData(callEntity);
if (CallStatusEnum.PUBLISH.getValue().equals(callEntity.getStatus())) {
if (hasNonRankingStep1Field(updateCallRequest)) {
throw new CustomValidationException(Status.VALIDATION_ERROR,
Translator.toLocale(GepafinConstant.PUBLISHED_CALL_STEP1_ONLY_RANKING_TYPE_ALLOWED));
}
if (updateCallRequest.getRankingType() != null) {
return updatePublishedCallStep1RankingTypeOnly(request, callEntity, oldCallEntity, updateCallRequest);
}
CallResponse unchanged = getCallResponseBean(callEntity);
unchanged.setCurrentStep(GepafinConstant.STEP_1);
return unchanged;
}
isValidDateRange(updateCallRequest, callEntity);
setIfUpdated(callEntity::getName, callEntity::setName, updateCallRequest.getName());
setIfUpdated(callEntity::getDescriptionShort, callEntity::setDescriptionShort,
@@ -648,8 +701,6 @@ public class CallDao {
if (!requestEndTime.equals(storedEndTime)) {
setIfUpdated(callEntity::getEndTime, callEntity::setEndTime, DateTimeUtil.parseTime(updateCallRequest.getEndTime()));
// callEntity.setStatus(CallStatusEnum.PUBLISH.getValue());
// callRepository.save(callEntity);
isEndTimeUpdated = true;
}
}
@@ -708,10 +759,15 @@ public class CallDao {
setIfUpdated(callEntity::getPhoneNumber, callEntity::setPhoneNumber, updateCallRequest.getPhoneNumber());
setIfUpdated(callEntity::getStartTime, callEntity::setStartTime, DateTimeUtil.parseTime(updateCallRequest.getStartTime()));
setIfUpdated(callEntity::getConfidi, callEntity::setConfidi, updateCallRequest.getConfidi());
setIfUpdated(callEntity::getEvaluationVersion, callEntity::setEvaluationVersion, updateCallRequest.getEvaluationVersion().getValue());
if (updateCallRequest.getEvaluationVersion() != null) {
setIfUpdated(callEntity::getEvaluationVersion, callEntity::setEvaluationVersion,
updateCallRequest.getEvaluationVersion().getValue());
}
setIfUpdated(callEntity::getNumberOfCheck, callEntity::setNumberOfCheck, updateCallRequest.getNumberOfCheck());
setIfUpdated(callEntity::getAppointmentTemplateId, callEntity::setAppointmentTemplateId, updateCallRequest.getAppointmentTemplateId());
setIfUpdated(callEntity::getAllowMultipleApplications, callEntity::setAllowMultipleApplications, updateCallRequest.getAllowMultipleApplications());
setIfUpdated(callEntity::getRankingType, callEntity::setRankingType,
updateCallRequest.getRankingType() != null ? updateCallRequest.getRankingType().getValue() : null);
callEntity = callRepository.save(callEntity);
/** This code is responsible for adding a version history log for the "update call step 1" operation **/
@@ -725,6 +781,15 @@ public class CallDao {
return createCallResponseBean;
}
public CallResponse updateCallRankingType(HttpServletRequest request, CallEntity callEntity, CallRankingTypeEnum rankingType) {
CallEntity oldCallEntity = Utils.getClonedEntityForData(callEntity);
callEntity.setRankingType(rankingType != null ? rankingType.getValue() : null);
callEntity = callRepository.save(callEntity);
loggingUtil.addVersionHistory(
VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCallEntity).newData(callEntity).build());
return getCallResponseBean(callEntity);
}
private void softDeleteFaq(FaqEntity faqEntity) {
FaqEntity oldFaqEntity = Utils.getClonedEntityForData(faqEntity);

View File

@@ -90,4 +90,10 @@ public class ApplicationEntity extends BaseEntity {
@Column(name = "COMPANY_DOCUMENT")
private String companyDocument;
@Column(name = "ranking_action_type")
private String rankingActionType;
@Column(name = "manual_ranking")
private Long manualRanking;
}

View File

@@ -0,0 +1,68 @@
package net.gepafin.tendermanagement.entities;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import org.hibernate.annotations.Immutable;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* Maps {@code application_ranking_view} (read-only).
*/
@Data
@Entity
@Immutable
@Table(name = "application_ranking_view")
public class ApplicationRankingView implements Serializable {
@Id
@Column(name = "application_id")
private Long applicationId;
@Column(name = "listing_rank")
private Long rank;
@Column(name = "call_id")
private Long callId;
@Column(name = "ranking_action_type")
private String rankingActionType;
@Column(name = "total_score")
private BigDecimal totalScore;
@Column(name = "user_id")
private Long userId;
@Column(name = "status")
private String status;
@Column(name = "submission_date")
private LocalDateTime submissionDate;
@Column(name = "protocol_datetime")
private LocalDateTime protocolDatetime;
@Column(name = "protocol_number")
private Long protocolNumber;
@Column(name = "ndg")
private String ndg;
@Column(name = "amount_accepted")
private BigDecimal amountAccepted;
@Column(name = "pec_email")
private String pecEmail;
@Column(name = "manual_ranking")
private Long manualRanking;
@Column(name = "ranking_type")
private String rankingType;
}

View File

@@ -102,5 +102,8 @@ public class CallEntity extends BaseEntity {
@Column(name = "allow_multiple_applications")
private Boolean allowMultipleApplications;
@Column(name = "ranking_type")
private String rankingType;
}

View File

@@ -0,0 +1,20 @@
package net.gepafin.tendermanagement.enums;
import com.fasterxml.jackson.annotation.JsonValue;
public enum ApplicationRankingActionTypeEnum {
REMOVE("REMOVE"),
REPOSITION("REPOSITION");
private final String value;
ApplicationRankingActionTypeEnum(String value) {
this.value = value;
}
@JsonValue
public String getValue() {
return value;
}
}

View File

@@ -0,0 +1,20 @@
package net.gepafin.tendermanagement.enums;
import com.fasterxml.jackson.annotation.JsonValue;
public enum CallRankingTypeEnum {
SCORE("SCORE"),
PROTOCOL_DATE_TIME("PROTOCOL_DATE_TIME");
private final String value;
CallRankingTypeEnum(String value) {
this.value = value;
}
@JsonValue
public String getValue() {
return value;
}
}

View File

@@ -236,7 +236,10 @@ public enum UserActionContextEnum {
REJECT_PEC_MAIL("REJECT_PEC_MAIL"),
FETCH_EMAIL_LOG("FETCH_EMAIL_LOG"),
FETCH_ALL_EMAIL_LOG("FETCH_ALL_EMAIL_LOG"),
UPLOAD_COMPANY_DOCUMENT_TO_APPLICATION("UPLOAD_COMPANY_DOCUMENT_TO_APPLICATION");
UPLOAD_COMPANY_DOCUMENT_TO_APPLICATION("UPLOAD_COMPANY_DOCUMENT_TO_APPLICATION"),
UPDATE_CALL_RANKING_TYPE("UPDATE_CALL_RANKING_TYPE"),
UPDATE_APPLICATION_RANKING_ACTION("UPDATE_APPLICATION_RANKING_ACTION"),
GET_APPLICATION_RANKING("GET_APPLICATION_RANKING");
private final String value;

View File

@@ -0,0 +1,13 @@
package net.gepafin.tendermanagement.model.request;
import lombok.Data;
import net.gepafin.tendermanagement.enums.ApplicationRankingActionTypeEnum;
@Data
public class ApplicationRankingActionRequest {
private ApplicationRankingActionTypeEnum rankingActionType;
/** Required when {@code rankingActionType} is {@link ApplicationRankingActionTypeEnum#REPOSITION}. */
private Long manualRanking;
}

View File

@@ -5,6 +5,7 @@ import java.time.LocalDateTime;
import java.util.List;
import lombok.Data;
import net.gepafin.tendermanagement.enums.CallRankingTypeEnum;
import net.gepafin.tendermanagement.enums.EvaluationVersionEnum;
@Data
@@ -49,4 +50,6 @@ public class CreateCallRequestStep1 {
private List<FaqReq> faq;
private EvaluationVersionEnum evaluationVersion;
private CallRankingTypeEnum rankingType;
}

View File

@@ -5,6 +5,7 @@ import java.time.LocalDateTime;
import java.util.List;
import lombok.Data;
import net.gepafin.tendermanagement.enums.CallRankingTypeEnum;
import net.gepafin.tendermanagement.enums.EvaluationVersionEnum;
@Data
@@ -46,6 +47,8 @@ public class UpdateCallRequestStep1 {
private Long appointmentTemplateId;
private EvaluationVersionEnum evaluationVersion;
private EvaluationVersionEnum evaluationVersion;
private CallRankingTypeEnum rankingType;
}

View File

@@ -0,0 +1,28 @@
package net.gepafin.tendermanagement.model.response;
import lombok.Data;
import net.gepafin.tendermanagement.enums.ApplicationRankingActionTypeEnum;
import net.gepafin.tendermanagement.enums.CallRankingTypeEnum;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
public class ApplicationRankingResponse {
private Long rank;
private Long applicationId;
private Long callId;
private ApplicationRankingActionTypeEnum rankingActionType;
private BigDecimal totalScore;
private Long userId;
private String status;
private LocalDateTime submissionDate;
private LocalDateTime protocolDatetime;
private Long protocolNumber;
private String ndg;
private BigDecimal amountAccepted;
private String pecEmail;
private Long manualRanking;
private CallRankingTypeEnum rankingType;
}

View File

@@ -1,6 +1,7 @@
package net.gepafin.tendermanagement.model.response;
import lombok.Data;
import net.gepafin.tendermanagement.enums.ApplicationRankingActionTypeEnum;
import net.gepafin.tendermanagement.enums.EvaluationVersionEnum;
import net.gepafin.tendermanagement.model.response.ApplicationFormFieldResponseBean;
@@ -54,4 +55,8 @@ public class ApplicationResponse{
private String ndg;
private ApplicationRankingActionTypeEnum rankingActionType;
private Long manualRanking;
}

View File

@@ -0,0 +1,19 @@
package net.gepafin.tendermanagement.model.response;
import lombok.Data;
import net.gepafin.tendermanagement.enums.CallRankingTypeEnum;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/** Call-level ranking payload: tender info plus ordered ranked applications. */
@Data
public class CallRankingSummaryResponse {
private Long callId;
private String callName;
private BigDecimal amount;
private CallRankingTypeEnum rankingType;
private List<ApplicationRankingResponse> applications;
}

View File

@@ -7,6 +7,7 @@ import java.util.List;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Data;
import net.gepafin.tendermanagement.enums.CallRankingTypeEnum;
import net.gepafin.tendermanagement.enums.CallStatusEnum;
import net.gepafin.tendermanagement.enums.EvaluationVersionEnum;
import net.gepafin.tendermanagement.util.DynamicLocalTimeSerializer;
@@ -83,6 +84,8 @@ public class CallResponse {
private Long preferredCallId;
private EvaluationVersionEnum evaluationVersion;
private CallRankingTypeEnum rankingType;
}

View File

@@ -0,0 +1,11 @@
package net.gepafin.tendermanagement.repositories;
import net.gepafin.tendermanagement.entities.ApplicationRankingView;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
@Repository
public interface ApplicationRankingViewRepository extends JpaRepository<ApplicationRankingView, Long>,
JpaSpecificationExecutor<ApplicationRankingView> {
}

View File

@@ -183,5 +183,6 @@ public interface ApplicationRepository extends JpaRepository<ApplicationEntity,
public List<ApplicationEntity> findByCallIdAndIsDeletedFalseAndStatusIn(Long callId,List<String> status);
boolean existsByCallIdAndManualRankingAndIsDeletedFalseAndIdNot(Long callId, Long manualRanking, Long id);
}

View File

@@ -5,6 +5,7 @@ import jakarta.servlet.http.HttpServletResponse;
import net.gepafin.tendermanagement.entities.ApplicationEntity;
import net.gepafin.tendermanagement.model.request.ApplicationPageableRequestBean;
import net.gepafin.tendermanagement.model.request.ApplicationRequest;
import net.gepafin.tendermanagement.enums.ApplicationRankingActionTypeEnum;
import net.gepafin.tendermanagement.enums.ApplicationStatusTypeEnum;
import net.gepafin.tendermanagement.enums.FormActionEnum;
import net.gepafin.tendermanagement.model.request.ApplicationRequestBean;
@@ -57,4 +58,10 @@ public interface ApplicationService {
public byte[] downloadRankingCsv(HttpServletRequest request, Long callId);
public void uploadCompanyDocumentsToApplication(HttpServletRequest request, Long applicationId, List<Long> companyDocumentIds);
ApplicationResponse updateApplicationRankingAction(HttpServletRequest request, Long applicationId,
ApplicationRankingActionTypeEnum rankingActionType, Long manualRanking);
CallRankingSummaryResponse getApplicationRanking(HttpServletRequest request, Long callId,
List<ApplicationRankingActionTypeEnum> rankingActionTypes);
}

View File

@@ -4,6 +4,7 @@ import java.util.List;
import jakarta.servlet.http.HttpServletRequest;
import net.gepafin.tendermanagement.entities.CallEntity;
import net.gepafin.tendermanagement.enums.CallRankingTypeEnum;
import net.gepafin.tendermanagement.enums.CallStatusEnum;
import net.gepafin.tendermanagement.enums.EvaluationVersionEnum;
import net.gepafin.tendermanagement.model.request.*;
@@ -37,4 +38,6 @@ public interface CallService {
CallResponse createCallStep2EvaluationV2(HttpServletRequest request, Long callId, CreateCallRequestStep2EvaluationV2 createCallRequest);
CallResponse updateCallRankingType(HttpServletRequest request, Long callId, CallRankingTypeEnum rankingType);
}

View File

@@ -12,6 +12,7 @@ import net.gepafin.tendermanagement.entities.CompanyEntity;
import net.gepafin.tendermanagement.entities.UserEntity;
import net.gepafin.tendermanagement.model.request.ApplicationPageableRequestBean;
import net.gepafin.tendermanagement.model.request.ApplicationRequest;
import net.gepafin.tendermanagement.enums.ApplicationRankingActionTypeEnum;
import net.gepafin.tendermanagement.enums.ApplicationStatusTypeEnum;
import net.gepafin.tendermanagement.enums.FormActionEnum;
import net.gepafin.tendermanagement.model.request.ApplicationRequestBean;
@@ -195,4 +196,20 @@ public class ApplicationServiceImpl implements ApplicationService {
UserEntity userEntity = validator.validateUser(request);
applicationDao.uploadCompanyDocumentsToApplication(applicationId,companyDocumentIds,userEntity);
}
@Override
@Transactional(rollbackFor = Exception.class)
public ApplicationResponse updateApplicationRankingAction(HttpServletRequest request, Long applicationId,
ApplicationRankingActionTypeEnum rankingActionType, Long manualRanking) {
return applicationDao.updateApplicationRankingAction(request, applicationId, rankingActionType, manualRanking);
}
@Override
@Transactional(readOnly = true)
public CallRankingSummaryResponse getApplicationRanking(HttpServletRequest request, Long callId,
List<ApplicationRankingActionTypeEnum> rankingActionTypes) {
validator.validateUser(request);
validator.validateSuperAdminOrDirector();
return applicationDao.getApplicationRanking(callId, rankingActionTypes);
}
}

View File

@@ -1,9 +1,12 @@
package net.gepafin.tendermanagement.service.impl;
import jakarta.servlet.http.HttpServletRequest;
import net.gepafin.tendermanagement.config.Translator;
import net.gepafin.tendermanagement.constants.GepafinConstant;
import net.gepafin.tendermanagement.dao.CallDao;
import net.gepafin.tendermanagement.entities.CallEntity;
import net.gepafin.tendermanagement.entities.UserEntity;
import net.gepafin.tendermanagement.enums.CallRankingTypeEnum;
import net.gepafin.tendermanagement.enums.CallStatusEnum;
import net.gepafin.tendermanagement.enums.EvaluationVersionEnum;
import net.gepafin.tendermanagement.model.request.*;
@@ -12,6 +15,8 @@ import net.gepafin.tendermanagement.model.response.CallResponse;
import net.gepafin.tendermanagement.model.response.PageableResponseBean;
import net.gepafin.tendermanagement.service.CallService;
import net.gepafin.tendermanagement.util.Validator;
import net.gepafin.tendermanagement.web.rest.api.errors.ForbiddenAccessException;
import net.gepafin.tendermanagement.web.rest.api.errors.Status;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -116,4 +121,15 @@ public class CallServiceImpl implements CallService {
CallEntity call = validator.validateUserWithCall(user, callId);
return callDao.createCallStep2EvaluationV2(call, createCallRequest, user);
}
@Override
@Transactional(rollbackFor = Exception.class)
public CallResponse updateCallRankingType(HttpServletRequest request, Long callId, CallRankingTypeEnum rankingType) {
validator.validateUser(request);
if (Boolean.FALSE.equals(validator.checkIsSuperAdmin())) {
throw new ForbiddenAccessException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PERMISSION_DENIED));
}
CallEntity call = callDao.validateCall(callId);
return callDao.updateCallRankingType(request, call, rankingType);
}
}

View File

@@ -0,0 +1,84 @@
package net.gepafin.tendermanagement.web.rest.api;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import net.gepafin.tendermanagement.enums.ApplicationRankingActionTypeEnum;
import net.gepafin.tendermanagement.enums.CallRankingTypeEnum;
import net.gepafin.tendermanagement.model.request.ApplicationRankingActionRequest;
import net.gepafin.tendermanagement.model.response.CallRankingSummaryResponse;
import net.gepafin.tendermanagement.model.response.ApplicationResponse;
import net.gepafin.tendermanagement.model.response.CallResponse;
import net.gepafin.tendermanagement.model.util.Response;
import net.gepafin.tendermanagement.web.rest.api.errors.ErrorConstants;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@Validated
public interface RankingApi {
@Operation(summary = "API to update call ranking type",
responses = {
@ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
@io.swagger.v3.oas.annotations.media.ExampleObject(value = ErrorConstants.NOTFOUND_ERROR_EXAMPLE)})),
@ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
@io.swagger.v3.oas.annotations.media.ExampleObject(value = ErrorConstants.UNAUTHORIZED_ERROR_EXAMPLE)})),
@ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
@io.swagger.v3.oas.annotations.media.ExampleObject(value = ErrorConstants.BADREQUEST_ERROR_EXAMPLE)}))
})
@PutMapping(value = "/call/{callId}/type", produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasRole('ROLE_SUPER_ADMIN')")
ResponseEntity<Response<CallResponse>> updateCallRankingType(HttpServletRequest request,
@PathVariable("callId") Long callId,
@RequestParam("rankingType") CallRankingTypeEnum rankingType);
@Operation(summary = "API to update application ranking action type in application",
responses = {
@ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
@io.swagger.v3.oas.annotations.media.ExampleObject(value = ErrorConstants.NOTFOUND_ERROR_EXAMPLE)})),
@ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
@io.swagger.v3.oas.annotations.media.ExampleObject(value = ErrorConstants.UNAUTHORIZED_ERROR_EXAMPLE)})),
@ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
@io.swagger.v3.oas.annotations.media.ExampleObject(value = ErrorConstants.BADREQUEST_ERROR_EXAMPLE)}))
})
@PatchMapping(value = "/application/{applicationId}/action", produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasRole('ROLE_SUPER_ADMIN')")
ResponseEntity<Response<ApplicationResponse>> updateApplicationRankingAction(HttpServletRequest request,
@PathVariable("applicationId") Long applicationId,
@Valid @RequestBody ApplicationRankingActionRequest rankingActionRequest);
@Operation(summary = "API to get ranking list with callId",
responses = {
@ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
@io.swagger.v3.oas.annotations.media.ExampleObject(value = ErrorConstants.NOTFOUND_ERROR_EXAMPLE)})),
@ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
@io.swagger.v3.oas.annotations.media.ExampleObject(value = ErrorConstants.UNAUTHORIZED_ERROR_EXAMPLE)}))
})
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasRole('ROLE_SUPER_ADMIN') || hasRole('ROLE_DIRECTOR')")
ResponseEntity<Response<CallRankingSummaryResponse>> getApplicationRanking(HttpServletRequest request,
@Parameter(description = "Call id", required = true)
@RequestParam("callId") Long callId,
@Parameter(description = "ranking action types", required = false,
array = @ArraySchema(schema = @Schema(implementation = ApplicationRankingActionTypeEnum.class)))
@RequestParam(value = "rankingActionType", required = false) List<ApplicationRankingActionTypeEnum> rankingActionTypes);
}

View File

@@ -0,0 +1,74 @@
package net.gepafin.tendermanagement.web.rest.api.impl;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import net.gepafin.tendermanagement.config.Translator;
import net.gepafin.tendermanagement.constants.GepafinConstant;
import net.gepafin.tendermanagement.enums.ApplicationRankingActionTypeEnum;
import net.gepafin.tendermanagement.enums.CallRankingTypeEnum;
import net.gepafin.tendermanagement.enums.UserActionContextEnum;
import net.gepafin.tendermanagement.enums.UserActionLogsEnum;
import net.gepafin.tendermanagement.model.request.ApplicationRankingActionRequest;
import net.gepafin.tendermanagement.model.request.UserActionRequest;
import net.gepafin.tendermanagement.model.response.CallRankingSummaryResponse;
import net.gepafin.tendermanagement.model.response.ApplicationResponse;
import net.gepafin.tendermanagement.model.response.CallResponse;
import net.gepafin.tendermanagement.model.util.Response;
import net.gepafin.tendermanagement.service.ApplicationService;
import net.gepafin.tendermanagement.service.CallService;
import net.gepafin.tendermanagement.util.LoggingUtil;
import net.gepafin.tendermanagement.web.rest.api.RankingApi;
import net.gepafin.tendermanagement.web.rest.api.errors.Status;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/v1/ranking")
public class RankingApiController implements RankingApi {
@Autowired
private CallService callService;
@Autowired
private ApplicationService applicationService;
@Autowired
private LoggingUtil loggingUtil;
@Override
public ResponseEntity<Response<CallResponse>> updateCallRankingType(HttpServletRequest request, Long callId,
CallRankingTypeEnum rankingType) {
loggingUtil.logUserAction(UserActionRequest.builder().request(request).actionType(UserActionLogsEnum.UPDATE)
.actionContext(UserActionContextEnum.UPDATE_CALL_RANKING_TYPE).build());
CallResponse updated = callService.updateCallRankingType(request, callId, rankingType);
return ResponseEntity.status(HttpStatus.OK)
.body(new Response<>(updated, Status.SUCCESS, Translator.toLocale(GepafinConstant.CALL_RANKING_TYPE_UPDATED_SUCCESSFULLY)));
}
@Override
public ResponseEntity<Response<ApplicationResponse>> updateApplicationRankingAction(HttpServletRequest request,
Long applicationId, @Valid @RequestBody ApplicationRankingActionRequest rankingActionRequest) {
loggingUtil.logUserAction(UserActionRequest.builder().request(request).actionType(UserActionLogsEnum.UPDATE)
.actionContext(UserActionContextEnum.UPDATE_APPLICATION_RANKING_ACTION).build());
ApplicationResponse body = applicationService.updateApplicationRankingAction(request, applicationId,
rankingActionRequest.getRankingActionType(), rankingActionRequest.getManualRanking());
return ResponseEntity.status(HttpStatus.OK)
.body(new Response<>(body, Status.SUCCESS, Translator.toLocale(GepafinConstant.APPLICATION_RANKING_ACTION_UPDATED_SUCCESSFULLY)));
}
@Override
public ResponseEntity<Response<CallRankingSummaryResponse>> getApplicationRanking(HttpServletRequest request,
Long callId, List<ApplicationRankingActionTypeEnum> rankingActionTypes) {
loggingUtil.logUserAction(UserActionRequest.builder().request(request).actionType(UserActionLogsEnum.VIEW)
.actionContext(UserActionContextEnum.GET_APPLICATION_RANKING).build());
CallRankingSummaryResponse ranking = applicationService.getApplicationRanking(request, callId, rankingActionTypes);
return ResponseEntity.status(HttpStatus.OK)
.body(new Response<>(ranking, Status.SUCCESS, Translator.toLocale(GepafinConstant.APPLICATION_RANKING_FETCHED_SUCCESSFULLY)));
}
}