Merge pull request #393 from Kitzanos/develop
Sync Master with Develop (09/04/2026)
This commit is contained in:
@@ -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";
|
||||
@@ -141,6 +147,9 @@ public class GepafinConstant {
|
||||
public static final String UPDATING_FORM_VALUE_IMPACT_ON_FLOW = "updating.form.value.impact.on.flow";
|
||||
public static final String APPLICATION_IS_INCOMPLETE_MSG = "application.is.incomplete";
|
||||
public static final String AUTHORIZATION = "Authorization";
|
||||
public static final String BEARER_PREFIX = "Bearer ";
|
||||
public static final String SPREADSHEET = "spreadsheet";
|
||||
public static final String TEMPLATE = "template";
|
||||
public static final String CHECK_VATNUMBER_URL_V1 = "https://imprese.openapi.it/advance";
|
||||
public static final String CHECK_VATNUMBER_URL_V2 = "https://company.openapi.com/IT-advanced";
|
||||
public static final String VAT_CHECK_API_VERSION = "VAT_CHECK_API_VERSION";
|
||||
@@ -205,6 +214,8 @@ public class GepafinConstant {
|
||||
public static final String CALL_ALREADY_ENDED = "call.already.ended";
|
||||
public static final String APPLICATION_STATUS_UPDATED_SUCCESSFULLY = "application.status.updated.successfully";
|
||||
public static final String APPLICATION_ALREADY_IN_PREVIOUS_STATUS = "application.already.in.provided.status";
|
||||
public static final String APPLICATION_STATUS_TRANSITION_RESTRICTED = "application.status.transition.restricted";
|
||||
public static final String APPLICATION_REGISTRY_SEGMENT_INVALID = "application.registry.segment.invalid";
|
||||
public static final String DELEGATION_NOT_FOUND = "delegation.not.found";
|
||||
public static final String USER_COMPANY_RELATION_NOT_FOUND = "user.company.relation.not.found";
|
||||
public static final String DELEGATION_DELETE_SUCCESS = "delegation.delete.success";
|
||||
@@ -445,6 +456,7 @@ public class GepafinConstant {
|
||||
public static final String COMPANY_DOCUMENT_NOT_FOUND = "company.document.not.found";
|
||||
public static final String COMPANY_DOCUMENT_UPDATED_SUCCESSFULLY = "company.document.updated.successfully";
|
||||
public static final String COMPANY_DOCUMENT_COPIED_SUCCESSFULLY = "company.document.copied.successfully";
|
||||
public static final String COMPANY_DOCUMENT_COPY_EVALUATION_REQUIRES_APPLICATION_EVALUATION = "company.document.copy.evaluation.requires.application.evaluation";
|
||||
public static final String COMPANY_DOCUMENT_FETCHED_SUCCESSFULLY = "company.document.fetched.successfully";
|
||||
|
||||
public static final String DOCUMENT_CATEGORY_CREATE_SUCCESS = "document.category.success";
|
||||
|
||||
@@ -1164,8 +1164,12 @@ public class ApplicationAmendmentRequestDao {
|
||||
applicationEvaluationRepository.save(existingApplicationAmendment.getApplicationEvaluationEntity());
|
||||
log.info("Updated ApplicationEvaluation status to OPEN for ID: {}", existingApplicationEvaluationEntity.getId());
|
||||
|
||||
if(Boolean.FALSE.equals(existingApplicationAmendment.getType().equals(ApplicationAmendmentRequestTypeEnum.SPECIAL.getValue()))){
|
||||
application.setStatus(application.getPreviousStatus());
|
||||
String previousApplicationStatus = application.getPreviousStatus();
|
||||
if (previousApplicationStatus != null) {
|
||||
application.setStatus(previousApplicationStatus);
|
||||
} else if (ApplicationAmendmentRequestTypeEnum.SPECIAL.getValue().equals(existingApplicationAmendment.getType())) {
|
||||
application.setStatus(ApplicationStatusTypeEnum.ADMISSIBLE.getValue());
|
||||
log.warn("Special amendment close: previousStatus was null for applicationId={}, defaulting to ADMISSIBLE", application.getId());
|
||||
}
|
||||
applicationRepository.save(application);
|
||||
log.info("Updated Application status to previous state for Application ID: {}", application.getId());
|
||||
@@ -1222,8 +1226,22 @@ public class ApplicationAmendmentRequestDao {
|
||||
applicationAmendmentRequestEntity.setExtensionDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now()));
|
||||
applicationAmendmentRequestEntity.setEndDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now().plusDays(newResponseDays)));
|
||||
applicationAmendmentRequestEntity.setStatus(ApplicationAmendmentRequestEnum.AWAITING.getValue());
|
||||
applicationAmendmentRequestEntity.getApplicationEvaluationEntity().setStatus(ApplicationEvaluationStatusTypeEnum.SOCCORSO.getValue());
|
||||
applicationAmendmentRequestEntity.getApplicationEvaluationEntity().getAssignedApplicationsEntity().getApplication().setStatus(ApplicationStatusTypeEnum.SOCCORSO.getValue());
|
||||
ApplicationEvaluationEntity evaluationEntity = applicationAmendmentRequestEntity.getApplicationEvaluationEntity();
|
||||
ApplicationEvaluationEntity oldEvaluationEntity = Utils.getClonedEntityForData(evaluationEntity);
|
||||
evaluationEntity.setStatus(ApplicationEvaluationStatusTypeEnum.SOCCORSO.getValue());
|
||||
ApplicationEntity applicationEntity = applicationService.validateApplication(applicationAmendmentRequestEntity.getApplicationId());
|
||||
ApplicationEntity oldApplicationEntity = Utils.getClonedEntityForData(applicationEntity);
|
||||
if (ApplicationAmendmentRequestTypeEnum.SPECIAL.getValue().equals(applicationAmendmentRequestEntity.getType())) {
|
||||
applicationEntity.setStatus(ApplicationStatusTypeEnum.AWAITING_TECHNICAL_EVALUATION.getValue());
|
||||
} else {
|
||||
applicationEntity.setStatus(ApplicationStatusTypeEnum.SOCCORSO.getValue());
|
||||
}
|
||||
applicationEvaluationRepository.save(evaluationEntity);
|
||||
loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldEvaluationEntity).newData(evaluationEntity).build());
|
||||
applicationRepository.save(applicationEntity);
|
||||
/** Version history for application when extending a special amendment (aligned with create special amendment). **/
|
||||
loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationEntity).newData(applicationEntity).build());
|
||||
|
||||
applicationAmendmentRequestRepository.save(applicationAmendmentRequestEntity);
|
||||
|
||||
/** This code is responsible for adding a version history log for the "Update Application Amendment" operation. **/
|
||||
@@ -1910,6 +1928,15 @@ public class ApplicationAmendmentRequestDao {
|
||||
if(Boolean.FALSE.equals(applicationEntity.getStatus().equals(ApplicationStatusTypeEnum.ADMISSIBLE.getValue()))) {
|
||||
throw new CustomValidationException(Status.VALIDATION_ERROR,Translator.toLocale(GepafinConstant.INVALID_APPLICATION_STATUS));
|
||||
}
|
||||
List<ApplicationAmendmentRequestEntity> existingAmendmentsForEval = applicationAmendmentRequestRepository.findAllByApplicationEvaluationIdAndIsDeletedFalse(applicationEvaluationEntity.getId());
|
||||
boolean noneClosedOrExpiredForSpecial = existingAmendmentsForEval.stream()
|
||||
.noneMatch(amendment ->
|
||||
amendment.getStatus().equals(ApplicationAmendmentRequestEnum.CLOSE.getValue()) ||
|
||||
amendment.getStatus().equals(ApplicationAmendmentRequestEnum.EXPIRED.getValue())
|
||||
);
|
||||
if (Boolean.TRUE.equals(noneClosedOrExpiredForSpecial)) {
|
||||
applicationEntity.setPreviousStatus(oldApplicationEntity.getStatus());
|
||||
}
|
||||
ApplicationAmendmentRequestEntity applicationAmendmentRequestEntity = new ApplicationAmendmentRequestEntity();
|
||||
if(Boolean.TRUE.equals(applicationAmendmentRequest.getAmendmentDocumentType().equals(AmendmentDocumentTypeEnum.ALTRE_GARANZIE))) {
|
||||
applicationAmendmentRequestEntity.setResponseDays(20l);
|
||||
|
||||
@@ -69,8 +69,10 @@ import java.text.SimpleDateFormat;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
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;
|
||||
@@ -214,6 +216,9 @@ public class ApplicationDao {
|
||||
@Autowired
|
||||
private ApplicationFormViewRepository applicationFormViewRepository;
|
||||
|
||||
@Autowired
|
||||
private ApplicationRankingViewRepository applicationRankingViewRepository;
|
||||
|
||||
@Autowired
|
||||
private FormRepository formRepository;
|
||||
|
||||
@@ -507,6 +512,12 @@ public class ApplicationDao {
|
||||
responseBean.setAmountRequested(applicationEntity.getAmountRequested());
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1046,6 +1057,10 @@ public class ApplicationDao {
|
||||
log.info("Updating status for Application id : " + applicationId);
|
||||
ApplicationEntity applicationEntity = validateApplication(applicationId);
|
||||
|
||||
if (ApplicationStatusTypeEnum.DELETED.equals(status) || ApplicationStatusTypeEnum.DELETED_CONFIRMED.equals(status)) {
|
||||
throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.APPLICATION_STATUS_TRANSITION_RESTRICTED));
|
||||
}
|
||||
|
||||
log.info("Call end date verified successfully | callId: {}", applicationEntity.getCall().getId());
|
||||
//cloned entity for old application data
|
||||
ApplicationEntity oldApplicationEntity = Utils.getClonedEntityForData(applicationEntity);
|
||||
@@ -1119,6 +1134,191 @@ public class ApplicationDao {
|
||||
return getApplicationResponse(applicationEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opaque registry segment update (restricted operators only). {@code segment} 1 → {@link ApplicationStatusTypeEnum#DELETED},
|
||||
* 2 → {@link ApplicationStatusTypeEnum#DELETED_CONFIRMED}.
|
||||
*/
|
||||
public ApplicationResponse recordApplicationRegistrySegment(HttpServletRequest request, Long applicationId,ApplicationStatusTypeEnum status) {
|
||||
validator.validateSuperAdminOrDirector();
|
||||
validator.validateUser(request);
|
||||
if (Boolean.FALSE.equals(status.equals(ApplicationStatusTypeEnum.DELETED)) && Boolean.FALSE.equals(status.equals(ApplicationStatusTypeEnum.DELETED_CONFIRMED))) {
|
||||
throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.APPLICATION_REGISTRY_SEGMENT_INVALID));
|
||||
}
|
||||
ApplicationEntity applicationEntity = validateApplication(applicationId);
|
||||
if (Boolean.FALSE.equals(validator.checkIsSuperAdmin())) {
|
||||
validator.validateHubId(request, applicationEntity.getHubId());
|
||||
}
|
||||
if (status.getValue().equals(applicationEntity.getStatus())) {
|
||||
throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.APPLICATION_ALREADY_IN_PREVIOUS_STATUS));
|
||||
}
|
||||
ApplicationEntity oldApplicationEntity = Utils.getClonedEntityForData(applicationEntity);
|
||||
applicationEntity.setStatus(status.getValue());
|
||||
applicationEntity = applicationRepository.save(applicationEntity);
|
||||
closeAmendmentsEvaluationAndAssignedApplicationForRegistry(request, applicationId);
|
||||
loggingUtil.addVersionHistory(
|
||||
VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationEntity).newData(applicationEntity).build());
|
||||
log.info("Registry segment persisted | applicationId={}, status={}", applicationId, status);
|
||||
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.
|
||||
*/
|
||||
private void closeAmendmentsEvaluationAndAssignedApplicationForRegistry(HttpServletRequest httpRequest, Long applicationId) {
|
||||
LocalDateTime nowUtc = DateTimeUtil.DateServerToUTC(LocalDateTime.now());
|
||||
|
||||
List<ApplicationAmendmentRequestEntity> amendments = applicationAmendmentRequestRepository.findByApplicationIdAndIsDeletedFalse(applicationId);
|
||||
for (ApplicationAmendmentRequestEntity amendment : amendments) {
|
||||
if (ApplicationAmendmentRequestEnum.CLOSE.getValue().equals(amendment.getStatus())
|
||||
|| ApplicationAmendmentRequestEnum.EXPIRED.getValue().equals(amendment.getStatus())
|
||||
|| ApplicationAmendmentRequestEnum.REJECTED.getValue().equals(amendment.getStatus())) {
|
||||
continue;
|
||||
}
|
||||
ApplicationAmendmentRequestEntity oldAmendment = Utils.getClonedEntityForData(amendment);
|
||||
amendment.setStatus(ApplicationAmendmentRequestEnum.CLOSE.getValue());
|
||||
amendment.setClosingDate(nowUtc);
|
||||
applicationAmendmentRequestRepository.save(amendment);
|
||||
loggingUtil.addVersionHistory(
|
||||
VersionHistoryRequest.builder().request(httpRequest).actionType(VersionActionTypeEnum.UPDATE).oldData(oldAmendment).newData(amendment).build());
|
||||
}
|
||||
|
||||
applicationEvaluationRepository.findByApplicationIdAndIsDeletedFalse(applicationId).ifPresent(evaluation -> {
|
||||
if (ApplicationEvaluationStatusTypeEnum.CLOSE.getValue().equals(evaluation.getStatus())) {
|
||||
return;
|
||||
}
|
||||
ApplicationEvaluationEntity oldEvaluation = Utils.getClonedEntityForData(evaluation);
|
||||
evaluation.setStatus(ApplicationEvaluationStatusTypeEnum.CLOSE.getValue());
|
||||
evaluation.setClosingDate(nowUtc);
|
||||
if (evaluation.getStartDate() != null && evaluation.getClosingDate() != null) {
|
||||
long activeDays = ChronoUnit.DAYS.between(evaluation.getStartDate(), evaluation.getClosingDate());
|
||||
activeDays -= evaluation.getSuspendedDays() != null ? evaluation.getSuspendedDays() : 0;
|
||||
evaluation.setActiveDays(activeDays);
|
||||
}
|
||||
applicationEvaluationRepository.save(evaluation);
|
||||
loggingUtil.addVersionHistory(
|
||||
VersionHistoryRequest.builder().request(httpRequest).actionType(VersionActionTypeEnum.UPDATE).oldData(oldEvaluation).newData(evaluation).build());
|
||||
});
|
||||
|
||||
assignedApplicationsRepository.findByApplicationIdAndIsDeletedFalse(applicationId).ifPresent(assigned -> {
|
||||
if (AssignedApplicationEnum.CLOSE.getValue().equals(assigned.getStatus())) {
|
||||
return;
|
||||
}
|
||||
AssignedApplicationsEntity oldAssigned = Utils.getClonedEntityForData(assigned);
|
||||
assigned.setStatus(AssignedApplicationEnum.CLOSE.getValue());
|
||||
assignedApplicationsRepository.save(assigned);
|
||||
loggingUtil.addVersionHistory(
|
||||
VersionHistoryRequest.builder().request(httpRequest).actionType(VersionActionTypeEnum.UPDATE).oldData(oldAssigned).newData(assigned).build());
|
||||
});
|
||||
}
|
||||
|
||||
public Integer calculateProgress(Long totalSteps, Long completedSteps) {
|
||||
if (FieldValidator.isNullOrZero(totalSteps)) {
|
||||
throw new CustomValidationException(Status.BAD_REQUEST,Translator.toLocale(GepafinConstant.TOTAL_STEPS_NOT_BE_ZERO));
|
||||
@@ -2004,8 +2204,13 @@ public class ApplicationDao {
|
||||
searchPattern
|
||||
);
|
||||
|
||||
Predicate ndgPredicate = criteriaBuilder.like(
|
||||
criteriaBuilder.upper(root.get(GepafinConstant.NDG_STRING)),
|
||||
searchPattern
|
||||
);
|
||||
|
||||
// Combine them using a single `or()`
|
||||
Predicate finalPredicate = criteriaBuilder.or(titlePredicate, callTitlePredicate,callNamePredicate,companyName);
|
||||
Predicate finalPredicate = criteriaBuilder.or(titlePredicate, callTitlePredicate,callNamePredicate,companyName, ndgPredicate);
|
||||
predicates.add(finalPredicate);
|
||||
}
|
||||
|
||||
@@ -2071,6 +2276,7 @@ public class ApplicationDao {
|
||||
responseBean.setAmountRequested(applicationView.getAmountRequested());
|
||||
responseBean.setDateAccepted(applicationView.getDateAccepted());
|
||||
responseBean.setDateRejected(applicationView.getDateRejected());
|
||||
responseBean.setNdg(applicationView.getNdg());
|
||||
return responseBean;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,14 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.json.JSONObject;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -115,6 +121,9 @@ public class ApplicationEvaluationDao {
|
||||
@Autowired
|
||||
private ApplicationAmendmentRequestDao applicationAmendmentRequestDao;
|
||||
|
||||
@Autowired
|
||||
private CompanyDocumentDao companyDocumentDao;
|
||||
|
||||
@Autowired
|
||||
private HubService hubService;
|
||||
|
||||
@@ -154,6 +163,12 @@ public class ApplicationEvaluationDao {
|
||||
@Value("${default.hub.uuid}")
|
||||
private String defaultHubUuid;
|
||||
|
||||
@Value("${excel.fill.api.url}")
|
||||
private String excelFillApiUrl;
|
||||
|
||||
@Value("${excel.fill.api.token:}")
|
||||
private String excelFillApiToken;
|
||||
|
||||
@Autowired
|
||||
private ApplicationSignedDocumentRepository applicationSignedDocumentRepository;
|
||||
|
||||
@@ -215,7 +230,6 @@ public class ApplicationEvaluationDao {
|
||||
setEvaluationDocResponse(response, allDocs);
|
||||
setApplicationDetails(response, entity);
|
||||
setRejectedDocuments(applicationEntity, response);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -760,6 +774,11 @@ public class ApplicationEvaluationDao {
|
||||
|
||||
// Map<String, String> placeHolders = notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.EVALUATION_CREATION);
|
||||
|
||||
// V2 only, idempotent: runs on every create/update of application evaluation (e.g. evaluation created at
|
||||
// instructor assignment). External fill API is called only for spreadsheet fields that still have no stored
|
||||
// value. If APPLICATION_EVALUATION_FORM / spreadsheet rows were created here earlier, later
|
||||
// createApplicationEvaluation and plain updates skip re-fill and leave existing flow unchanged.
|
||||
populateSpreadsheetEvaluationFormFieldsForV2(application, entity);
|
||||
Map<String, String> placeHolders = new HashMap<>();
|
||||
placeHolders.put("{{call_name}}", application.getCall().getName());
|
||||
String protocolNumber=application.getProtocol().getExternalProtocolNumber();
|
||||
@@ -2188,6 +2207,123 @@ public class ApplicationEvaluationDao {
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluation V2 only ({@link ApplicationEntity#getEvaluationVersion()}).
|
||||
* <p>
|
||||
* Ensures {@link ApplicationEvaluationFormEntity} exists for the call's evaluation form, then for each
|
||||
* {@code spreadsheet} field calls the external fill API and saves {@code APPLICATION_EVALUATION_FORM_FIELD}
|
||||
* only when that field has no stored value yet. Safe when this runs at assignment-time (first evaluation create)
|
||||
* and on later {@code createOrUpdateApplicationEvaluation} updates: no second API call and no overwrite once a
|
||||
* value is present. {@link #createApplicationEvaluation} continues to work: it reuses the same form row via
|
||||
* {@link #getApplicationEvaluationFormOrCreate}.
|
||||
*/
|
||||
private void populateSpreadsheetEvaluationFormFieldsForV2(ApplicationEntity application, ApplicationEvaluationEntity entity) {
|
||||
if (!EvaluationVersionEnum.V2.getValue().equals(application.getEvaluationVersion())) {
|
||||
return;
|
||||
}
|
||||
EvaluationFormEntity evaluationFormEntity = evaluationFormRepository.findByCallIdAndIsDeletedFalse(application.getCall().getId());
|
||||
if (evaluationFormEntity == null) {
|
||||
return;
|
||||
}
|
||||
List<ContentResponseBean> contentResponseBeans = evaluationFormDao
|
||||
.convertEvaluationFormEntityToEvaluationFormResponseBean(evaluationFormEntity)
|
||||
.getContent();
|
||||
if (CollectionUtils.isEmpty(contentResponseBeans)) {
|
||||
return;
|
||||
}
|
||||
boolean evaluationFormHasSpreadsheet = contentResponseBeans.stream()
|
||||
.anyMatch(c -> GepafinConstant.SPREADSHEET.equals(c.getName()));
|
||||
if (!evaluationFormHasSpreadsheet) {
|
||||
return;
|
||||
}
|
||||
ApplicationEvaluationFormEntity applicationEvaluationFormEntity = getApplicationEvaluationFormOrCreate(evaluationFormEntity, entity);
|
||||
List<ApplicationFormFieldRequestBean> spreadsheetUpdates = new ArrayList<>();
|
||||
for (ContentResponseBean contentResponseBean : contentResponseBeans) {
|
||||
if (!GepafinConstant.SPREADSHEET.equals(contentResponseBean.getName())) {
|
||||
continue;
|
||||
}
|
||||
String fieldId = contentResponseBean.getId();
|
||||
Optional<ApplicationEvaluationFormFieldEntity> existingSpreadsheetField = applicationEvaluationFormFieldRepository
|
||||
.findByApplicationEvaluationFormIdAndFieldIdAndIsDeletedFalse(applicationEvaluationFormEntity.getId(), fieldId);
|
||||
if (existingSpreadsheetField.isPresent()
|
||||
&& StringUtils.isNotBlank(existingSpreadsheetField.get().getFieldValue())) {
|
||||
continue;
|
||||
}
|
||||
Object populatedSpreadsheetValue = fetchSpreadsheetValueFromExternalApi(
|
||||
application.getCall().getId(),
|
||||
application.getId(),
|
||||
contentResponseBean
|
||||
);
|
||||
if (populatedSpreadsheetValue == null) {
|
||||
log.warn("Skipping spreadsheet field {}: external API did not return a template workbook value", fieldId);
|
||||
continue;
|
||||
}
|
||||
ApplicationFormFieldRequestBean spreadsheetFieldRequest = new ApplicationFormFieldRequestBean();
|
||||
spreadsheetFieldRequest.setFieldId(fieldId);
|
||||
spreadsheetFieldRequest.setFieldValue(populatedSpreadsheetValue);
|
||||
spreadsheetUpdates.add(spreadsheetFieldRequest);
|
||||
}
|
||||
if (!spreadsheetUpdates.isEmpty()) {
|
||||
createOrUpdateMultipleFormFields(spreadsheetUpdates, applicationEvaluationFormEntity, evaluationFormEntity);
|
||||
}
|
||||
}
|
||||
|
||||
private Object fetchSpreadsheetValueFromExternalApi(Long callId, Long applicationId, ContentResponseBean spreadsheetField) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
if (StringUtils.isNotBlank(excelFillApiToken)) {
|
||||
headers.set(GepafinConstant.AUTHORIZATION, GepafinConstant.BEARER_PREFIX + excelFillApiToken);
|
||||
}
|
||||
|
||||
Map<String, Object> requestBody = new LinkedHashMap<>();
|
||||
requestBody.put("call_id", callId);
|
||||
requestBody.put("application_id", applicationId);
|
||||
requestBody.put(GepafinConstant.TEMPLATE, spreadsheetField);
|
||||
|
||||
try {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
ResponseEntity<Object> response = restTemplate.postForEntity(
|
||||
excelFillApiUrl,
|
||||
new HttpEntity<>(requestBody, headers),
|
||||
Object.class
|
||||
);
|
||||
return resolveSpreadsheetFieldValueFromApiResponse(response.getBody());
|
||||
} catch (RestClientException exception) {
|
||||
log.error("Failed to populate spreadsheet field {} from external API", spreadsheetField.getId(), exception);
|
||||
throw new CustomValidationException(Status.BAD_REQUEST,
|
||||
"Failed to populate spreadsheet field from external API for fieldId: " + spreadsheetField.getId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* External fill API returns a spreadsheet field-shaped JSON (id, name, settings, …). Persist only the
|
||||
* workbook object: {@code settings[name=template].value}. If the response is already a bare workbook
|
||||
* ({@code sheets} / {@code sheetOrder}), it is stored as-is.
|
||||
*/
|
||||
private Object resolveSpreadsheetFieldValueFromApiResponse(Object apiResponseBody) {
|
||||
if (apiResponseBody == null) {
|
||||
return null;
|
||||
}
|
||||
if (!(apiResponseBody instanceof Map<?, ?> map)) {
|
||||
return apiResponseBody;
|
||||
}
|
||||
Object settingsObj = map.get("settings");
|
||||
if (settingsObj instanceof List<?> settingsList) {
|
||||
for (Object setting : settingsList) {
|
||||
if (!(setting instanceof Map<?, ?> settingMap)) {
|
||||
continue;
|
||||
}
|
||||
Object name = settingMap.get("name");
|
||||
if (GepafinConstant.TEMPLATE.equals(name != null ? name.toString() : null)) {
|
||||
return settingMap.get("value");
|
||||
}
|
||||
}
|
||||
log.warn("External spreadsheet API returned settings[] without a template entry");
|
||||
return null;
|
||||
}
|
||||
return apiResponseBody;
|
||||
}
|
||||
|
||||
private ApplicationEvaluationFormEntity getApplicationEvaluationFormOrCreate(EvaluationFormEntity evaluationFormEntity, ApplicationEvaluationEntity applicationEvaluationEntity) {
|
||||
|
||||
ApplicationEvaluationFormEntity applicationEvaluationFormEntity = applicationEvaluationFormRepository.findByEvaluationIdAndEvaluationFormId(applicationEvaluationEntity.getId(), evaluationFormEntity.getId());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -3,6 +3,9 @@ package net.gepafin.tendermanagement.dao;
|
||||
import com.amazonaws.services.s3.AmazonS3;
|
||||
import com.amazonaws.services.s3.AmazonS3Client;
|
||||
import com.amazonaws.services.s3.model.CopyObjectRequest;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import jakarta.persistence.criteria.Join;
|
||||
import jakarta.persistence.criteria.JoinType;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -11,13 +14,17 @@ import net.gepafin.tendermanagement.constants.GepafinConstant;
|
||||
import net.gepafin.tendermanagement.entities.*;
|
||||
import net.gepafin.tendermanagement.enums.*;
|
||||
import net.gepafin.tendermanagement.model.request.CompanyDocumentRequest;
|
||||
import net.gepafin.tendermanagement.model.request.EvaluationDocumentRequest;
|
||||
import net.gepafin.tendermanagement.model.request.VersionHistoryRequest;
|
||||
import net.gepafin.tendermanagement.model.response.DocumentCategoryResponse;
|
||||
import net.gepafin.tendermanagement.model.response.CompanyDocumentResponseBean;
|
||||
import net.gepafin.tendermanagement.model.response.DocumentResponseBean;
|
||||
import net.gepafin.tendermanagement.model.response.UploadFileOnAmazonS3Response;
|
||||
import net.gepafin.tendermanagement.model.util.NanoIdUtils;
|
||||
import net.gepafin.tendermanagement.repositories.ApplicationEvaluationRepository;
|
||||
import net.gepafin.tendermanagement.repositories.CompanyDocumentRepository;
|
||||
import net.gepafin.tendermanagement.repositories.DocumentRepository;
|
||||
import net.gepafin.tendermanagement.repositories.UserWithCompanyRepository;
|
||||
import net.gepafin.tendermanagement.service.AmazonS3Service;
|
||||
import net.gepafin.tendermanagement.service.ApplicationService;
|
||||
import net.gepafin.tendermanagement.service.CompanyService;
|
||||
@@ -26,6 +33,7 @@ import net.gepafin.tendermanagement.util.LoggingUtil;
|
||||
import net.gepafin.tendermanagement.util.Utils;
|
||||
import net.gepafin.tendermanagement.util.Validator;
|
||||
import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException;
|
||||
import net.gepafin.tendermanagement.web.rest.api.errors.ForbiddenAccessException;
|
||||
import net.gepafin.tendermanagement.web.rest.api.errors.ResourceNotFoundException;
|
||||
import net.gepafin.tendermanagement.web.rest.api.errors.Status;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -35,9 +43,13 @@ import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.net.URL;
|
||||
import java.security.SecureRandom;
|
||||
import java.time.LocalDateTime;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static net.gepafin.tendermanagement.util.Utils.setIfUpdated;
|
||||
@@ -91,6 +103,64 @@ public class CompanyDocumentDao {
|
||||
@Autowired
|
||||
private AmazonS3 amazonS3;
|
||||
|
||||
@Autowired
|
||||
private UserWithCompanyRepository userWithCompanyRepository;
|
||||
|
||||
@Autowired
|
||||
private ApplicationEvaluationRepository applicationEvaluationRepository;
|
||||
|
||||
/**
|
||||
* Instructor uploads a company document tied to an application. Files are stored under the same S3 layout as
|
||||
* {@link CompanyDocumentTypeEnum#COMPANY_DOCUMENT}; persisted row type remains {@link CompanyDocumentTypeEnum#APPLICATION_DOCUMENT}.
|
||||
*/
|
||||
public List<CompanyDocumentResponseBean> uploadInstructorCompanyDocumentToApplication(Long userId, List<MultipartFile> files, Long companyId, Long applicationId, Long documentCategoryId, LocalDateTime expirationDate, String name) {
|
||||
log.info("Instructor upload company document to application. userId={}, companyId={}, applicationId={}", userId, companyId, applicationId);
|
||||
ApplicationEntity application = applicationService.validateApplicationWithCompany(applicationId, companyId);
|
||||
DocumentCategoryEntity categoryEntity = categoryDao.validateCategory(documentCategoryId);
|
||||
Optional<UserWithCompanyEntity> userWithCompanyOpt = userWithCompanyRepository.findByUserIdAndCompanyIdAndIsDeletedFalse(userId, companyId);
|
||||
|
||||
LocalDateTime currentDate = LocalDateTime.now();
|
||||
if (expirationDate.isBefore(currentDate)) {
|
||||
log.warn("Expiration date {} is before current time {}", expirationDate, currentDate);
|
||||
throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.INVALID_EXPIRATION_DATE));
|
||||
}
|
||||
CompanyDocumentTypeEnum storedType = CompanyDocumentTypeEnum.COMPANY_DOCUMENT;
|
||||
List<CompanyDocumentEntity> companyDocumentEntities = new ArrayList<>();
|
||||
for (MultipartFile file : files) {
|
||||
log.info("Uploading instructor company document '{}' for companyId={}, applicationId={}", file.getOriginalFilename(), companyId, applicationId);
|
||||
UploadFileOnAmazonS3Response uploadFileOnAmazonS3Response = uploadFileOnAmazonS3(file, CompanyDocumentTypeEnum.COMPANY_DOCUMENT, companyId);
|
||||
if (uploadFileOnAmazonS3Response != null) {
|
||||
CompanyDocumentEntity companyDocumentEntity = new CompanyDocumentEntity();
|
||||
companyDocumentEntity.setFileName(uploadFileOnAmazonS3Response.getFileName());
|
||||
companyDocumentEntity.setCompanyId(companyId);
|
||||
companyDocumentEntity.setApplicationId(applicationId);
|
||||
companyDocumentEntity.setType(storedType.getValue());
|
||||
companyDocumentEntity.setFilePath(uploadFileOnAmazonS3Response.getFilePath());
|
||||
companyDocumentEntity.setIsDeleted(false);
|
||||
companyDocumentEntity.setUploadedBy(userId);
|
||||
companyDocumentEntity.setName(name);
|
||||
if (expirationDate.isBefore(currentDate.plusDays(7))) {
|
||||
companyDocumentEntity.setStatus(CompanyDocumentStatusEnum.DUE.getValue());
|
||||
} else {
|
||||
companyDocumentEntity.setStatus(CompanyDocumentStatusEnum.VALID.getValue());
|
||||
}
|
||||
companyDocumentEntity.setCategoryEntity(categoryEntity);
|
||||
companyDocumentEntity.setUserWithCompany(userWithCompanyOpt.orElse(null));
|
||||
companyDocumentEntity.setExpirationDate(expirationDate);
|
||||
companyDocumentEntities.add(companyDocumentEntity);
|
||||
}
|
||||
}
|
||||
companyDocumentRepository.saveAll(companyDocumentEntities);
|
||||
companyDocumentEntities.forEach(entity -> loggingUtil.addVersionHistory(
|
||||
VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.INSERT).oldData(null).newData(entity).build()));
|
||||
|
||||
copyInstructorCompanyDocumentsToEvaluationDocuments(application, companyDocumentEntities, userId);
|
||||
|
||||
return companyDocumentEntities.stream()
|
||||
.map(this::convertToCompanyDocumentResponseBean)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<CompanyDocumentResponseBean> uploadFileForCompany(Long userId, List<MultipartFile> files, Long companyId, Long documentCategoryId, CompanyDocumentTypeEnum companyDocumentSourceTypeEnum, LocalDateTime expirationDate,String name){
|
||||
|
||||
log.info("Uploading files for company. userId={}, companyId={}, documentCategoryId={}", userId, companyId, documentCategoryId);
|
||||
@@ -173,7 +243,9 @@ public class CompanyDocumentDao {
|
||||
public CompanyDocumentResponseBean convertToCompanyDocumentResponseBean(CompanyDocumentEntity entity) {
|
||||
CompanyDocumentResponseBean responseBean = new CompanyDocumentResponseBean();
|
||||
DocumentCategoryEntity categoryEntity = entity.getCategoryEntity();
|
||||
DocumentCategoryResponse responseCategory = categoryDao.convertToResponseBean(categoryEntity);
|
||||
if (categoryEntity != null) {
|
||||
responseBean.setCategory(categoryDao.convertToResponseBean(categoryEntity));
|
||||
}
|
||||
responseBean.setId(entity.getId());
|
||||
responseBean.setFileName(entity.getFileName());
|
||||
responseBean.setType(CompanyDocumentTypeEnum.valueOf(entity.getType()));
|
||||
@@ -182,9 +254,9 @@ public class CompanyDocumentDao {
|
||||
responseBean.setExpirationDate(entity.getExpirationDate());
|
||||
responseBean.setStatus(entity.getStatus());
|
||||
responseBean.setUploadedBy(entity.getUploadedBy());
|
||||
responseBean.setCategory(responseCategory);
|
||||
responseBean.setName(entity.getName());
|
||||
responseBean.setUserWithCompanyId(entity.getUserWithCompany().getId());
|
||||
responseBean.setUserWithCompanyId(entity.getUserWithCompany() != null ? entity.getUserWithCompany().getId() : null);
|
||||
responseBean.setApplicationId(entity.getApplicationId());
|
||||
responseBean.setCreatedDate(entity.getCreatedDate());
|
||||
responseBean.setUpdatedDate(entity.getUpdatedDate());
|
||||
|
||||
@@ -271,25 +343,205 @@ public class CompanyDocumentDao {
|
||||
else if(type.equals(CompanyDocumentTypeEnum.PERSONAL_DOCUMENT)){
|
||||
userActionContext = UserActionContextEnum.UPLOAD_COMPANY_PERSONAL_DOCUMENT;
|
||||
}
|
||||
else if (type.equals(CompanyDocumentTypeEnum.APPLICATION_DOCUMENT)) {
|
||||
userActionContext = UserActionContextEnum.UPLOAD_COMPANY_DOCUMENT_TO_APPLICATION;
|
||||
}
|
||||
return userActionContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Beneficiary or confidi: duplicate into application storage. Super admin, pre-instructor (instructor), or
|
||||
* instructor manager: evaluation S3 folder + {@link ApplicationEvaluationEntity#getEvaluationDocument()} merge.
|
||||
* Other roles are not allowed.
|
||||
*/
|
||||
public DocumentResponseBean createDuplicateCompanyDocument(HttpServletRequest request , Long userId ,Long companyDocumentId , Long applicationId , DocumentTypeEnum documentTypeEnum){
|
||||
log.info("Creating duplicate of company document ID '{}' for application ID '{}'", companyDocumentId, applicationId);
|
||||
ApplicationEntity applicationEntity = applicationService.validateApplication(applicationId);
|
||||
CompanyDocumentEntity companyDocumentEntity = validateCompanyDocument(companyDocumentId);
|
||||
validator.validateUserWithCompany(request,companyDocumentEntity.getCompanyId());
|
||||
|
||||
boolean beneficiaryOrConfidi = Boolean.TRUE.equals(validator.checkIsBeneficiary())
|
||||
|| Boolean.TRUE.equals(validator.checkIsConfidi());
|
||||
boolean staffEvaluationRoles = Boolean.TRUE.equals(validator.checkIsSuperAdmin())
|
||||
|| Boolean.TRUE.equals(validator.checkIsPreInstructor())
|
||||
|| Boolean.TRUE.equals(validator.checkIsInstructorManager());
|
||||
|
||||
if (beneficiaryOrConfidi) {
|
||||
ApplicationEntity applicationEntity = applicationService.validateApplication(applicationId);
|
||||
validator.validateUserWithCompany(request, companyDocumentEntity.getCompanyId());
|
||||
|
||||
String companyDocumentPath = companyDocumentEntity.getFilePath();
|
||||
String documentPath = s3ConfigBean.generateDocumentPath(DocumentSourceTypeEnum.APPLICATION, applicationEntity.getCall().getId(), applicationId, 0L, 0L, 0L);
|
||||
|
||||
log.info("Original Paths - oldPath: {}, newPath: {}", companyDocumentPath, documentPath);
|
||||
|
||||
UploadFileOnAmazonS3Response response;
|
||||
try {
|
||||
response = amazonS3ServiceImpl.copyFile(companyDocumentEntity.getName(), companyDocumentPath, documentPath);
|
||||
} catch (Exception e) {
|
||||
log.error("Error occurred while uploading file from Amazon S3: {} for application ID '{}' and company Document ID '{}' ", e, applicationId, companyDocumentId);
|
||||
throw new CustomValidationException(Status.VALIDATION_ERROR,
|
||||
Translator.toLocale(GepafinConstant.UPLOAD_ERROR_S3));
|
||||
}
|
||||
|
||||
DocumentEntity entity = new DocumentEntity();
|
||||
entity.setFilePath(response.getFilePath());
|
||||
entity.setFileName(response.getFileName());
|
||||
entity.setSource(DocumentSourceTypeEnum.APPLICATION.getValue());
|
||||
entity.setType(documentTypeEnum.getValue());
|
||||
entity.setSourceId(applicationId);
|
||||
entity.setUploadedBy(userId);
|
||||
|
||||
documentRepository.save(entity);
|
||||
|
||||
/** This code is responsible for adding a version history log for the "inserting data" operation. **/
|
||||
loggingUtil.addVersionHistory(
|
||||
VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.INSERT).oldData(null).newData(entity).build());
|
||||
|
||||
return callDao.convertToDocumentResponseBean(entity);
|
||||
}
|
||||
|
||||
if (staffEvaluationRoles) {
|
||||
ApplicationEntity applicationEntity = applicationService.validateApplicationWithCompany(applicationId, companyDocumentEntity.getCompanyId());
|
||||
CompanyEntity companyEntity = companyService.validateCompany(companyDocumentEntity.getCompanyId());
|
||||
validator.validateHubId(request, companyEntity.getHub().getId());
|
||||
return addCompanyDocumentToEvaluation(applicationEntity, companyDocumentEntity, userId);
|
||||
}
|
||||
|
||||
throw new ForbiddenAccessException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PERMISSION_DENIED));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies one company document into the evaluation S3 folder, persists a {@link DocumentEntity} with source
|
||||
* {@link DocumentSourceTypeEnum#EVALUATION}, and appends the corresponding entry to
|
||||
* {@link ApplicationEvaluationEntity#getEvaluationDocument()} (same JSON shape as instructor upload).
|
||||
*/
|
||||
private DocumentResponseBean addCompanyDocumentToEvaluation(ApplicationEntity application,
|
||||
CompanyDocumentEntity companyDoc, Long userId) {
|
||||
Long evaluationId = application.getApplicationEvaluationId();
|
||||
if (evaluationId == null || evaluationId <= 0) {
|
||||
throw new CustomValidationException(Status.VALIDATION_ERROR,
|
||||
Translator.toLocale(GepafinConstant.COMPANY_DOCUMENT_COPY_EVALUATION_REQUIRES_APPLICATION_EVALUATION));
|
||||
}
|
||||
Optional<ApplicationEvaluationEntity> evaluationOpt =
|
||||
applicationEvaluationRepository.findByIdAndIsDeletedFalse(evaluationId);
|
||||
if (evaluationOpt.isEmpty()) {
|
||||
throw new CustomValidationException(Status.VALIDATION_ERROR,
|
||||
Translator.toLocale(GepafinConstant.APPLICATION_EVALUATION_NOT_FOUND, evaluationId));
|
||||
}
|
||||
|
||||
Long callId = application.getCall().getId();
|
||||
Long applicationId = application.getId();
|
||||
String evaluationFolderPath = s3ConfigBean.generateDocumentPath(
|
||||
DocumentSourceTypeEnum.EVALUATION, callId, applicationId, evaluationId, 0L, 0L);
|
||||
|
||||
DocumentEntity saved = persistInstructorCompanyDocumentAsEvaluationDocument(
|
||||
companyDoc, evaluationFolderPath, evaluationId, userId);
|
||||
EvaluationDocumentRequest entry = buildEvaluationDocumentEntry(companyDoc, saved);
|
||||
if (entry == null) {
|
||||
throw new CustomValidationException(Status.VALIDATION_ERROR,
|
||||
Translator.toLocale(GepafinConstant.UPLOAD_ERROR_S3));
|
||||
}
|
||||
|
||||
ApplicationEvaluationEntity evaluation = evaluationOpt.get();
|
||||
List<EvaluationDocumentRequest> merged = new ArrayList<>(parseEvaluationDocumentJson(evaluation.getEvaluationDocument()));
|
||||
merged.add(entry);
|
||||
evaluation.setEvaluationDocument(Utils.convertObjectToJson(merged));
|
||||
applicationEvaluationRepository.save(evaluation);
|
||||
|
||||
return callDao.convertToDocumentResponseBean(saved);
|
||||
}
|
||||
|
||||
private EvaluationDocumentRequest buildEvaluationDocumentEntry(CompanyDocumentEntity companyDoc, DocumentEntity saved) {
|
||||
if (saved == null || saved.getId() == null) {
|
||||
return null;
|
||||
}
|
||||
EvaluationDocumentRequest entry = new EvaluationDocumentRequest();
|
||||
SecureRandom random = new SecureRandom();
|
||||
entry.setFieldId(NanoIdUtils.randomNanoId(random, NanoIdUtils.DEFAULT_ALPHABET, 10));
|
||||
entry.setNameValue(instructorDocumentDisplayName(companyDoc));
|
||||
entry.setFileValue(String.valueOf(saved.getId()));
|
||||
entry.setValid(null);
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* For instructor uploads: copies each saved company document into the evaluation S3 folder, persists
|
||||
* {@link DocumentEntity} rows (source EVALUATION), and merges entries into the existing {@code EVALUATION_DOCUMENT}
|
||||
* column ({@link ApplicationEvaluationEntity#getEvaluationDocument()} JSON array of {@link EvaluationDocumentRequest}),
|
||||
* appending to any data already stored there.
|
||||
*/
|
||||
private void copyInstructorCompanyDocumentsToEvaluationDocuments(ApplicationEntity application,
|
||||
List<CompanyDocumentEntity> instructorCompanyDocuments, Long userId) {
|
||||
if (instructorCompanyDocuments == null || instructorCompanyDocuments.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Long evaluationId = application.getApplicationEvaluationId();
|
||||
if (evaluationId == null || evaluationId <= 0) {
|
||||
log.debug("Skip evaluation document copy for applicationId={}: no applicationEvaluationId set", application.getId());
|
||||
return;
|
||||
}
|
||||
Optional<ApplicationEvaluationEntity> evaluationOpt = applicationEvaluationRepository.findByIdAndIsDeletedFalse(evaluationId);
|
||||
if (evaluationOpt.isEmpty()) {
|
||||
log.warn("Skip evaluation document copy: applicationEvaluationId={} not found or deleted", evaluationId);
|
||||
return;
|
||||
}
|
||||
|
||||
Long callId = application.getCall().getId();
|
||||
Long applicationId = application.getId();
|
||||
String evaluationFolderPath = s3ConfigBean.generateDocumentPath(
|
||||
DocumentSourceTypeEnum.EVALUATION, callId, applicationId, evaluationId, 0L, 0L);
|
||||
|
||||
List<EvaluationDocumentRequest> newEntries = new ArrayList<>();
|
||||
for (CompanyDocumentEntity companyDoc : instructorCompanyDocuments) {
|
||||
DocumentEntity saved = persistInstructorCompanyDocumentAsEvaluationDocument(
|
||||
companyDoc, evaluationFolderPath, evaluationId, userId);
|
||||
EvaluationDocumentRequest entry = buildEvaluationDocumentEntry(companyDoc, saved);
|
||||
if (entry != null) {
|
||||
newEntries.add(entry);
|
||||
}
|
||||
}
|
||||
if (newEntries.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ApplicationEvaluationEntity evaluation = evaluationOpt.get();
|
||||
List<EvaluationDocumentRequest> merged = new ArrayList<>(parseEvaluationDocumentJson(evaluation.getEvaluationDocument()));
|
||||
merged.addAll(newEntries);
|
||||
evaluation.setEvaluationDocument(Utils.convertObjectToJson(merged));
|
||||
applicationEvaluationRepository.save(evaluation);
|
||||
}
|
||||
|
||||
private static List<EvaluationDocumentRequest> parseEvaluationDocumentJson(String json) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<EvaluationDocumentRequest> parsed = Utils.convertJsonToList(json,
|
||||
new TypeReference<List<EvaluationDocumentRequest>>() {});
|
||||
return parsed != null ? new ArrayList<>(parsed) : new ArrayList<>();
|
||||
}
|
||||
|
||||
|
||||
private static String instructorDocumentDisplayName(CompanyDocumentEntity companyDoc) {
|
||||
if (companyDoc == null) {
|
||||
return "";
|
||||
}
|
||||
if (StringUtils.isNotBlank(companyDoc.getName())) {
|
||||
return companyDoc.getName().trim();
|
||||
}
|
||||
return StringUtils.defaultString(companyDoc.getFileName());
|
||||
}
|
||||
|
||||
private DocumentEntity persistInstructorCompanyDocumentAsEvaluationDocument(CompanyDocumentEntity companyDocumentEntity,
|
||||
String evaluationDocumentFolderPath, Long evaluationId, Long userId) {
|
||||
String companyDocumentPath = companyDocumentEntity.getFilePath();
|
||||
String documentPath = s3ConfigBean.generateDocumentPath(DocumentSourceTypeEnum.APPLICATION,applicationEntity.getCall().getId(),applicationId,0L,0L,0L);
|
||||
|
||||
log.info("Original Paths - oldPath: {}, newPath: {}", companyDocumentPath, documentPath);
|
||||
log.info("Instructor evaluation copy: companyDocumentId={}, oldPath={}, evaluationFolder={}",
|
||||
companyDocumentEntity.getId(), companyDocumentPath, evaluationDocumentFolderPath);
|
||||
|
||||
UploadFileOnAmazonS3Response response;
|
||||
try {
|
||||
response = amazonS3ServiceImpl.copyFile(companyDocumentEntity.getName(), companyDocumentPath, documentPath);
|
||||
response = amazonS3ServiceImpl.copyFile(companyDocumentEntity.getName(), companyDocumentPath, evaluationDocumentFolderPath);
|
||||
} catch (Exception e) {
|
||||
log.error("Error occurred while uploading file from Amazon S3: {} for application ID '{}' and company Document ID '{}' ", e,applicationId,companyDocumentId);
|
||||
log.error("Error copying instructor company document id={} to evaluation folder: {}",
|
||||
companyDocumentEntity.getId(), e.getMessage(), e);
|
||||
throw new CustomValidationException(Status.VALIDATION_ERROR,
|
||||
Translator.toLocale(GepafinConstant.UPLOAD_ERROR_S3));
|
||||
}
|
||||
@@ -297,27 +549,27 @@ public class CompanyDocumentDao {
|
||||
DocumentEntity entity = new DocumentEntity();
|
||||
entity.setFilePath(response.getFilePath());
|
||||
entity.setFileName(response.getFileName());
|
||||
entity.setSource(DocumentSourceTypeEnum.APPLICATION.getValue());
|
||||
entity.setType(documentTypeEnum.getValue());
|
||||
entity.setSourceId(applicationId);
|
||||
entity.setSource(DocumentSourceTypeEnum.EVALUATION.getValue());
|
||||
entity.setType(DocumentTypeEnum.DOCUMENT.getValue());
|
||||
entity.setSourceId(evaluationId);
|
||||
entity.setUploadedBy(userId);
|
||||
|
||||
documentRepository.save(entity);
|
||||
|
||||
/** This code is responsible for adding a version history log for the "inserting data" operation. **/
|
||||
loggingUtil.addVersionHistory(
|
||||
VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.INSERT).oldData(null).newData(entity).build());
|
||||
|
||||
DocumentResponseBean responseBean = callDao.convertToDocumentResponseBean(entity);
|
||||
return responseBean;
|
||||
return entity;
|
||||
}
|
||||
|
||||
public List<CompanyDocumentResponseBean> getAllCompanyDocument(UserEntity user , Long companyId, CompanyDocumentTypeEnum typeEnum){
|
||||
log.info("Fetching all company documents for Company ID '{}', User ID '{}', Type '{}'", companyId, user.getId(), typeEnum);
|
||||
if(Boolean.TRUE.equals(validator.checkIsBeneficiary())) {
|
||||
validator.validateUserWithCompany(request, companyId);
|
||||
CompanyEntity companyEntity = companyService.validateCompany(companyId);
|
||||
if (Boolean.TRUE.equals(validator.checkIsBeneficiary())) {
|
||||
if (typeEnum == CompanyDocumentTypeEnum.PERSONAL_DOCUMENT || typeEnum == null) {
|
||||
validator.validateUserWithCompany(request, companyId);
|
||||
} else {
|
||||
validator.validateHubId(request, companyEntity.getHub().getId());
|
||||
}
|
||||
}
|
||||
companyService.validateCompany(companyId);
|
||||
|
||||
Specification<CompanyDocumentEntity> spec = filterCompanyDocuments(companyId, user.getId(), typeEnum);
|
||||
|
||||
@@ -345,6 +597,9 @@ public class CompanyDocumentDao {
|
||||
// Case 1: Fetch only COMPANY_DOCUMENT type documents for the given company
|
||||
predicate = builder.and(predicate, builder.equal(root.get("type"), CompanyDocumentTypeEnum.COMPANY_DOCUMENT.getValue()));
|
||||
|
||||
} else if (typeEnum == CompanyDocumentTypeEnum.APPLICATION_DOCUMENT) {
|
||||
predicate = builder.and(predicate, builder.equal(root.get("type"), CompanyDocumentTypeEnum.APPLICATION_DOCUMENT.getValue()));
|
||||
|
||||
} else if (typeEnum == CompanyDocumentTypeEnum.PERSONAL_DOCUMENT) {
|
||||
// Case 2: Fetch only PERSONAL_DOCUMENT type documents for the logged-in user
|
||||
predicate = builder.and(
|
||||
@@ -353,20 +608,28 @@ public class CompanyDocumentDao {
|
||||
builder.equal(root.get("userWithCompany").get("userId"), userId)
|
||||
);
|
||||
}
|
||||
}else {
|
||||
// Case 3: If typeEnum is null, fetch all documents for the company and personal documents for the user
|
||||
Predicate companyPredicate = builder.equal(root.get("companyId"), companyId);
|
||||
Predicate personalPredicate = builder.and(
|
||||
builder.equal(root.get("type"), CompanyDocumentTypeEnum.PERSONAL_DOCUMENT.getValue()),
|
||||
builder.equal(root.get("userWithCompany").get("userId"), userId)
|
||||
} else {
|
||||
|
||||
Predicate companyAndApplicationDocs = root.get("type").in(
|
||||
CompanyDocumentTypeEnum.COMPANY_DOCUMENT.getValue(),
|
||||
CompanyDocumentTypeEnum.APPLICATION_DOCUMENT.getValue()
|
||||
);
|
||||
predicate = builder.and(predicate, builder.or(companyPredicate, personalPredicate));
|
||||
predicate = builder.and(predicate, companyAndApplicationDocs);
|
||||
}
|
||||
return predicate;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
public List<CompanyDocumentResponseBean> listApplicationCompanyDocumentsForEvaluation(Long applicationId, Long companyId) {
|
||||
if (applicationId == null || companyId == null) {
|
||||
return List.of();
|
||||
}
|
||||
List<CompanyDocumentEntity> entities = companyDocumentRepository.findByApplicationIdAndCompanyIdAndTypeAndStatusNot(
|
||||
applicationId,
|
||||
companyId,
|
||||
CompanyDocumentTypeEnum.APPLICATION_DOCUMENT.getValue(),
|
||||
CompanyDocumentStatusEnum.EXPIRED.getValue());
|
||||
return entities.stream().map(this::convertToCompanyDocumentResponseBean).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.springframework.stereotype.Component;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -423,15 +424,40 @@ public class NotificationDao {
|
||||
}
|
||||
|
||||
public void sendNotificationToBeneficiaryForDocumentExpiration(CompanyDocumentEntity companyDocumentEntity, NotificationTypeEnum notificationTypeEnum) {
|
||||
|
||||
CompanyEntity companyEntity = companyService.validateCompany(companyDocumentEntity.getCompanyId());
|
||||
Long companyId = companyDocumentEntity.getCompanyId();
|
||||
if (companyId == null) {
|
||||
log.warn("Skipping document expiration notification: companyId is null for document id {}", companyDocumentEntity.getId());
|
||||
return;
|
||||
}
|
||||
CompanyEntity companyEntity = companyService.validateCompany(companyId);
|
||||
List<UserWithCompanyEntity> linkedUsers = userWithCompanyRepository.findByCompanyIdAndIsDeletedFalse(companyId);
|
||||
if (linkedUsers == null || linkedUsers.isEmpty()) {
|
||||
log.warn("Skipping document expiration notification: no users linked to company {} for document id {}", companyId, companyDocumentEntity.getId());
|
||||
return;
|
||||
}
|
||||
Map<String, String> placeHolders = new HashMap<>();
|
||||
placeHolders.put("{{file_name}}", companyDocumentEntity.getFileName());
|
||||
placeHolders.put("{{company_name}}", companyEntity.getCompanyName());
|
||||
placeHolders.put("{{expiration_date}}", companyDocumentEntity.getExpirationDate().toString());
|
||||
NotificationReq notificationReq = createNotificationReq(notificationTypeEnum.getValue(), placeHolders, companyDocumentEntity.getUserWithCompany().getUserId(), companyDocumentEntity.getUserWithCompany(),
|
||||
listOf(companyDocumentEntity.getCompanyId()));
|
||||
sendNotification(notificationReq);
|
||||
Map<Long, UserWithCompanyEntity> oneRowPerUser = new LinkedHashMap<>();
|
||||
for (UserWithCompanyEntity uwc : linkedUsers) {
|
||||
if (uwc.getUserId() != null) {
|
||||
oneRowPerUser.putIfAbsent(uwc.getUserId(), uwc);
|
||||
}
|
||||
}
|
||||
if (oneRowPerUser.isEmpty()) {
|
||||
log.warn("Skipping document expiration notification: no user ids on USER_WITH_COMPANY for company {}, document id {}", companyId, companyDocumentEntity.getId());
|
||||
return;
|
||||
}
|
||||
for (UserWithCompanyEntity uwc : oneRowPerUser.values()) {
|
||||
NotificationReq notificationReq = createNotificationReq(
|
||||
notificationTypeEnum.getValue(),
|
||||
placeHolders,
|
||||
uwc.getUserId(),
|
||||
uwc,
|
||||
listOf(companyId));
|
||||
sendNotification(notificationReq);
|
||||
}
|
||||
}
|
||||
|
||||
public PageableResponseBean<List<NotificationResponse>> getNotificationsByUserIdAndCompanyIdByPagination(Long userId, Long companyId, NotificationRequestBean notificationRequestBean) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -91,5 +91,7 @@ public class ApplicationView implements Serializable {
|
||||
@Column(name = "CREATED_DATE")
|
||||
private LocalDateTime createdDate;
|
||||
|
||||
@Column(name = "NDG")
|
||||
private String ndg;
|
||||
|
||||
}
|
||||
|
||||
@@ -102,5 +102,8 @@ public class CallEntity extends BaseEntity {
|
||||
|
||||
@Column(name = "allow_multiple_applications")
|
||||
private Boolean allowMultipleApplications;
|
||||
|
||||
@Column(name = "ranking_type")
|
||||
private String rankingType;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,5 +44,7 @@ public class CompanyDocumentEntity extends BaseEntity {
|
||||
@JoinColumn(name = "DOCUMENT_CATEGORY_ID")
|
||||
private DocumentCategoryEntity categoryEntity;
|
||||
|
||||
@Column(name = "APPLICATION_ID")
|
||||
private Long applicationId;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,9 @@ public enum ApplicationStatusForEvaluation {
|
||||
ADMISSIBLE("ADMISSIBLE"),
|
||||
TECHNICAL_EVALUATION("TECHNICAL_EVALUATION"),
|
||||
AWAITING_TECHNICAL_EVALUATION("AWAITING_TECHNICAL_EVALUATION"),
|
||||
TECHNICAL_EVALUATION_REJECTED("TECHNICAL_EVALUATION_REJECTED");
|
||||
TECHNICAL_EVALUATION_REJECTED("TECHNICAL_EVALUATION_REJECTED"),
|
||||
DELETED("DELETED"),
|
||||
DELETED_CONFIRMED("DELETED_CONFIRMED");;
|
||||
|
||||
private String value;
|
||||
|
||||
|
||||
@@ -20,7 +20,9 @@ public enum ApplicationStatusTypeEnum {
|
||||
TECHNICAL_EVALUATION_REJECTED("TECHNICAL_EVALUATION_REJECTED"),
|
||||
AWAITING_TECHNICAL_EVALUATION("AWAITING_TECHNICAL_EVALUATION"),
|
||||
AWAITING_CONTRACT("AWAITING_CONTRACT"),
|
||||
CONTRACT_SIGNED("CONTRACT_SIGNED");
|
||||
CONTRACT_SIGNED("CONTRACT_SIGNED"),
|
||||
DELETED("DELETED"),
|
||||
DELETED_CONFIRMED("DELETED_CONFIRMED");
|
||||
|
||||
private String value;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,8 @@ package net.gepafin.tendermanagement.enums;
|
||||
|
||||
public enum CompanyDocumentTypeEnum {
|
||||
COMPANY_DOCUMENT("COMPANY_DOCUMENT"),
|
||||
PERSONAL_DOCUMENT("PERSONAL_DOCUMENT");
|
||||
PERSONAL_DOCUMENT("PERSONAL_DOCUMENT"),
|
||||
APPLICATION_DOCUMENT("APPLICATION_DOCUMENT");
|
||||
|
||||
private String value;
|
||||
|
||||
|
||||
@@ -204,6 +204,7 @@ public enum UserActionContextEnum {
|
||||
GET_COMPANY_DOCUMENT("GET_COMPANY_DOCUMENT"),
|
||||
DELETE_COMPANY_DOCUMENT("DELETE_COMPANY_DOCUMENT"),
|
||||
UPLOAD_COMPANY_DOCUMENT("UPLOAD_COMPANY_DOCUMENT"),
|
||||
UPLOAD_COMPANY_APPLICATION_DOCUMENT("UPLOAD_COMPANY_APPLICATION_DOCUMENT"),
|
||||
UPLOAD_COMPANY_PERSONAL_DOCUMENT("UPLOAD_COMPANY_PERSONAL_DOCUMENT"),
|
||||
GET_ALL_COMPANY_DOCUMENT("GET_ALL_COMPANY_DOCUMENT"),
|
||||
UPDATE_COMPANY_DOCUMENT("UPDATE_COMPANY_DOCUMENT"),
|
||||
@@ -235,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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ public class ApplicationEvaluationFormResponse {
|
||||
private ApplicationEvaluationFormResponseBean applicationEvaluationFormResponse;
|
||||
private List<FieldResponse> files;
|
||||
private List<EvaluationDocumentResponse> evaluationDocument;
|
||||
private List<CompanyDocumentResponseBean> applicationCompanyDocuments;
|
||||
private List<AmendmentDocumentResponseBean> amendmentDetails;
|
||||
private LocalDateTime createdDate;
|
||||
private LocalDateTime updatedDate;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -52,4 +53,10 @@ public class ApplicationResponse{
|
||||
|
||||
private EvaluationVersionEnum evaluationVersion;
|
||||
|
||||
private String ndg;
|
||||
|
||||
private ApplicationRankingActionTypeEnum rankingActionType;
|
||||
|
||||
private Long manualRanking;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ public class CompanyDocumentResponseBean extends BaseBean {
|
||||
|
||||
private Long userWithCompanyId;
|
||||
|
||||
private Long applicationId;
|
||||
|
||||
private DocumentCategoryResponse category;
|
||||
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -37,5 +37,7 @@ public interface CompanyDocumentRepository extends JpaRepository<CompanyDocument
|
||||
|
||||
List<CompanyDocumentEntity> findByIdInAndIsDeletedFalseAndStatusNot(List<Long> ids, String status);
|
||||
|
||||
List<CompanyDocumentEntity> findByApplicationIdAndCompanyIdAndTypeAndStatusNot(
|
||||
Long applicationId, Long companyId, String type, String status);
|
||||
|
||||
}
|
||||
|
||||
@@ -20,4 +20,6 @@ public interface UserWithCompanyRepository extends JpaRepository<UserWithCompany
|
||||
@Query("SELECT u FROM UserWithCompanyEntity u WHERE u.userId = :userId AND u.companyId = :companyId AND u.isDeleted = false")
|
||||
UserWithCompanyEntity findByUserIdAndCompanyIdAndIsDeletedFalseForNotification(Long userId, Long companyId);
|
||||
|
||||
List<UserWithCompanyEntity> findByCompanyIdAndIsDeletedFalse(Long companyId);
|
||||
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public class CompanyDocumentExpirationScheduler {
|
||||
@Autowired
|
||||
private ExpirationConfigRepository expirationConfigRepository;
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ExpirationScheduler.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(CompanyDocumentExpirationScheduler.class);
|
||||
|
||||
@Scheduled(cron = "0 0 4 * * ?")
|
||||
public void processDocumentExpiration(){
|
||||
|
||||
@@ -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;
|
||||
@@ -34,6 +35,8 @@ public interface ApplicationService {
|
||||
|
||||
public ApplicationResponse updateApplicationStatus(HttpServletRequest request, Long applicationId, ApplicationStatusTypeEnum status);
|
||||
|
||||
ApplicationResponse recordApplicationRegistrySegment(HttpServletRequest request, Long applicationId,ApplicationStatusTypeEnum status);
|
||||
|
||||
public ApplicationSignedDocumentResponse uploadSignedDocument(HttpServletRequest request, Long applicationId, MultipartFile file);
|
||||
|
||||
public ApplicationSignedDocumentResponse getSignedDocument(HttpServletRequest request, Long applicationId);
|
||||
@@ -55,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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,6 @@ public interface CompanyDocumentService {
|
||||
|
||||
List<CompanyDocumentResponseBean> getAllCompanyDocument(HttpServletRequest request ,Long companyId , CompanyDocumentTypeEnum typeEnum);
|
||||
|
||||
|
||||
List<CompanyDocumentResponseBean> uploadInstructorCompanyDocumentToApplication(HttpServletRequest request, List<MultipartFile> files, Long companyId, Long applicationId, Long documentCategoryId, LocalDateTime expirationDate, String name);
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -107,6 +108,13 @@ public class ApplicationServiceImpl implements ApplicationService {
|
||||
return applicationDao.updateApplicationStatus(request, applicationId, status);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ApplicationResponse recordApplicationRegistrySegment(HttpServletRequest request, Long applicationId,ApplicationStatusTypeEnum status) {
|
||||
UserEntity userEntity = validator.validateUser(request);
|
||||
return applicationDao.recordApplicationRegistrySegment(request, applicationId, status);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<ApplicationResponse> getAllApplications(HttpServletRequest request, Long callId, Long companyId ,List<ApplicationStatusTypeEnum> statusList) {
|
||||
@@ -188,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.gepafin.tendermanagement.service.impl;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import net.gepafin.tendermanagement.dao.CompanyDocumentDao;
|
||||
import net.gepafin.tendermanagement.entities.CompanyEntity;
|
||||
import net.gepafin.tendermanagement.entities.UserEntity;
|
||||
import net.gepafin.tendermanagement.enums.CompanyDocumentTypeEnum;
|
||||
import net.gepafin.tendermanagement.enums.DocumentTypeEnum;
|
||||
@@ -9,6 +10,7 @@ import net.gepafin.tendermanagement.model.request.CompanyDocumentRequest;
|
||||
import net.gepafin.tendermanagement.model.response.CompanyDocumentResponseBean;
|
||||
import net.gepafin.tendermanagement.model.response.DocumentResponseBean;
|
||||
import net.gepafin.tendermanagement.service.CompanyDocumentService;
|
||||
import net.gepafin.tendermanagement.service.CompanyService;
|
||||
import net.gepafin.tendermanagement.util.Utils;
|
||||
import net.gepafin.tendermanagement.util.Validator;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -28,6 +30,9 @@ public class CompanyDocumentServiceImpl implements CompanyDocumentService {
|
||||
@Autowired
|
||||
private CompanyDocumentDao companyDocumentDao;
|
||||
|
||||
@Autowired
|
||||
private CompanyService companyService;
|
||||
|
||||
@Override
|
||||
public List<CompanyDocumentResponseBean> uploadFileForCompany(HttpServletRequest request, List<MultipartFile> files, Long companyId, Long documentCategoryId , CompanyDocumentTypeEnum documentSourceTypeEnum, LocalDateTime expirationDate,String name) {
|
||||
Map<String, Object> userInfo = validator.getUserInfoFromToken(request);
|
||||
@@ -37,6 +42,17 @@ public class CompanyDocumentServiceImpl implements CompanyDocumentService {
|
||||
return companyDocumentDao.uploadFileForCompany(userId,files,companyId,documentCategoryId,documentSourceTypeEnum,expirationDate,name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CompanyDocumentResponseBean> uploadInstructorCompanyDocumentToApplication(HttpServletRequest request, List<MultipartFile> files, Long companyId, Long applicationId, Long documentCategoryId, LocalDateTime expirationDate, String name) {
|
||||
Map<String, Object> userInfo = validator.getUserInfoFromToken(request);
|
||||
Long userId = validator.getUserId(userInfo);
|
||||
files.forEach(Utils::validateFileType);
|
||||
validator.validateUser(request);
|
||||
CompanyEntity company = companyService.validateCompany(companyId);
|
||||
validator.validateHubId(request, company.getHub().getId());
|
||||
return companyDocumentDao.uploadInstructorCompanyDocumentToApplication(userId, files, companyId, applicationId, documentCategoryId, expirationDate, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompanyDocumentResponseBean updateCompanyDocument(HttpServletRequest request, Long companyDocumentId, CompanyDocumentRequest companyDocumentRequest) {
|
||||
validator.validateUser(request);
|
||||
|
||||
@@ -197,6 +197,26 @@ public class Validator {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Boolean checkIsDirector() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication != null && authentication.isAuthenticated()) {
|
||||
for (GrantedAuthority authority : authentication.getAuthorities()) {
|
||||
if (RoleStatusEnum.ROLE_DIRECTOR.getValue().equals(authority.getAuthority())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Super admin (any hub) or director (hub must match application — enforced separately). */
|
||||
public void validateSuperAdminOrDirector() {
|
||||
if (Boolean.TRUE.equals(checkIsSuperAdmin()) || Boolean.TRUE.equals(checkIsDirector())) {
|
||||
return;
|
||||
}
|
||||
throw new ForbiddenAccessException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PERMISSION_DENIED));
|
||||
}
|
||||
public Boolean checkIsConfidi() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication != null && authentication.isAuthenticated()) {
|
||||
|
||||
@@ -206,7 +206,7 @@ public interface ApplicationAmendmentRequestApi {
|
||||
@PostMapping(value = "/user/{userId}/pagination", produces = { "application/json" })
|
||||
ResponseEntity<Response<PageableResponseBean<List<ApplicationAmendmentRequestViewResponse>>>> getApplicationAmendmentByPaginnation(HttpServletRequest request, @Parameter(description = "The user id", required = true) @PathVariable("userId") Long userId,
|
||||
@RequestBody ApplicationAmendmentPaginationRequestBean userActionPaginationRequest);
|
||||
@Operation(summary = "Api to create special Amendment",
|
||||
@Operation(summary = "Api to create special Amendment new",
|
||||
responses = {
|
||||
@ApiResponse(responseCode = "200", description = "OK"),
|
||||
@ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
|
||||
|
||||
@@ -132,6 +132,21 @@ public interface ApplicationApi {
|
||||
@Parameter(description = "The application id", required = true) @PathVariable("applicationId") Long applicationId,
|
||||
@Parameter(description = "status", required = true)@RequestParam(value = "status", required = true) ApplicationStatusTypeEnum status);
|
||||
|
||||
@Operation(summary = "Api to update application external status",
|
||||
responses = {
|
||||
@ApiResponse(responseCode = "200", description = "OK"),
|
||||
@ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
|
||||
@ExampleObject(value = ErrorConstants.NOTFOUND_ERROR_EXAMPLE) })),
|
||||
@ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
|
||||
@ExampleObject(value = ErrorConstants.UNAUTHORIZED_ERROR_EXAMPLE) })),
|
||||
@ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
|
||||
@ExampleObject(value = ErrorConstants.BADREQUEST_ERROR_EXAMPLE) })) })
|
||||
@PatchMapping(value = "/{applicationId}/external/status", produces = { "application/json" })
|
||||
@PreAuthorize("hasRole('ROLE_SUPER_ADMIN') || hasRole('ROLE_DIRECTOR')")
|
||||
ResponseEntity<Response<ApplicationResponse>> updateApplicationSegment(HttpServletRequest request,
|
||||
@Parameter(description = "The application id", required = true) @PathVariable("applicationId") Long applicationId,
|
||||
@Parameter(description = "Application status", required = true) @RequestParam ApplicationStatusTypeEnum status);
|
||||
|
||||
@Operation(summary = "API to generate PDF for an application",
|
||||
responses = {
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(mediaType = "application/pdf")),
|
||||
@@ -234,7 +249,7 @@ public interface ApplicationApi {
|
||||
@ExampleObject(value = ErrorConstants.BADREQUEST_ERROR_EXAMPLE)}))
|
||||
})
|
||||
@GetMapping(value = "/call/{callId}/csv")
|
||||
@PreAuthorize("hasRole('ROLE_SUPER_ADMIN')")
|
||||
@PreAuthorize("hasRole('ROLE_SUPER_ADMIN')|| hasRole('ROLE_INSTRUCTOR_MANAGER')")
|
||||
public ResponseEntity<byte[]> exportCsv(
|
||||
HttpServletRequest request, @Parameter(description = "The call id", required = true) @PathVariable(value = "callId", required = true) Long callId);
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.http.HttpStatus;
|
||||
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.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
@@ -48,6 +49,27 @@ public interface CompanyDocumentApi {
|
||||
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_SUPER_ADMIN') || hasRole('ROLE_INSTRUCTOR_MANAGER') || hasRole('ROLE_PRE_INSTRUCTOR')")
|
||||
@Operation(summary = "API to upload company document for an application (Only for instructor)",
|
||||
responses = {
|
||||
@ApiResponse(responseCode = "200", description = "OK"),
|
||||
@ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
|
||||
@ExampleObject(value = ErrorConstants.NOTFOUND_ERROR_EXAMPLE) })),
|
||||
@ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
|
||||
@ExampleObject(value = ErrorConstants.UNAUTHORIZED_ERROR_EXAMPLE) })),
|
||||
@ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
|
||||
@ExampleObject(value = ErrorConstants.BADREQUEST_ERROR_EXAMPLE) }))})
|
||||
@PostMapping(value = "company/{companyId}/application/{applicationId}/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
default ResponseEntity<Response<List<CompanyDocumentResponseBean>>> uploadInstructorCompanyDocumentToApplication(HttpServletRequest httpServletRequest,
|
||||
@Parameter(description = "Company Id", required = true) @PathVariable("companyId") Long companyId,
|
||||
@Parameter(description = "Application Id", required = true) @PathVariable("applicationId") Long applicationId,
|
||||
@Parameter(description = "Document category id", required = true) @RequestParam(value = "documentCategoryId") Long documentCategoryId,
|
||||
@Parameter(description = "Display name", required = true) @RequestParam(value = "name") String name,
|
||||
@Parameter(description = "Expiration date (ISO-8601)", required = true) @RequestParam("expirationDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime expirationDate,
|
||||
@RequestParam("file") List<MultipartFile> files) {
|
||||
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
@Operation(summary = "Api to update company document",
|
||||
responses = {
|
||||
@ApiResponse(responseCode = "200", description = "OK"),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -22,6 +22,8 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
@@ -141,6 +143,18 @@ public class ApplicationApiController implements ApplicationApi {
|
||||
.body(new Response<>(applicationResponse, Status.SUCCESS, Translator.toLocale(GepafinConstant.APPLICATION_STATUS_UPDATED_SUCCESSFULLY)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Response<ApplicationResponse>> updateApplicationSegment(HttpServletRequest request, Long applicationId,
|
||||
ApplicationStatusTypeEnum status) {
|
||||
|
||||
loggingUtil.logUserAction(
|
||||
UserActionRequest.builder().request(request).actionType(UserActionLogsEnum.UPDATE).actionContext(UserActionContextEnum.UPDATE_APPLICATION_STATUS).build());
|
||||
|
||||
ApplicationResponse applicationResponse = applicationService.recordApplicationRegistrySegment(request, applicationId, status);
|
||||
return ResponseEntity.status(HttpStatus.OK)
|
||||
.body(new Response<>(applicationResponse, Status.SUCCESS, Translator.toLocale(GepafinConstant.APPLICATION_STATUS_UPDATED_SUCCESSFULLY)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<byte[]> generateApplicationPdf(HttpServletRequest request, Long applicationId) {
|
||||
|
||||
|
||||
@@ -57,6 +57,14 @@ public class CompanyDocumentApiControlller implements CompanyDocumentApi {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Response<List<CompanyDocumentResponseBean>>> uploadInstructorCompanyDocumentToApplication(HttpServletRequest request, Long companyId, Long applicationId, Long documentCategoryId, String name, LocalDateTime expirationDate, List<MultipartFile> files) {
|
||||
loggingUtil.logUserAction(UserActionRequest.builder().request(request).actionType(UserActionLogsEnum.UPLOAD).actionContext(UserActionContextEnum.UPLOAD_COMPANY_DOCUMENT_TO_APPLICATION).build());
|
||||
List<CompanyDocumentResponseBean> responseBeans = companyDocumentService.uploadInstructorCompanyDocumentToApplication(request, files, companyId, applicationId, documentCategoryId, expirationDate, name);
|
||||
return ResponseEntity.status(HttpStatus.CREATED)
|
||||
.body(new Response<>(responseBeans, Status.SUCCESS, Translator.toLocale(GepafinConstant.FILES_UPLOADED_MSG)));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Response<CompanyDocumentResponseBean>> updateCompanyDocument(HttpServletRequest httpServletRequest, Long companyDocumentId, CompanyDocumentRequest companyDocumentRequest) {
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
# DataSource Configuration
|
||||
spring.datasource.url=jdbc:postgresql://localhost:5432/gepafin_local
|
||||
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
|
||||
spring.datasource.username=postgres
|
||||
spring.datasource.password=root
|
||||
spring.datasource.driver-class-name=org.postgresql.Driver
|
||||
|
||||
@@ -77,6 +77,9 @@ spring.rabbitmq.connection-timeout=120000
|
||||
app.bandi.login.url.suffix=/loginadmin
|
||||
app.confidi.login.url.suffix=/confidi
|
||||
|
||||
excel.fill.api.url=https://excel-gepafin-dev-be.bflows.ai/excel/fill
|
||||
excel.fill.api.token=a3f8c2d1e4b5a6f7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1
|
||||
|
||||
#sviluppumbria protocol
|
||||
codAoo=SVILUMBRIA-01
|
||||
sviluppumbria.username=protocollatoresvilumbria
|
||||
|
||||
@@ -3205,7 +3205,81 @@
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
|
||||
<changeSet id="16-03-2026_RK_153512" author="Rajesh Khore">
|
||||
<sqlFile dbms="postgresql" path="db/dump/update_system_email_template_special_amendment_16_03_2026.sql"/>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="23-03-2026_NK_174724" author="Rajesh Khore">
|
||||
<sqlFile dbms="postgresql" path="db/dump/update_application_view_23_03_2026.sql"/>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="24-03-2026_RK_191723" author="Rajesh Khore">
|
||||
<addColumn tableName="company_document">
|
||||
<column name="application_id" type="INTEGER"/>
|
||||
</addColumn>
|
||||
<dropNotNullConstraint tableName="company_document" columnName="user_with_company_id" columnDataType="INTEGER"/>
|
||||
</changeSet>
|
||||
<changeSet id="25-03-2026_NK_170536" author="Rajesh Khore">
|
||||
<sqlFile dbms="postgresql" path="db/dump/insert_form_field_25_03_2026.sql"/>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="07-04-2026_ranking_columns" author="GEPAFIN">
|
||||
<addColumn tableName="call">
|
||||
<column name="ranking_type" type="VARCHAR(64)"/>
|
||||
</addColumn>
|
||||
<addColumn tableName="application">
|
||||
<column name="ranking_action_type" type="VARCHAR(64)"/>
|
||||
<column name="manual_ranking" type="BIGINT"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
<changeSet id="07-04-2026_RK_145623" author="Rajesh Khore">
|
||||
<sqlFile path="db/dump/create_application_ranking_view_07_04_2026.sql"/>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="09-04-2026_RK_115923" author="Rajesh Khore">
|
||||
<sqlFile dbms="postgresql" path="db/dump/update_email_config_for_mailgun_09_04_2026.sql" />
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="09-04-2026_RK_143457" author="Rajesh Khore">
|
||||
<sqlFile dbms="postgresql" path="db/dump/update_system_email_template_09_04_2026.sql" />
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="09-04-2026_RK_174653" author="Rajesh Khore">
|
||||
<update tableName="role">
|
||||
<column name="permissions" value="MANAGE_SUBSEQUENT_PHASES"/>
|
||||
<where>role_type='ROLE_GEPAFIN_OPERATOR'</where>
|
||||
</update>
|
||||
|
||||
<update tableName="role">
|
||||
<column name="permissions" value="VIEW_CALLS,APPLY_CALLS"/>
|
||||
<where>role_type='ROLE_BENEFICIARY'</where>
|
||||
</update>
|
||||
|
||||
<update tableName="role">
|
||||
<column name="permissions" value="EVALUATE_APPLICATIONS,MANAGE_PRELIMINARY_RELIEF"/>
|
||||
<where>role_type='ROLE_PRE_INSTRUCTOR'</where>
|
||||
</update>
|
||||
|
||||
<update tableName="role">
|
||||
<column name="permissions" value="EVALUATE_APPLICATIONS,MANAGE_PRELIMINARY_RELIEF,ASSIGED_APPLICATION"/>
|
||||
<where>role_type='ROLE_INSTRUCTOR_MANAGER'</where>
|
||||
</update>
|
||||
|
||||
<update tableName="role">
|
||||
<column name="permissions" value="VIEW_CONFIDI_CALLS,APPLY_CONFIDI_CALLS"/>
|
||||
<where>role_type='ROLE_CONFIDI'</where>
|
||||
</update>
|
||||
|
||||
<update tableName="role">
|
||||
<column name="permissions" value="ROOT_MANAGE_APPL_VIEW_DELETED,ROOT_MANAGE_APPL_DELETE_CONFIRM"/>
|
||||
<where>role_type='ROLE_DIRECTOR'</where>
|
||||
</update>
|
||||
|
||||
<update tableName="role">
|
||||
<column name="permissions" value="MANAGE_TENDERS,MANAGE_USERS,ROOT_MANAGE_NDG,ROOT_MANAGE_APPL_STATUS,ROOT_MANAGE_AMENDMENT_REOPEN,ROOT_MANAGE_AMENDMENT_EXTEND,ROOT_MANAGE_APPL_VIEW_DELETED,ROOT_MANAGE_APPL_DELETE,ROOT_MANAGE_PEC_SEND,ROOT_MANAGE_VIEW_LOG,ROOT_MANAGE_EMAIL_TEMPLATES,ROOT_MANAGE_CALL_COPY"/>
|
||||
<where>role_type='ROLE_SUPER_ADMIN'</where>
|
||||
</update>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
DROP VIEW IF EXISTS application_ranking_view;
|
||||
|
||||
CREATE OR REPLACE VIEW application_ranking_view AS
|
||||
WITH evaluation_scores AS (
|
||||
SELECT
|
||||
ae.application_id,
|
||||
COALESCE(
|
||||
SUM(
|
||||
COALESCE(NULLIF(TRIM(score_item ->> 'score'), ''), '0')::numeric
|
||||
),
|
||||
0
|
||||
) AS total_score
|
||||
FROM application_evaluation ae
|
||||
LEFT JOIN LATERAL jsonb_array_elements(
|
||||
CASE
|
||||
WHEN ae.criteria IS NULL OR BTRIM(ae.criteria) = '' THEN '[]'::jsonb
|
||||
ELSE ae.criteria::jsonb
|
||||
END
|
||||
) score_item ON TRUE
|
||||
WHERE ae.is_deleted = false
|
||||
GROUP BY ae.application_id
|
||||
),
|
||||
approved_applications AS (
|
||||
SELECT
|
||||
a.id AS application_id,
|
||||
a.call_id,
|
||||
a.company_id,
|
||||
a.user_id,
|
||||
a.status AS status,
|
||||
a.submission_date,
|
||||
a.amount_requested,
|
||||
a.amount_accepted,
|
||||
a.manual_ranking,
|
||||
a.ranking_action_type,
|
||||
a.ndg,
|
||||
a.pec_email,
|
||||
c.ranking_type,
|
||||
p.created_date AS protocol_datetime,
|
||||
p.protocol_number AS protocol_number,
|
||||
COALESCE(a.amount_accepted, a.amount_requested, 0) AS amount,
|
||||
COALESCE(es.total_score, 0) AS total_score
|
||||
FROM application a
|
||||
JOIN call c ON c.id = a.call_id
|
||||
AND (c.is_deleted = false OR c.is_deleted IS NULL)
|
||||
AND NULLIF(BTRIM(c.ranking_type), '') IS NOT NULL
|
||||
LEFT JOIN protocol p ON p.id = a.protocol_number
|
||||
LEFT JOIN evaluation_scores es ON es.application_id = a.id
|
||||
WHERE (a.is_deleted = false OR a.is_deleted IS NULL)
|
||||
AND a.status = 'APPROVED'
|
||||
AND COALESCE(a.ranking_action_type, '') <> 'EXCLUDE'
|
||||
),
|
||||
ranking_core AS (
|
||||
SELECT *
|
||||
FROM approved_applications
|
||||
WHERE COALESCE(ranking_action_type, '') <> 'REMOVE'
|
||||
),
|
||||
remove_apps AS (
|
||||
SELECT *
|
||||
FROM approved_applications
|
||||
WHERE COALESCE(ranking_action_type, '') = 'REMOVE'
|
||||
),
|
||||
natural_ranked AS (
|
||||
SELECT
|
||||
rc.*,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY rc.call_id
|
||||
ORDER BY
|
||||
CASE WHEN rc.ranking_type = 'SCORE' THEN rc.total_score END DESC NULLS LAST,
|
||||
CASE WHEN rc.ranking_type = 'PROTOCOL_DATE_TIME' THEN rc.protocol_datetime END ASC NULLS LAST,
|
||||
rc.protocol_datetime ASC NULLS LAST,
|
||||
rc.application_id ASC
|
||||
) AS natural_rank
|
||||
FROM ranking_core rc
|
||||
),
|
||||
manual_repositioned AS (
|
||||
SELECT *
|
||||
FROM natural_ranked
|
||||
WHERE COALESCE(ranking_action_type, '') = 'REPOSITION'
|
||||
AND manual_ranking IS NOT NULL
|
||||
),
|
||||
non_manual AS (
|
||||
SELECT *
|
||||
FROM natural_ranked
|
||||
WHERE COALESCE(ranking_action_type, '') <> 'REPOSITION'
|
||||
OR manual_ranking IS NULL
|
||||
),
|
||||
manual_slots AS (
|
||||
SELECT
|
||||
mr.call_id,
|
||||
mr.application_id,
|
||||
mr.company_id,
|
||||
mr.user_id,
|
||||
mr.status,
|
||||
mr.submission_date,
|
||||
mr.amount_requested,
|
||||
mr.amount_accepted,
|
||||
mr.amount,
|
||||
mr.ranking_type,
|
||||
mr.protocol_datetime,
|
||||
mr.protocol_number,
|
||||
mr.total_score,
|
||||
mr.ranking_action_type,
|
||||
mr.manual_ranking,
|
||||
mr.ndg,
|
||||
mr.pec_email,
|
||||
mr.natural_rank,
|
||||
mr.manual_ranking AS effective_rank_slot
|
||||
FROM manual_repositioned mr
|
||||
),
|
||||
non_manual_shifted AS (
|
||||
SELECT
|
||||
nm.call_id,
|
||||
nm.application_id,
|
||||
nm.company_id,
|
||||
nm.user_id,
|
||||
nm.status,
|
||||
nm.submission_date,
|
||||
nm.amount_requested,
|
||||
nm.amount_accepted,
|
||||
nm.amount,
|
||||
nm.ranking_type,
|
||||
nm.protocol_datetime,
|
||||
nm.protocol_number,
|
||||
nm.total_score,
|
||||
nm.ranking_action_type,
|
||||
nm.manual_ranking,
|
||||
nm.ndg,
|
||||
nm.pec_email,
|
||||
nm.natural_rank,
|
||||
nm.natural_rank
|
||||
+ COALESCE((
|
||||
SELECT COUNT(*)::bigint
|
||||
FROM manual_repositioned mr
|
||||
WHERE mr.call_id = nm.call_id
|
||||
AND mr.manual_ranking <= nm.natural_rank
|
||||
), 0) AS effective_rank_slot
|
||||
FROM non_manual nm
|
||||
),
|
||||
combined AS (
|
||||
SELECT * FROM manual_slots
|
||||
UNION ALL
|
||||
SELECT * FROM non_manual_shifted
|
||||
),
|
||||
max_slot_by_call AS (
|
||||
SELECT call_id, MAX(effective_rank_slot) AS max_slot
|
||||
FROM combined
|
||||
GROUP BY call_id
|
||||
),
|
||||
remove_placed AS (
|
||||
SELECT
|
||||
ra.call_id,
|
||||
ra.application_id,
|
||||
ra.company_id,
|
||||
ra.user_id,
|
||||
ra.status,
|
||||
ra.submission_date,
|
||||
ra.amount_requested,
|
||||
ra.amount_accepted,
|
||||
ra.amount,
|
||||
ra.ranking_type,
|
||||
ra.protocol_datetime,
|
||||
ra.protocol_number,
|
||||
ra.total_score,
|
||||
ra.ranking_action_type,
|
||||
ra.manual_ranking,
|
||||
ra.ndg,
|
||||
ra.pec_email,
|
||||
NULL::bigint AS natural_rank,
|
||||
COALESCE(ms.max_slot, 0)
|
||||
+ ROW_NUMBER() OVER (
|
||||
PARTITION BY ra.call_id
|
||||
ORDER BY ra.protocol_datetime ASC NULLS LAST, ra.application_id
|
||||
) AS effective_rank_slot
|
||||
FROM remove_apps ra
|
||||
LEFT JOIN max_slot_by_call ms ON ms.call_id = ra.call_id
|
||||
),
|
||||
final_rows AS (
|
||||
SELECT * FROM combined
|
||||
UNION ALL
|
||||
SELECT * FROM remove_placed
|
||||
)
|
||||
SELECT
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY fr.call_id
|
||||
ORDER BY fr.effective_rank_slot, fr.protocol_datetime ASC NULLS LAST, fr.application_id
|
||||
) AS listing_rank,
|
||||
fr.application_id,
|
||||
fr.call_id,
|
||||
fr.ranking_action_type,
|
||||
fr.total_score,
|
||||
fr.user_id,
|
||||
fr.status,
|
||||
fr.submission_date,
|
||||
fr.protocol_datetime,
|
||||
fr.protocol_number,
|
||||
fr.ndg,
|
||||
fr.amount_accepted,
|
||||
fr.pec_email,
|
||||
fr.manual_ranking,
|
||||
fr.ranking_type
|
||||
FROM final_rows fr;
|
||||
12
src/main/resources/db/dump/insert_form_field_25_03_2026.sql
Normal file
12
src/main/resources/db/dump/insert_form_field_25_03_2026.sql
Normal file
@@ -0,0 +1,12 @@
|
||||
INSERT INTO FORM_FIELD (SORT_ORDER, NAME, LABEL, DESCRIPTION, SETTINGS, VALIDATORS, CREATED_DATE, UPDATED_DATE)
|
||||
VALUES
|
||||
(
|
||||
24,
|
||||
'spreadsheet',
|
||||
'Foglio di Calcolo',
|
||||
'Modello di foglio di calcolo con variabili dinamiche',
|
||||
'[{name: "label",value: "Foglio di Calcolo"},{name: "template",value: {}}]',
|
||||
'{}',
|
||||
CURRENT_TIMESTAMP,
|
||||
CURRENT_TIMESTAMP
|
||||
);
|
||||
@@ -0,0 +1,86 @@
|
||||
DROP VIEW IF EXISTS gepafin_schema.application_view ;
|
||||
|
||||
|
||||
CREATE OR REPLACE VIEW application_view AS
|
||||
|
||||
SELECT
|
||||
a.id,
|
||||
a.status,
|
||||
a.submission_date,
|
||||
a.comments,
|
||||
a.amount_requested,
|
||||
a.amount_accepted,
|
||||
a.date_accepted,
|
||||
a.date_rejected,
|
||||
a.is_deleted,
|
||||
a.hub_id,
|
||||
a.user_id,
|
||||
a.ndg,
|
||||
|
||||
a.evaluation_version AS evaluation_version,
|
||||
a.updated_date AS modified_date,
|
||||
a.created_date AS created_date,
|
||||
|
||||
|
||||
|
||||
-- Call Details
|
||||
a.call_id AS call_id,
|
||||
cl.name AS call_title,
|
||||
cl.end_date AS call_end_date,
|
||||
cl.end_time AS call_end_time,
|
||||
|
||||
-- Company Details
|
||||
a.COMPANY_ID AS company_id,
|
||||
c.company_name AS company_name,
|
||||
|
||||
-- Protocol Details
|
||||
p.protocol_number AS protocol_number,
|
||||
|
||||
|
||||
-- Assigned User Details from ASSIGNED_APPLICATION and GEPAFIN_USER
|
||||
COALESCE(aa.user_id, NULL) AS assigned_user_id,
|
||||
COALESCE(
|
||||
NULLIF(
|
||||
TRIM(CONCAT(
|
||||
COALESCE(u.first_name, ''), ' ',
|
||||
COALESCE(u.last_name, '')
|
||||
)),
|
||||
''
|
||||
),
|
||||
''
|
||||
) AS assigned_user_name,
|
||||
|
||||
-- User with Company Details (From Application's User)
|
||||
COALESCE(uwc.id, NULL) AS user_with_company_id
|
||||
|
||||
|
||||
FROM gepafin_schema.APPLICATION a
|
||||
|
||||
-- Join Call Entity
|
||||
LEFT JOIN gepafin_schema.CALL cl
|
||||
ON a.CALL_ID = cl.id
|
||||
|
||||
-- Join Company Entity (Ensuring it is not deleted)
|
||||
LEFT JOIN gepafin_schema.COMPANY c
|
||||
ON a.COMPANY_ID = c.id
|
||||
|
||||
-- Join Protocol Entity
|
||||
LEFT JOIN gepafin_schema.PROTOCOL p
|
||||
ON a.PROTOCOL_NUMBER = p.id
|
||||
|
||||
-- Join Assigned Application Entity (Ensuring it is not deleted)
|
||||
LEFT JOIN gepafin_schema.assigned_applications aa
|
||||
ON a.id = aa.APPLICATION_ID
|
||||
AND (aa.IS_DELETED IS FALSE OR aa.IS_DELETED IS NULL)
|
||||
|
||||
-- Join User Entity (Get First & Last Name Combined)
|
||||
LEFT JOIN gepafin_schema.GEPAFIN_USER u
|
||||
ON aa.user_id = u.id
|
||||
|
||||
-- Get User With Company ID (From Application's User & Company)
|
||||
LEFT JOIN gepafin_schema.USER_WITH_COMPANY uwc
|
||||
ON a.user_id = uwc.user_id
|
||||
AND a.COMPANY_ID = uwc.company_id
|
||||
AND uwc.is_deleted = FALSE -- Ensuring the user is active
|
||||
|
||||
WHERE a.IS_DELETED IS FALSE OR a.IS_DELETED IS NULL;
|
||||
@@ -0,0 +1,202 @@
|
||||
-- 1
|
||||
UPDATE gepafin_schema.system_email_template
|
||||
SET template_name = 'Application submission template to beneficiary and company',
|
||||
"type" = 'APPLICATION_SUBMISSION_TO_USER_AND_COMPANY',
|
||||
html_content = '<html>
|
||||
<body style="font-family: Arial, sans-serif; color: #333; line-height: 1.6;">
|
||||
<div style="padding: 20px; border: 1px solid #ddd; border-radius: 8px; max-width: 600px; margin: auto;">
|
||||
<p>Buongiorno,</p>
|
||||
<p>
|
||||
Si comunica che, in riferimento alla domanda di concessione di
|
||||
Finanziamento agevolato a valere sul Fondo prestiti
|
||||
<strong>{{call_name}}</strong> di cui all''oggetto, la stessa è stata
|
||||
regolarmente acquisita ed è stata registrata con Protocollo n.
|
||||
<strong>{{protocol_number}}</strong> del <strong>{{date}}</strong> alle
|
||||
<strong>{{time}}</strong>.
|
||||
</p>
|
||||
<p>Distinti Saluti,</p>
|
||||
<p>
|
||||
<strong>{{email_signature}}</strong>
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>',
|
||||
subject = 'BANDO {{call_name}} - Domanda di concessione di finanziamento agevolato {{company_name}}'
|
||||
WHERE email_scenario = 'APPLICATION_SUBMITTED'
|
||||
AND type = 'APPLICATION_SUBMISSION_TO_USER_AND_COMPANY'
|
||||
AND hub_id IS NULL;
|
||||
|
||||
-- 2
|
||||
UPDATE gepafin_schema.system_email_template
|
||||
SET template_name='Application submission template to gepafin',
|
||||
"type"='APPLICATION_SUBMISSION_TO_GEPAFIN',
|
||||
html_content='<html>
|
||||
<body style="font-family: Arial, sans-serif; color: #333; line-height: 1.6;">
|
||||
<div style="padding: 20px; border: 1px solid #ddd; border-radius: 8px; max-width: 600px; margin: auto;">
|
||||
<p>
|
||||
In riferimento alla domanda di concessione di Finanziamento agevolato a valere sul Fondo prestiti
|
||||
<strong>{{call_name}}</strong> di cui all''oggetto, la stessa è stata regolarmente acquisita ed è stata
|
||||
registrata con Protocollo n. <strong>{{protocol_number}}</strong> del <strong>{{date}}</strong> alle <strong>{{time}}</strong>.
|
||||
</p>
|
||||
<p>Distinti Saluti,</p>
|
||||
<p>
|
||||
<strong>{{email_signature}}</strong>
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>',
|
||||
subject='BANDO {{call_name}} - Domanda di concessione di finanziamento agevolato {{company_name}}'
|
||||
WHERE email_scenario='APPLICATION_SUBMITTED'
|
||||
AND type='APPLICATION_SUBMISSION_TO_GEPAFIN'
|
||||
AND hub_id IS NULL;
|
||||
|
||||
-- 3
|
||||
UPDATE gepafin_schema.system_email_template
|
||||
SET html_content='<html>
|
||||
<body style="font-family: Arial, sans-serif; color: #000; line-height: 1.6;">
|
||||
<div style="padding: 20px; border: 1px solid #ddd; border-radius: 8px; max-width: 600px; margin: auto;">
|
||||
<p><strong>RICHIESTA INTEGRAZIONE DOCUMENTALE</strong></p>
|
||||
<p>Buongiorno,</p>
|
||||
<p>In riferimento alla domanda di concessione di Finanziamento agevolato a valere sul Fondo prestiti
|
||||
<strong>{{call_name}}</strong> di cui al <strong>Protocollo n. {{protocol_number}} del
|
||||
{{protocol_date}} e {{protocol_time}}</strong>, alla luce dell''attività istruttoria svolta,
|
||||
segnaliamo quanto segue:
|
||||
</p>
|
||||
<ul>
|
||||
{{form_dataInput}}
|
||||
</ul>
|
||||
<p>{{note}}</p>
|
||||
<p>Vi invitiamo a fornire quanto sopra richiesto integrando la documentazione sia caricandola all''interno dello sportello
|
||||
online <a href="{{platform_link}}">{{platform_link}}</a> che inviandola a mezzo PEC all''indirizzo
|
||||
bandi.gepafin@legalmail.it entro e <strong>non oltre {{response_days}} giorni</strong> dal ricevimento della presente comunicazione,
|
||||
precisando che, in caso di mancata ricezione nei termini indicati, saremo costretti a non prendere in considerazione la Vostra richiesta di finanziamento.
|
||||
</p>
|
||||
<p>Vi informiamo che per la ricezione della PEC farà fede la ricevuta di avvenuta consegna che attesterà il buon esito
|
||||
dell''invio. La documentazione trasmessa e le informazioni fornite saranno processate dall''istruttore assegnatario della pratica.
|
||||
</p>
|
||||
<p>Distinti Saluti,</p>
|
||||
<p><strong>{{email_signature}}</strong></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>'
|
||||
WHERE email_scenario='APPLICATION_AMENDMENT_REQUESTED'
|
||||
AND hub_id IS NULL;
|
||||
|
||||
-- 4
|
||||
UPDATE gepafin_schema.system_email_template
|
||||
SET subject='BANDO {{call_name}} - Domanda di finanziamento non ammessa {{company_name}}'
|
||||
WHERE email_scenario='APPLICATION_AMENDMENT_EXPIRED';
|
||||
|
||||
-- 5
|
||||
UPDATE gepafin_schema.system_email_template
|
||||
SET subject='BANDO {{call_name}} – Esito positivo istruttoria di ammissibilità {{company_name}}'
|
||||
WHERE email_scenario='APPLICATION_ADMISSIBLE';
|
||||
|
||||
-- 6
|
||||
UPDATE gepafin_schema.system_email_template
|
||||
SET html_content='<html> <body style="font-family: Arial, sans-serif; color: #000; line-height: 1.6;">
|
||||
<div style="padding: 20px; border: 1px solid #ddd; border-radius: 8px; max-width: 600px; margin: auto;">
|
||||
<p>Buongiorno,</p>
|
||||
<p>Si comunica che, in riferimento alla domanda a valere sul bando “<strong>{{call_name}}</strong>” di cui al
|
||||
<strong>Protocollo n. {{protocol_number}} del {{protocol_date}} alle {{protocol_time}}</strong>,
|
||||
</p>
|
||||
<p>
|
||||
{{tipo_inammissibilita}}.</p>
|
||||
<p>Le motivazioni sono le seguenti: <strong>{{form_text}}</strong></p>
|
||||
<p>Vi ricordiamo che i Beneficiari che hanno presentato richieste valutate non ammissibili entro 10 giorni dalla data di ricevimento della presente potranno finoltrare richiesta di chiarimenti e/o osservazioni alla scrivente Società ai sensi e per gli effetti dell''art.10 bis della L.241/1990 e s.m.i.</p>
|
||||
<p>Distinti Saluti,</p>
|
||||
<p><strong>{{email_signature}}</strong></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>'
|
||||
WHERE email_scenario='APPLICATION_REJECTED'
|
||||
AND hub_id IS NULL;
|
||||
|
||||
|
||||
-- 7
|
||||
UPDATE gepafin_schema.system_email_template
|
||||
SET html_content='<html>
|
||||
<body style="font-family: Arial, sans-serif; color: #000; line-height: 1.6;">
|
||||
<div style="padding: 20px; border: 1px solid #ddd; border-radius: 8px; max-width: 600px; margin: auto;">
|
||||
<p><strong>PROMEMORIA PER LA PRESENTAZIONE DELL''Soccorso Istruttorio</strong></p>
|
||||
<p>Buongiorno,</p>
|
||||
<p>Questo è un promemoria per completare la presentazione dell''Soccorso Istruttorio entro il termine specificato. Di seguito i dettagli:</p>
|
||||
<ul>
|
||||
<li><strong>Amendment ID:</strong> {{amendment_id}}</li>
|
||||
<li><strong>Data di Scadenza:</strong> {{amendment_due_date}}</li>
|
||||
</ul>
|
||||
<p>Si prega di assicurarsi che l''Soccorso Istruttorio venga presentato entro la data di scadenza per evitare ritardi. Inviare l''Soccorso Istruttorio tramite la piattaforma online <a href="{{platform_link}}">{{platform_link}}</a> </p>
|
||||
<p>Distinti saluti,</p>
|
||||
<p><strong>{{email_signature}}</strong></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>'
|
||||
WHERE email_scenario='APPLICATION_AMENDMENT_REMINDER';
|
||||
|
||||
-- 8
|
||||
UPDATE gepafin_schema.system_email_template
|
||||
SET template_name='Welcome Email for New Confidi User'
|
||||
WHERE email_scenario='USER_CREATION' AND type='USER_ONBOARDING_CONFIDI';
|
||||
|
||||
-- 9
|
||||
UPDATE gepafin_schema.system_email_template
|
||||
SET template_name='Password Reset Link Email (Italian)'
|
||||
WHERE email_scenario='PASSWORD_RESET_REQUEST';
|
||||
|
||||
-- 10 (hub specific)
|
||||
UPDATE gepafin_schema.system_email_template
|
||||
SET html_content='<html>
|
||||
<body style="font-family: Arial, sans-serif; color: #000; line-height: 1.6;">
|
||||
<div style="padding: 20px; border: 1px solid #ddd; border-radius: 8px; max-width: 600px; margin: auto;">
|
||||
<p><strong>RICHIESTA INTEGRAZIONE DOCUMENTALE</strong></p>
|
||||
<p>Buongiorno,</p>
|
||||
<p>In riferimento alla domanda di concessione di Finanziamento agevolato a valere sul Bando
|
||||
"<strong>{{call_name}}</strong>" di cui al <strong>Protocollo n. {{protocol_number}} del
|
||||
{{protocol_date}} e {{protocol_time}}</strong>, alla luce dell''attività istruttoria svolta,
|
||||
segnaliamo quanto segue:
|
||||
</p>
|
||||
{{note}}
|
||||
<p>Vi invitiamo a fornire quanto sopra richiesto integrando la documentazione caricandola all''interno dello sportello
|
||||
online <a href="{{platform_link}}">{{platform_link}}</a> entro e <strong>non oltre {{response_days}} giorni</strong> dal ricevimento della presente comunicazione,
|
||||
precisando che, in caso di mancata ricezione nei termini indicati, saremo costretti a non prendere in considerazione la Vostra richiesta di finanziamento.
|
||||
</p>
|
||||
<p>La documentazione trasmessa e le informazioni fornite saranno processate dall''istruttore assegnatario della pratica.
|
||||
</p>
|
||||
<p>Distinti Saluti,</p>
|
||||
<p><strong>{{email_signature}}</strong></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>'
|
||||
WHERE email_scenario='APPLICATION_AMENDMENT_REQUESTED' AND hub_id=2;
|
||||
|
||||
-- 11 (hub specific reject)
|
||||
UPDATE gepafin_schema.system_email_template
|
||||
SET subject='BANDO {{call_name}} – Esito negativo istruttoria di ammissibilità {{company_name}}'
|
||||
WHERE email_scenario='APPLICATION_REJECTED' AND hub_id=2;
|
||||
|
||||
-- 12
|
||||
UPDATE gepafin_schema.system_email_template
|
||||
SET template_name='Welcome Email for New Bandi User'
|
||||
WHERE email_scenario='USER_CREATION' AND type='USER_ONBOARDING_BANDI';
|
||||
|
||||
-- 13
|
||||
UPDATE gepafin_schema.system_email_template
|
||||
SET template_name='Application submission failure notification'
|
||||
WHERE email_scenario='APPLICATION_SUBMISSION_FAILURE';
|
||||
|
||||
-- 14
|
||||
UPDATE gepafin_schema.system_email_template
|
||||
SET subject='BANDO – "{{call_name}}" – Esito negativo della valutazione tecnica – {{company_name}}'
|
||||
WHERE email_scenario='APPLICATION_TECHNICAL_EVALUATION_REJECTED';
|
||||
|
||||
-- 15
|
||||
UPDATE gepafin_schema.system_email_template
|
||||
SET subject='Comunicazione esito valutazione tecnica ed economico-finanziaria – Avviso {{call_name}} '
|
||||
WHERE email_scenario='SPECIAL_APPLICATION_AMENDMENT_REQUESTED';
|
||||
|
||||
-- 16
|
||||
UPDATE gepafin_schema.system_email_template
|
||||
SET subject='Comunicazione esito valutazione tecnica ed economico-finanziaria – Avviso {{call_name}} '
|
||||
WHERE email_scenario='SPECIAL_APPLICATION_AMENDMENT_REQUESTED_BLUE_TONGUE';
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
UPDATE gepafin_schema.system_email_template
|
||||
SET subject='Comunicazione esito valutazione tecnica ed economico-finanziaria– Avviso {{call_name}} '
|
||||
WHERE email_scenario='SPECIAL_APPLICATION_AMENDMENT_REQUESTED';
|
||||
|
||||
UPDATE gepafin_schema.system_email_template
|
||||
SET subject='Comunicazione esito valutazione tecnica ed economico-finanziaria– Avviso {{call_name}} '
|
||||
WHERE email_scenario='SPECIAL_APPLICATION_AMENDMENT_REQUESTED_BLUE_TONGUE';
|
||||
@@ -61,6 +61,12 @@ status.same.error=Status is already set.
|
||||
invalid.status.change.from.draft=Status cannot be changed to READY_TO_PUBLISH or PUBLISH from DRAFT.
|
||||
status.cannot.be.changed=Status cannot be changed.
|
||||
published.call.not.update=Published call cannot be updated.
|
||||
published.call.step1.only.ranking.type.allowed=For a published call, this step accepts only ranking type.
|
||||
call.ranking.type.updated.successfully=Call ranking type updated successfully.
|
||||
application.ranking.action.updated.successfully=Application ranking action updated successfully.
|
||||
application.ranking.fetched.successfully=Application ranking fetched successfully.
|
||||
call.must.be.closed.for.ranking.action=Ranking action is allowed only when the call is closed.
|
||||
application.ranking.action.invalid=Invalid application ranking action request.
|
||||
invalid.status.change.from.publish=Status cannot be changed to READY_TO_PUBLISH from PUBLISH.
|
||||
invalid.status.change.from.publish.to.draft=Status cannot be changed to DRAFT from PUBLISH as Applications are already created for this CALL.
|
||||
validation.table.message=Data for field {0} is not present.
|
||||
@@ -230,6 +236,8 @@ call.not.started.yet = The call has not started yet. Please wait until the speci
|
||||
call.already.ended = The call has already ended. You cannot submit the application after the deadline.
|
||||
status.updated.successfully=Status updated successfully.
|
||||
application.status.updated.successfully = Application status updated successfully.
|
||||
application.status.transition.restricted=This status change is not available through this operation.
|
||||
application.registry.segment.invalid=Invalid segment value.
|
||||
application.already.in.provided.status=Application is already in provided status.
|
||||
delegation.not.found=Delegation not found.
|
||||
user.company.relation.not.found=User with the specified company relation not found.
|
||||
@@ -389,6 +397,7 @@ company.document.fetched.successfully = Company Document fetched successfully.
|
||||
company.document.updated.successfully = Company Document Updated successfully.
|
||||
category.cannot.be.deleted = Category cannot be deleted as it is associated with company documents.
|
||||
company.document.copied.successfully = Company Document Copied successfully.
|
||||
company.document.copy.evaluation.requires.application.evaluation=Copy to evaluation requires an application evaluation linked to this application.
|
||||
invalid.expiration.date = Invalid Expiration Date
|
||||
|
||||
|
||||
|
||||
@@ -61,6 +61,12 @@ status.same.error=Lo stato ? gi? impostato.
|
||||
invalid.status.change.from.draft=Lo stato non pu? essere cambiato in READY_TO_PUBLISH o PUBLISH da DRAFT.
|
||||
status.cannot.be.changed=Lo stato non pu? essere cambiato.
|
||||
published.call.not.update=Il bando pubblicato non pu? essere aggiornato.
|
||||
published.call.step1.only.ranking.type.allowed=Per un bando pubblicato, questo passaggio accetta solo il tipo di graduatoria; omettere tutti gli altri campi.
|
||||
call.ranking.type.updated.successfully=Tipo di graduatoria del bando aggiornato correttamente.
|
||||
application.ranking.action.updated.successfully=Azione di graduatoria sulla domanda aggiornata correttamente.
|
||||
application.ranking.fetched.successfully=Graduatoria recuperata correttamente.
|
||||
call.must.be.closed.for.ranking.action=L'azione di graduatoria <20> consentita solo se il bando <20> chiuso (scaduto).
|
||||
application.ranking.action.invalid=Richiesta di azione sulla graduatoria non valida.
|
||||
invalid.status.change.from.publish=Lo stato non pu? essere modificato in READY_TO_PUBLISH da PUBLISH.
|
||||
invalid.status.change.from.publish.to.draft=Lo stato non pu essere modificato da PUBLISH a DRAFT poich sono gi state create applicazioni per questa CALL.
|
||||
|
||||
@@ -71,7 +77,7 @@ email.already.exists=Esiste gi? un utente con questa email.
|
||||
invalid_user=Validazione utente fallita. Controlla le informazioni, lo stato dell'account e la scadenza del token.
|
||||
|
||||
#Global messages
|
||||
common_message=Qualcosa è andato storto. Riprova.
|
||||
common_message=Qualcosa <EFBFBD> andato storto. Riprova.
|
||||
invalid_signature=Gettone non valido.
|
||||
invalid_login=Nome utente o password errati
|
||||
req_validation_er=Errore di convalida
|
||||
@@ -374,6 +380,7 @@ document.category.not.found = Categoria documento non trovata.
|
||||
document.category.get.success = Categoria del documento recuperata correttamente.
|
||||
document.category.success =Categoria documento creata correttamente.
|
||||
company.document.copied.successfully = Documento aziendale copiato correttamente.
|
||||
company.document.copy.evaluation.requires.application.evaluation=La copia nella valutazione richiede una valutazione dell'applicazione collegata a questa domanda.
|
||||
error.moving.file.to.deleted.folder = Si è verificato un errore durante lo spostamento del file aziendale nella cartella eliminata.ss
|
||||
|
||||
company.document.fetched.successfully = Documento aziendale recuperato con successo.
|
||||
@@ -424,5 +431,7 @@ amendment.must.be.approved.first=L'emendamento deve essere approvato dal diretto
|
||||
upload.company.document.to.application=Documento aziendale caricato correttamente nell'applicazione.
|
||||
company.document.not.found.with.ids=Documento aziendale non trovato. ID mancanti: {0}
|
||||
amount.field.not.provided= Si prega di fornire i campi obbligatori per l'importo.
|
||||
vat.or.tax.code.required=È obbligatorio il numero di partita IVA o il codice fiscale.
|
||||
provide.valid.vat.number=Inserisci un numero di partita IVA valido per procedere con NDG.
|
||||
vat.or.tax.code.required=<EFBFBD> obbligatorio il numero di partita IVA o il codice fiscale.
|
||||
provide.valid.vat.number=Inserisci un numero di partita IVA valido per procedere con NDG.
|
||||
application.status.transition.restricted=Questa modifica di stato non <20> disponibile tramite questa operazione.
|
||||
application.registry.segment.invalid=Valore del segmento non valido.
|
||||
|
||||
Reference in New Issue
Block a user