Merge branch 'develop' of https://github.com/Kitzanos/GEPAFIN-BE into develop

This commit is contained in:
Piyush
2025-05-14 14:53:15 +05:30
16 changed files with 552 additions and 167 deletions

View File

@@ -541,10 +541,14 @@ public class GepafinConstant {
public static final String RESEND_EMAIL_SENT_FAILED_MSG = "resend.email.sent.failed.msg";
public static final String REMINDER_EMAIL_FAILED_MSG = "reminder.email.sent.failed.msg";
public static final String READMIT_APPLICATION_SUCCESS = "application.readmit.success";
public static final String NO_EMAIL_LOG_FOUND = "no.email.log.msg";
public static final String USER_ACTION_ID_NOT_FOUND = "user.action.id.not.found";
public static final String CAUSE_STRING = "cause" ;
public static final String ERROR_DESCRIPTION_STRING = "errorDescription" ;
public static final String ERROR_STRING = "errors";
}

View File

@@ -1089,12 +1089,12 @@ public class ApplicationAmendmentRequestDao {
application.setStatus(ApplicationStatusTypeEnum.EVALUATION.getValue());
applicationRepository.save(application);
existingApplicationAmendment.getApplicationEvaluationEntity().getAssignedApplicationsEntity().setStatus(AssignedApplicationEnum.OPEN.getValue());
AssignedApplicationsEntity assignedApplicationsEntity = assignedApplicationsDao.validateAssignedApplication(
existingApplicationAmendment.getApplicationEvaluationEntity().getAssignedApplicationsEntity().getId());
AssignedApplicationsEntity oldAssignedApplicationData = Utils.getClonedEntityForData(assignedApplicationsEntity);
existingApplicationAmendment.getApplicationEvaluationEntity().getAssignedApplicationsEntity().setStatus(AssignedApplicationEnum.OPEN.getValue());
assignedApplicationsEntity = assignedApplicationsRepository.save(existingApplicationAmendment.getApplicationEvaluationEntity().getAssignedApplicationsEntity());

View File

@@ -2170,6 +2170,88 @@ public class ApplicationDao {
emailNotificationDao.sendMail(hub.getId(), subject, body, List.of(GepafinConstant.RINALDO_EMAIL),emailLogRequest);
}
public ApplicationResponse readmitApplication(HttpServletRequest request, Long applicationId) {
log.info("Re-admiting the Application with id : {}", applicationId);
ApplicationEntity applicationEntity = fetchRejectedApplication(applicationId);
if(applicationEntity == null){
throw new ResourceNotFoundException(Status.NOT_FOUND,
Translator.toLocale(GepafinConstant.APPLICATION_NOT_FOUND_MSG));
}
validator.validateUserWithCompany(request, applicationEntity.getCompanyId());
validateCallEndDate(applicationEntity);
assignedApplicationsRepository.findByApplicationIdAndStatusAndIsDeletedFalse(applicationEntity.getId(), AssignedApplicationEnum.CLOSE.getValue())
.ifPresent(assignedApp -> processAssignedAppAndEvaluation(request, applicationEntity, assignedApp));
return getApplicationResponse(applicationEntity);
}
private ApplicationEntity fetchRejectedApplication(Long applicationId) {
return applicationRepository.findByIdAndStatusAndIsDeletedFalse(applicationId, ApplicationStatusTypeEnum.REJECTED.getValue());
}
private void validateCallEndDate(ApplicationEntity applicationEntity) {
checkCallEndDate(applicationEntity.getCall());
log.info("Call end date verified successfully | callId: {}", applicationEntity.getCall().getId());
}
private void processAssignedAppAndEvaluation(HttpServletRequest request, ApplicationEntity applicationEntity, AssignedApplicationsEntity assignedApp) {
applicationEvaluationRepository.findByAssignedApplicationsEntity_IdAndStatusAndIsDeletedFalse(assignedApp.getId(), ApplicationEvaluationStatusTypeEnum.CLOSE.getValue())
.ifPresent(eval -> reopenApplication(request, applicationEntity, assignedApp, eval));
}
private void reopenApplication(HttpServletRequest request, ApplicationEntity applicationEntity,
AssignedApplicationsEntity assignedApp, ApplicationEvaluationEntity evaluationEntity) {
ApplicationEntity oldApplication = Utils.getClonedEntityForData(applicationEntity);
AssignedApplicationsEntity oldAssignedApp = Utils.getClonedEntityForData(assignedApp);
ApplicationEvaluationEntity oldEvaluation = Utils.getClonedEntityForData(evaluationEntity);
updateApplicationStatus(applicationEntity);
updateAssignedApplicationStatus(assignedApp);
updateEvaluationEntity(applicationEntity.getHubId(), evaluationEntity);
saveEntities(applicationEntity, assignedApp, evaluationEntity);
/** This code is responsible for adding a version history log for the "Update Application" operation. **/
loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplication).newData(applicationEntity).build());
/** This code is responsible for adding a version history log for the "Update Application Evaluation" operation. **/
loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldEvaluation).newData(evaluationEntity).build());
/** This code is responsible for adding a version history log for the "Update Assigned Application" operation. **/
loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldAssignedApp).newData(assignedApp).build());
}
private void updateApplicationStatus(ApplicationEntity applicationEntity) {
applicationEntity.setStatus(ApplicationStatusTypeEnum.EVALUATION.getValue());
applicationEntity.setDateRejected(null);
}
private void updateAssignedApplicationStatus(AssignedApplicationsEntity assignedApp) {
assignedApp.setStatus(AssignedApplicationEnum.OPEN.getValue());
}
private void updateEvaluationEntity(Long hubId, ApplicationEvaluationEntity evaluationEntity) {
HubEntity hub = hubService.valdateHub(hubId);
Long evaluationDays = (hub != null) ? hub.getEvaluationExpirationDays() : 30L;
LocalDateTime now = DateTimeUtil.DateServerToUTC(LocalDateTime.now());
evaluationEntity.setStatus(ApplicationEvaluationStatusTypeEnum.OPEN.getValue());
evaluationEntity.setClosingDate(null);
evaluationEntity.setActiveDays(null);
evaluationEntity.setEndDate(now.plusDays(evaluationDays));
evaluationEntity.setStartDate(now);
evaluationEntity.setRemainingDays(evaluationDays);
evaluationEntity.setSuspendedDays(0L);
evaluationEntity.setStopDateTime(null);
}
private void saveEntities(ApplicationEntity app, AssignedApplicationsEntity assignedApp, ApplicationEvaluationEntity eval) {
applicationRepository.save(app);
assignedApplicationsRepository.save(assignedApp);
applicationEvaluationRepository.save(eval);
}
}

View File

@@ -1,7 +1,6 @@
package net.gepafin.tendermanagement.dao;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -13,12 +12,14 @@ import net.gepafin.tendermanagement.config.Translator;
import net.gepafin.tendermanagement.config.jwt.TokenProvider;
import net.gepafin.tendermanagement.constants.AppointmentApiConstant;
import net.gepafin.tendermanagement.constants.GepafinConstant;
import net.gepafin.tendermanagement.entities.ApplicationAmendmentRequestEntity;
import net.gepafin.tendermanagement.entities.ApplicationEntity;
import net.gepafin.tendermanagement.entities.ApplicationEvaluationEntity;
import net.gepafin.tendermanagement.entities.CompanyEntity;
import net.gepafin.tendermanagement.entities.DocumentEntity;
import net.gepafin.tendermanagement.entities.HubEntity;
import net.gepafin.tendermanagement.enums.ApplicationStatusTypeEnum;
import net.gepafin.tendermanagement.enums.DocumentSourceTypeEnum;
import net.gepafin.tendermanagement.enums.NotificationTypeEnum;
import net.gepafin.tendermanagement.enums.VersionActionTypeEnum;
import net.gepafin.tendermanagement.model.request.AppointmentCreationRequest;
@@ -63,7 +64,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -145,6 +145,15 @@ public class AppointmentDao {
@Autowired
private AmazonS3Service amazonS3Service;
@Autowired
private ApplicationDao applicationDao;
@Autowired
private ApplicationAmendmentRequestDao applicationAmendmentRequestDao;
@Autowired
private ApplicationEvaluationDao applicationEvaluationDao;
private final Map<Long, ExecutorService> executorMap = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Long, ExecutorService> threadForDocumentMap = new ConcurrentHashMap<>();
@@ -175,9 +184,182 @@ public class AppointmentDao {
return ndgResponse;
}
private HubEntity loginToOdessa(HubEntity hub, ApplicationEntity application) {
// private HubEntity loginToOdessa(HubEntity hub, ApplicationEntity application) {
//
// int maxRetries = 3;
// int attempt = 0;
// boolean success = false;
// while (attempt < maxRetries && !success) {
// attempt++;
// try {
// //code to generate token with payload having "iat" epoch timestamp and secret key with no expiry and send in below method call
// String authJwtToken = Utils.generateAuthTokenForLoginToOdessa();
// log.info("Got the auth for login to odessa {}", authJwtToken);
// hub.setAuthToken(authJwtToken);
// hubRepository.save(hub);
// Map<String, Object> body = Collections.emptyMap();
// ResponseEntity<Object> responseLogin = appointmentApiService.loginWithOdessa(authJwtToken, source, context, user, password, body);
// if (responseLogin.getStatusCode() == HttpStatus.OK) {
// log.info("Login successful to odessa. Parsing response.");
// String loginResponseJson = Utils.convertObjectToJson(responseLogin.getBody());
// AppointmentLoginResponse parsedResponse = parseLoginResponse(loginResponseJson);
//
// // Validate and save token
// if (parsedResponse.getTokenId() != null) {
// hub.setAppointmentAuthTokenId(parsedResponse.getTokenId());
// hub.setAreaCode(parsedResponse.getAreaCode());
// hubRepository.save(hub);
// log.info("Saved new authToken and areaCode for Hub.");
// success = true;
// return hub;
// } else {
// throw new RuntimeException("Login response is missing a valid tokenId for login to odessa system, please try again.");
// }
// }
// throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_IN_GENERATING_NDG_TRY_AGAIN));
// } catch (FeignException.Forbidden forbiddenException) {
// log.error("Failed to login to odessa due to some error");
//
// // Extract raw response body
// String responseBody = forbiddenException.contentUTF8(); // Extract raw JSON response
//
// // Parse JSON to check for "PasswordExpired"
// try {
// ObjectMapper objectMapper = new ObjectMapper();
// JsonNode rootNode = objectMapper.readTree(responseBody);
// JsonNode errorsNode = rootNode.path("errors");
//
// if (errorsNode.isArray()) {
// for (JsonNode error : errorsNode) {
// // Check the main errorCode
// if (GepafinConstant.PASSWORD_EXPIRED.equals(error.path("errorCode").asText())) {
// application.setNdgStatus(GepafinConstant.NDG_FAILED);
// applicationRepository.save(application);
// throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA));
// }
//
// // Check inside "subErrors"
// JsonNode subErrorsNode = error.path("subErrors");
// if (subErrorsNode.isArray()) {
// for (JsonNode subError : subErrorsNode) {
// if (GepafinConstant.PASSWORD_EXPIRED.equals(subError.path("errorCode").asText())) {
// application.setNdgStatus(GepafinConstant.NDG_FAILED);
// applicationRepository.save(application);
// throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA));
// }
// }
// }
// }
// }
// } catch (IOException e) {
// log.error("Error parsing JSON response: {}", e.getMessage());
// }
// } catch (Exception e) {
// log.error("Failed to authenticate user on Odessa : {}", e.getMessage(), e);
// throw new RuntimeException("Authentication failed on Odessa. try again", e);
// }
// }
// return null;
// }
//
//
// private HubEntity authenticateAndSaveToken(HubEntity hub) {
//
// int maxRetries = 3;
// int attempt = 0;
// boolean success = false;
// while (attempt < maxRetries && !success) {
// attempt++;
// try {
// //code to generate token with payload having "iat" epoch timestamp and secret key with no expiry and send in below method call
// String authJwtToken = Utils.generateAuthTokenForLoginToOdessa();
// log.info("Got the auth for login to odessa {}", authJwtToken);
// hub.setAuthToken(authJwtToken);
// hubRepository.save(hub);
// // Prepare the request body (adjust if necessary for login API)
// Map<String, Object> body = Collections.emptyMap();
// // Perform login API call
// ResponseEntity<Object> responseLogin = appointmentApiService.loginWithOdessa(authJwtToken, source, context, user, password, body);
//
// // Handle successful login
// if (responseLogin.getStatusCode() == HttpStatus.OK) {
// log.info("Login successful to odessa. Parsing response.");
// String loginResponseJson = Utils.convertObjectToJson(responseLogin.getBody());
// AppointmentLoginResponse parsedResponse = parseLoginResponse(loginResponseJson);
//
// // Validate and save token
// if (parsedResponse.getTokenId() != null) {
// hub.setAppointmentAuthTokenId(parsedResponse.getTokenId());
// hub.setAreaCode(parsedResponse.getAreaCode());
// hubRepository.save(hub);
//
// log.info("Saved new authToken and areaCode for Hub.");
// success = true;
// return hub;
// } else {
// throw new RuntimeException("Login response is missing a valid tokenId for login to odessa system, please try again.");
// }
// }
// // Handle non-OK response
// throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_IN_GENERATING_NDG_TRY_AGAIN));
// } catch (FeignException.Forbidden forbiddenException) {
// log.error("Failed to login to odessa due to some error occurred.");
//
// // Extract raw response body
// String responseBody = forbiddenException.contentUTF8(); // Extract raw JSON response
//
// // Parse JSON to check for "PasswordExpired"
// try {
// ObjectMapper objectMapper = new ObjectMapper();
// JsonNode rootNode = objectMapper.readTree(responseBody);
// JsonNode errorsNode = rootNode.path("errors");
//
// if (errorsNode.isArray()) {
// for (JsonNode error : errorsNode) {
// // Check the main errorCode
// if (GepafinConstant.PASSWORD_EXPIRED.equals(error.path("errorCode").asText())) {
// throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA));
// }
//
// // Check inside "subErrors"
// JsonNode subErrorsNode = error.path("subErrors");
// if (subErrorsNode.isArray()) {
// for (JsonNode subError : subErrorsNode) {
// if (GepafinConstant.PASSWORD_EXPIRED.equals(subError.path("errorCode").asText())) {
// throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA));
// }
// }
// }
// }
// }
// } catch (IOException e) {
// log.error("Error parsing JSON response: {}", e.getMessage());
// }
// } catch (Exception e) {
// log.error("Failed to authenticate user on Odessa : {}", e.getMessage(), e);
// throw new RuntimeException("Authentication failed on Odessa. try again", e);
// }
// }
// return null;
// }
private void loginToOdessa(HubEntity hub, ApplicationEntity application) {
performOdessaLogin(hub, application);
}
private HubEntity authenticateAndSaveToken(HubEntity hub, ApplicationEntity application) {
return performOdessaLogin(hub, application);
}
private HubEntity performOdessaLogin(HubEntity hub, ApplicationEntity application) {
int maxRetries = 3;
int attempt = 0;
while (attempt < maxRetries) {
attempt++;
try {
//code to generate token with payload having "iat" epoch timestamp and secret key with no expiry and send in below method call
String authJwtToken = Utils.generateAuthTokenForLoginToOdessa();
log.info("Got the auth for login to odessa {}", authJwtToken);
hub.setAuthToken(authJwtToken);
@@ -189,7 +371,6 @@ public class AppointmentDao {
String loginResponseJson = Utils.convertObjectToJson(responseLogin.getBody());
AppointmentLoginResponse parsedResponse = parseLoginResponse(loginResponseJson);
// Validate and save token
if (parsedResponse.getTokenId() != null) {
hub.setAppointmentAuthTokenId(parsedResponse.getTokenId());
hub.setAreaCode(parsedResponse.getAreaCode());
@@ -201,14 +382,20 @@ public class AppointmentDao {
}
}
throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_IN_GENERATING_NDG_TRY_AGAIN));
} catch (FeignException.Forbidden forbiddenException) {
log.error("Failed to login to odessa due to forbidden error.");
CheckPasswordExpiredOrErrorInResponse(application, forbiddenException);
} catch (Exception e) {
log.error("Failed to authenticate user on Odessa (Attempt {}): {}", attempt, e.getMessage(), e);
}
catch (FeignException.Forbidden forbiddenException) {
logForbiddenError();
}
throw new RuntimeException("Max retries exceeded. Failed to login to Odessa.");
}
private void CheckPasswordExpiredOrErrorInResponse(ApplicationEntity application, FeignException.Forbidden forbiddenException) {
// Extract raw response body
String responseBody = forbiddenException.contentUTF8(); // Extract raw JSON response
String responseBody = forbiddenException.contentUTF8();
// Parse JSON to check for "PasswordExpired"
try {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode rootNode = objectMapper.readTree(responseBody);
@@ -216,20 +403,22 @@ public class AppointmentDao {
if (errorsNode.isArray()) {
for (JsonNode error : errorsNode) {
// Check the main errorCode
if (GepafinConstant.PASSWORD_EXPIRED.equals(error.path("errorCode").asText())) {
if (application != null) {
application.setNdgStatus(GepafinConstant.NDG_FAILED);
applicationRepository.save(application);
}
throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA));
}
// Check inside "subErrors"
JsonNode subErrorsNode = error.path("subErrors");
if (subErrorsNode.isArray()) {
for (JsonNode subError : subErrorsNode) {
if (GepafinConstant.PASSWORD_EXPIRED.equals(subError.path("errorCode").asText())) {
if (application != null) {
application.setNdgStatus(GepafinConstant.NDG_FAILED);
applicationRepository.save(application);
}
throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA));
}
}
@@ -239,15 +428,6 @@ public class AppointmentDao {
} catch (IOException e) {
log.error("Error parsing JSON response: {}", e.getMessage());
}
// Regenerate the token and retry
loginToOdessa(hub, application);
}
catch (Exception e) {
log.error("Failed to authenticate user on Odessa : {}", e.getMessage(), e);
throw new RuntimeException("Authentication failed on Odessa. try again", e);
}
return null;
}
private void startAsyncNdgProcessing(Long applicationId) {
@@ -296,7 +476,7 @@ public class AppointmentDao {
try {
// Authenticate and fetch token if required
if (hub.getAppointmentAuthTokenId() == null || hub.getAreaCode() == null) {
authenticateAndSaveToken(hub);
authenticateAndSaveToken(hub, application);
}
String authorizationToken = getBearerToken(hub);
@@ -342,10 +522,11 @@ public class AppointmentDao {
application.setStatus(ApplicationStatusTypeEnum.NDG.getValue());
applicationRepository.save(application);
companyRepository.save(company);
ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationService.validateApplicationEvaluation(application.getApplicationEvaluationId());
ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationService.validateApplicationEvaluation(
application.getApplicationEvaluationId());
Map<String, String> placeHolders = notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.NDG_GENERATION);
notificationDao.sendNotificationToInstructor(placeHolders, applicationEvaluationEntity, NotificationTypeEnum.NDG_GENERATION);
notificationDao.sendNotificationToSuperUser(application,placeHolders,NotificationTypeEnum.NDG_GENERATION);
notificationDao.sendNotificationToSuperUser(application, placeHolders, NotificationTypeEnum.NDG_GENERATION);
log.info("NDG saved successfully for applicationId: {}", application.getId());
break;
}
@@ -395,7 +576,7 @@ public class AppointmentDao {
ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationService.validateApplicationEvaluation(application.getApplicationEvaluationId());
Map<String, String> placeHolders = notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.NDG_GENERATION);
notificationDao.sendNotificationToInstructor(placeHolders, applicationEvaluationEntity, NotificationTypeEnum.NDG_GENERATION);
notificationDao.sendNotificationToSuperUser(application,placeHolders,NotificationTypeEnum.NDG_GENERATION);
notificationDao.sendNotificationToSuperUser(application, placeHolders, NotificationTypeEnum.NDG_GENERATION);
log.info("NDG saved for applicationId: {}, {}", application.getId(), application.getNdg());
}
@@ -413,7 +594,7 @@ public class AppointmentDao {
} catch (FeignException.Forbidden forbiddenException) {
log.error("403 Forbidden received while getting visuraList for Ndg code. Regenerating token...");
// Regenerate the token and retry
String newAuthorizationToken = regenerateTokenAndSave(hub);
String newAuthorizationToken = regenerateTokenAndSave(hub, application);
return getVisuraList(idVisura, newAuthorizationToken, application, hub);
} catch (Exception e) {
log.error("Failed to fetch Ndg code: {}", e.getMessage(), e);
@@ -421,82 +602,6 @@ public class AppointmentDao {
}
}
private HubEntity authenticateAndSaveToken(HubEntity hub) {
try {
//code to generate token with payload having "iat" epoch timestamp and secret key with no expiry and send in below method call
String authJwtToken = Utils.generateAuthTokenForLoginToOdessa();
log.info("Got the auth for login to odessa {}", authJwtToken);
hub.setAuthToken(authJwtToken);
hubRepository.save(hub);
// Prepare the request body (adjust if necessary for login API)
Map<String, Object> body = Collections.emptyMap();
// Perform login API call
ResponseEntity<Object> responseLogin = appointmentApiService.loginWithOdessa(authJwtToken, source, context, user, password, body);
// Handle successful login
if (responseLogin.getStatusCode() == HttpStatus.OK) {
log.info("Login successful to odessa. Parsing response.");
String loginResponseJson = Utils.convertObjectToJson(responseLogin.getBody());
AppointmentLoginResponse parsedResponse = parseLoginResponse(loginResponseJson);
// Validate and save token
if (parsedResponse.getTokenId() != null) {
hub.setAppointmentAuthTokenId(parsedResponse.getTokenId());
hub.setAreaCode(parsedResponse.getAreaCode());
hubRepository.save(hub);
log.info("Saved new authToken and areaCode for Hub.");
return hub;
} else {
throw new RuntimeException("Login response is missing a valid tokenId for login to odessa system, please try again.");
}
}
// Handle non-OK response
throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_IN_GENERATING_NDG_TRY_AGAIN));
} catch (FeignException.Forbidden forbiddenException) {
logForbiddenError();
// Extract raw response body
String responseBody = forbiddenException.contentUTF8(); // Extract raw JSON response
// Parse JSON to check for "PasswordExpired"
try {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode rootNode = objectMapper.readTree(responseBody);
JsonNode errorsNode = rootNode.path("errors");
if (errorsNode.isArray()) {
for (JsonNode error : errorsNode) {
// Check the main errorCode
if (GepafinConstant.PASSWORD_EXPIRED.equals(error.path("errorCode").asText())) {
throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA));
}
// Check inside "subErrors"
JsonNode subErrorsNode = error.path("subErrors");
if (subErrorsNode.isArray()) {
for (JsonNode subError : subErrorsNode) {
if (GepafinConstant.PASSWORD_EXPIRED.equals(subError.path("errorCode").asText())) {
throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA));
}
}
}
}
}
} catch (IOException e) {
log.error("Error parsing JSON response: {}", e.getMessage());
}
// Regenerate the token and retry
regenerateTokenAndSave(hub);
} catch (Exception e) {
log.error("Failed to authenticate user on Odessa : {}", e.getMessage(), e);
throw new RuntimeException("Authentication failed on Odessa. try again", e);
}
return null;
}
private AppointmentLoginResponse retrieveNdgByVatNumber(String vatNumber, String authorizationToken, HubEntity hub, ApplicationEntity application) {
try {
@@ -510,7 +615,7 @@ public class AppointmentDao {
} catch (FeignException.Forbidden forbiddenException) {
logForbiddenError();
// Regenerate the token and retry
String newAuthorizationToken = regenerateTokenAndSave(hub);
String newAuthorizationToken = regenerateTokenAndSave(hub, application);
return retrieveNdgByVatNumber(vatNumber, newAuthorizationToken, hub, application);
} catch (Exception e) {
log.error("Failed to retrieve NDG by VAT number: {}", e.getMessage(), e);
@@ -518,8 +623,9 @@ public class AppointmentDao {
}
}
private String regenerateTokenAndSave(HubEntity hub) {
hub = authenticateAndSaveToken(hub);
private String regenerateTokenAndSave(HubEntity hub, ApplicationEntity application) {
hub = authenticateAndSaveToken(hub, application);
return "Bearer " + hub.getAppointmentAuthTokenId();
}
@@ -533,7 +639,7 @@ public class AppointmentDao {
} catch (FeignException.Forbidden forbiddenException) {
logForbiddenError();
// Regenerate the token and retry
String newAuthorizationToken = regenerateTokenAndSave(hub);
String newAuthorizationToken = regenerateTokenAndSave(hub, null);
return createVisura(company, newAuthorizationToken, hub);
} catch (Exception e) {
log.error("Failed to create Visura for Ndg : {}", e.getMessage());
@@ -703,9 +809,8 @@ public class AppointmentDao {
throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NDG_NOT_FOUND_FOR_APPLICATION));
}
hub = authenticateAndSaveToken(hub);
// Generate authorization token and fetch template data
String authorizationToken = getBearerToken(hub);
String authorizationToken = regenerateTokenAndSave(hub, application);
Long appointmentTemplateId = application.getCall().getAppointmentTemplateId();
if (appointmentTemplateId == null) {
throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.APPOINTMENT_CANNOT_BE_CREATED));
@@ -749,7 +854,7 @@ public class AppointmentDao {
} catch (FeignException.Forbidden forbiddenException) {
log.error("403 Forbidden received while retrieving template. Regenerating token...");
regenerateTokenAndSave(hub);
regenerateTokenAndSave(hub, application);
return createAppointment(applicationId, createAppointmentRequest);
}
}
@@ -758,13 +863,32 @@ public class AppointmentDao {
if (appointmentResponse.getBody() != null) {
log.info("Appointment API Response : {}", appointmentResponse.getBody());
try {
Map<String, Object> responseBody = (Map<String, Object>) appointmentResponse.getBody();
// 1. Try to get appointment ID from data.id
if (responseBody.containsKey(GepafinConstant.DATA_STRING)) {
Map<String, Object> data = (Map<String, Object>) responseBody.get(GepafinConstant.DATA_STRING);
if (data != null && data.containsKey(GepafinConstant.ID_STRING)) {
return data.get(GepafinConstant.ID_STRING).toString();
}
}
// 2. If ID not present, check errors[0].cause.errorDescription
if (responseBody.containsKey(GepafinConstant.ERROR_STRING)) {
List<Map<String, Object>> errors = (List<Map<String, Object>>) responseBody.get(GepafinConstant.ERROR_STRING);
if (errors != null && !errors.isEmpty()) {
Map<String, Object> firstError = errors.get(0);
if (firstError.containsKey(GepafinConstant.CAUSE_STRING)) {
Map<String, Object> cause = (Map<String, Object>) firstError.get(GepafinConstant.CAUSE_STRING);
if (cause != null && cause.containsKey(GepafinConstant.ERROR_DESCRIPTION_STRING)) {
String errorDescription = cause.get(GepafinConstant.ERROR_DESCRIPTION_STRING).toString();
log.warn("Appointment creation failed: {}", errorDescription);
}
}
}
}
} catch (Exception e) {
log.error("Error while extracting appointment ID or parsing error message", e);
}
}
return null;
}
@@ -867,13 +991,40 @@ public class AppointmentDao {
// Check if the document is already being processed
DocumentEntity systemDoc = documentDao.validateDocument(documentId);
ApplicationEntity application = null;
if (systemDoc != null) {
DocumentSourceTypeEnum sourceType = DocumentSourceTypeEnum.valueOf(systemDoc.getSource());
switch (sourceType) {
case APPLICATION:
application = applicationDao.validateApplication(systemDoc.getSourceId());
break;
case AMENDMENT:
ApplicationAmendmentRequestEntity applicationAmendmentEntity = applicationAmendmentRequestDao.validateApplicationAmendmentRequest(systemDoc.getSourceId());
application = applicationDao.validateApplication(applicationAmendmentEntity.getApplicationId());
break;
case EVALUATION:
ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationDao.validateApplicationEvaluation(systemDoc.getSourceId());
application = applicationDao.validateApplication(applicationEvaluationEntity.getApplicationId());
break;
case CALL:
break;
default:
log.warn("Unhandled document source type: {}", sourceType);
break;
}
}
Claims claims = tokenProvider.getClaimsFromToken(tokenProvider.extractTokenFromRequest(request));
Long hubId = Utils.extractHubIdFromPayload(claims.getSubject());
// Authenticate the hub before proceeding
HubEntity hub = hubRepository.findByHubId(hubId);
authenticateAndSaveToken(hub);
if (systemDoc.getDocumentAttachmentId() != null) {
authenticateAndSaveToken(hub, application);
if (systemDoc != null && systemDoc.getDocumentAttachmentId() != null) {
// If the documentAttachmentId is already set, return the response
log.info("Document already uploaded with documentAttachmentId: {}", systemDoc.getDocumentAttachmentId());
DocumentUploadResponse response = new DocumentUploadResponse();
@@ -893,11 +1044,12 @@ public class AppointmentDao {
});
threadForDocumentMap.put(documentId, executor);
ApplicationEntity finalApplication = application;
executor.submit(() -> {
threadLocalHubId.set(hubId);
try {
log.info("Starting async document upload for documentId: {}", documentId);
uploadDocumentToExternalSystemSync(documentId, docToExternalSystemRequest);
uploadDocumentToExternalSystemSync(documentId, docToExternalSystemRequest, finalApplication);
} catch (Exception e) {
log.error("Error in async document upload for documentId: {}", documentId, e);
} finally {
@@ -913,7 +1065,7 @@ public class AppointmentDao {
return null;
}
private void uploadDocumentToExternalSystemSync(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest) {
private void uploadDocumentToExternalSystemSync(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest, ApplicationEntity application) {
// Synchronous upload logic
DocumentEntity systemDoc = documentDao.validateDocument(documentId);
@@ -954,8 +1106,8 @@ public class AppointmentDao {
log.info("Document uploaded successfully to external system: {}", parsedResponse);
} catch (FeignException.Forbidden forbiddenException) {
log.error("403 Forbidden received while uploading document. Regenerating token...");
regenerateTokenAndSave(hub);
uploadDocumentToExternalSystemSync(documentId, docToExternalSystemRequest);
regenerateTokenAndSave(hub, application);
uploadDocumentToExternalSystemSync(documentId, docToExternalSystemRequest, application);
} catch (Exception e) {
log.error("Exception during document upload: {}", e.getMessage(), e);
throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.EXTERNAL_DOCUMENT_UPLOAD_FAILURE_MSG));

View File

@@ -5,9 +5,7 @@ import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.log4j.Log4j2;
import net.gepafin.tendermanagement.config.Translator;
import net.gepafin.tendermanagement.constants.GepafinConstant;
import net.gepafin.tendermanagement.entities.CallEntity;
import net.gepafin.tendermanagement.entities.EmailLogEntity;
import net.gepafin.tendermanagement.entities.UserActionEntity;
import net.gepafin.tendermanagement.entities.*;
import net.gepafin.tendermanagement.enums.EmailScenarioTypeEnum;
import net.gepafin.tendermanagement.enums.EmailServiceTypeEnum;
import net.gepafin.tendermanagement.enums.RecipientTypeEnum;
@@ -15,8 +13,7 @@ import net.gepafin.tendermanagement.enums.StatusTypeEnum;
import net.gepafin.tendermanagement.model.request.EmailLogRequest;
import net.gepafin.tendermanagement.model.response.EmailResendResponseBean;
import net.gepafin.tendermanagement.model.response.EmailSendResponse;
import net.gepafin.tendermanagement.repositories.EmailLogRepository;
import net.gepafin.tendermanagement.repositories.UserActionsRepository;
import net.gepafin.tendermanagement.repositories.*;
import net.gepafin.tendermanagement.service.CallService;
import net.gepafin.tendermanagement.util.Utils;
import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException;
@@ -47,6 +44,15 @@ public class EmailDao {
@Autowired
private UserActionsRepository userActionsRepository;
@Autowired
private ApplicationAmendmentRequestRepository applicationAmendmentRequestRepository;
@Autowired
private UserRepository userRepository;
@Autowired
private ApplicationEvaluationRepository applicationEvaluationRepository;
public EmailResendResponseBean resendEmail(HttpServletRequest request , Long userActionId){
UserActionEntity userActionEntity = userActionsRepository.findUserActionByIdAndIsDeletedFalse(userActionId);
if(userActionEntity == null){
@@ -82,9 +88,90 @@ public class EmailDao {
}
EmailSendResponse emailSendResponse = buildEmailSendResponseFromRequest(request);
emailResendResponseBean.setEmailSendResponse(emailSendResponse);
if (Boolean.TRUE.equals(emailSendResponse.getIsEmailSend())){
updateEmailSendStatusIfSuccessful(emailSendResponse);
}
return emailResendResponseBean;
}
private void updateEmailSendStatusIfSuccessful(EmailSendResponse emailSendResponse){
Long actionId = emailSendResponse.getUserActionId();
EmailLogEntity emailLog = emailLogRepository.findTopByUserActionIdAndEmailServiceTypeAndSendStatusOrderByIdDesc(
actionId,
EmailServiceTypeEnum.PEC_SERVICE.getValue(),
StatusTypeEnum.SUCCESS.getValue()
);
if (emailLog != null) {
switch (emailSendResponse.getEmailScenario()) {
case APPLICATION_AMENDMENT_REQUESTED:
case APPLICATION_AMENDMENT_REMINDER:
updateApplicationAmendmentStatus(emailLog, emailSendResponse.getEmailScenario().getValue());
break;
case USER_CREATION:
case PASSWORD_RESET_REQUEST:
updateUserEmailStatus(emailLog, emailSendResponse.getEmailScenario().getValue());
break;
case APPLICATION_ADMISSIBLE:
case APPLICATION_REJECTED:
updateApplicationEvaluationStatus(emailLog, emailSendResponse.getEmailScenario().getValue());
break;
default:
log.warn("Unhandled email scenario: {}", emailSendResponse.getEmailScenario());
}
}
}
private void updateApplicationAmendmentStatus(EmailLogEntity log, String scenario) {
if (log.getAmendmentId() != null) {
applicationAmendmentRequestRepository.findById(log.getAmendmentId()).ifPresent(amendment -> {
if (updateEmailSendResponse(amendment.getEmailSendResponse(), scenario)) {
applicationAmendmentRequestRepository.save(amendment);
}
});
}
}
private void updateApplicationEvaluationStatus(EmailLogEntity log, String scenario) {
if (log.getApplicationId() != null) {
ApplicationEvaluationEntity evaluation = applicationEvaluationRepository.findByApplicationId(log.getApplicationId());
if (evaluation != null && updateEmailSendResponse(evaluation.getEmailSendResponse(), scenario)) {
applicationEvaluationRepository.save(evaluation);
}
}
}
private void updateUserEmailStatus(EmailLogEntity log, String scenario) {
if (log.getUserId() != null) {
userRepository.findById(log.getUserId()).ifPresent(user -> {
if (updateEmailSendResponse(user.getEmailSendResponse(), scenario)) {
userRepository.save(user);
}
});
}
}
private boolean updateEmailSendResponse(List<EmailSendResponse> responses, String scenario) {
if (responses == null || responses.isEmpty()) return false;
for (EmailSendResponse response : responses) {
if (scenario.equals(response.getEmailScenario().getValue())) {
response.setIsEmailSend(true);
return true;
}
}
return false;
}
public EmailSendResponse buildEmailSendResponseFromRequest(HttpServletRequest request) {
Long userActionId = (Long) request.getAttribute(GepafinConstant.USER_ACTION_ID);
List<EmailLogEntity> emailLogs = emailLogRepository.findByUserActionIdAndEmailServiceType(userActionId,EmailServiceTypeEnum.PEC_SERVICE.getValue());

View File

@@ -46,6 +46,7 @@ public enum UserActionContextEnum {
GET_SIGNED_DOCUMENT("GET_SIGNED_DOCUMENT"),
GET_NEXT_PREVIOUS_FORM("GET_NEXT_PREVIOUS_FORM"),
DOWNLOAD_APPLICATION_DOC_ZIP("DOWNLOAD_APPLICATION_DOC_ZIP"),
READMIT_APPLICATION("READMIT_APPLICATION"),
/** FAQ action context **/
CREATE_FAQ("CREATE_FAQ"),

View File

@@ -76,5 +76,14 @@ public interface ApplicationEvaluationRepository extends JpaRepository<Applicati
@Param("assignedApplicationId") Long assignedApplicationId
);
Optional<ApplicationEvaluationEntity> findByAssignedApplicationsEntity_IdAndStatusAndIsDeletedFalse(
Long assignedApplicationId,
String status
);
@Query("SELECT ae FROM ApplicationEvaluationEntity ae WHERE ae.applicationId = :applicationId AND ae.isDeleted = false")
ApplicationEvaluationEntity findByApplicationId(@Param("applicationId") Long applicationId);
}

View File

@@ -178,4 +178,7 @@ public interface ApplicationRepository extends JpaRepository<ApplicationEntity,
void resetNdgStatusForInProgress(@Param("status") String status);
boolean existsByCallId(Long callId);
ApplicationEntity findByIdAndStatusAndIsDeletedFalse( Long id, String status);
}

View File

@@ -110,4 +110,8 @@ public interface AssignedApplicationsRepository extends JpaRepository<AssignedAp
@Param("sevenDaysAgo") LocalDateTime sevenDaysAgo);
Optional<AssignedApplicationsEntity> findByApplicationIdAndStatusAndIsDeletedFalse( Long applicationId, String status);
}

View File

@@ -17,5 +17,12 @@ public interface EmailLogRepository extends JpaRepository<EmailLogEntity,Long> {
List<EmailLogEntity> findByUserActionIdAndEmailServiceType(
Long userActionId, String emailServiceType);
EmailLogEntity findTopByUserActionIdAndEmailServiceTypeAndSendStatusOrderByIdDesc(
Long userActionId,
String emailServiceType,
String sendStatus
);
}

View File

@@ -50,4 +50,6 @@ public interface ApplicationService {
public byte[] exportCsv(HttpServletRequest request, Long callId);
public ApplicationResponse readmitApplication(HttpServletRequest request, Long applicationId);
}

View File

@@ -170,5 +170,10 @@ public class ApplicationServiceImpl implements ApplicationService {
return csvBytes;
}
@Override
@Transactional(rollbackFor = Exception.class)
public ApplicationResponse readmitApplication(HttpServletRequest request, Long applicationId) {
UserEntity userEntity = validator.validateUser(request);
return applicationDao.readmitApplication(request, applicationId);
}
}

View File

@@ -238,6 +238,20 @@ public interface ApplicationApi {
public ResponseEntity<byte[]> exportCsv(
HttpServletRequest request, @Parameter(description = "The call id", required = true) @PathVariable(value = "callId", required = true) Long callId);
@Operation(summary = "Api to re-admit an application",
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) })) })
@PutMapping(value = "/{applicationId}/readmit", produces = { "application/json" })
@PreAuthorize("hasRole('ROLE_SUPER_ADMIN')|| hasRole('ROLE_INSTRUCTOR_MANAGER')|| hasRole('ROLE_PRE_INSTRUCTOR')")
ResponseEntity<Response<ApplicationResponse>> readmitApplication(HttpServletRequest request,
@Parameter(description = "The application id", required = true) @PathVariable("applicationId") Long applicationId);
}

View File

@@ -242,5 +242,16 @@ public class ApplicationApiController implements ApplicationApi {
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(csvBytes);
}
@Override
public ResponseEntity<Response<ApplicationResponse>> readmitApplication(HttpServletRequest request, Long applicationId) {
/** This code is responsible for creating user action logs for the "re-admit application" operation. **/
loggingUtil.logUserAction(
UserActionRequest.builder().request(request).actionType(UserActionLogsEnum.UPDATE).actionContext(UserActionContextEnum.READMIT_APPLICATION).build());
ApplicationResponse applicationResponse = applicationService.readmitApplication(request, applicationId);
return ResponseEntity.status(HttpStatus.OK)
.body(new Response<>(applicationResponse, Status.SUCCESS, Translator.toLocale(GepafinConstant.READMIT_APPLICATION_SUCCESS)));
}
}

View File

@@ -404,5 +404,7 @@ invalid.user=Invalid user.
reminder.email.sent.failed.msg = Failed to send reminder email
resend.email.sent.success.msg = Email resend successfully
resend.email.sent.failed.msg = Failed to resend the email.
application.readmit.success=Application has been readmitted successfully.
no.email.log.msg = No failed emails found for given userActionId.
user.action.id.not.found = User Action id not found.

View File

@@ -53,7 +53,7 @@ call.update.successfully=Chiamata aggiornata con successo.
call.fetch.success=Dettagli della chiamata recuperati con successo.
call.not.found=Chiamata non trovata.
score.not.null=Il punteggio non pu? essere nullo o zero.
field.not.null={0} non può essere vuoto.
field.not.null={0} non pu<EFBFBD> essere vuoto.
field.not.empty=la {0} non pu? essere vuota.
update_call_status_success_msg=Lo stato della chiamata ? stato aggiornato con successo.
status.same.error=Lo stato ? gi? impostato.
@@ -389,11 +389,13 @@ error.invalid.limit=Il limite dovrebbe essere compreso tra 1 e 3000.
insufficient.score.msg = Punteggio non sufficiente per passaggio alla valutazione tecnica ed economico finanziaria
validation.table.message=I dati per il campo {0} non sono presenti.
password.expired.for.login.to.odessa = La password di accesso a Odessa è scaduta
password.expired.for.login.to.odessa = La password di accesso a Odessa <EFBFBD> scaduta
invalid.user=Utente non valido.
reminder.email.sent.failed.msg = Impossibile inviare l'e-mail di promemoria
resend.email.sent.success.msg = Email reinviata con successo
resend.email.sent.failed.msg = Impossibile inviare nuovamente l'e-mail.
application.readmit.success=L'applicazione è stata riammessa con successo.
no.email.log.msg = Nessuna email trovata per userActionId specificato.
user.action.id.not.found = ID azione utente non trovato.